'use strict';
const db = require('../db/db');
const { awardCoins } = require('./gamificationController');

const BG_SHOP = [
  { id:'space',  name:'Космос',  price:50,  desc:'Звёздный простор' },
  { id:'forest', name:'Лес',     price:50,  desc:'Таинственный лес' },
  { id:'aqua',   name:'Океан',   price:75,  desc:'Морская глубина'  },
  { id:'sunset', name:'Закат',   price:75,  desc:'Пурпурный закат'  },
];

function _parseOwned(raw) {
  try { return JSON.parse(raw || '[]'); } catch { return []; }
}

function _petLevel(xp) {
  if (xp >= 80000) return 8;
  if (xp >= 40000) return 7;
  if (xp >= 20000) return 6;
  if (xp >= 10000) return 5;
  if (xp >= 5000)  return 4;
  if (xp >= 2000)  return 3;
  if (xp >= 500)   return 2;
  return 1;
}

function _mood(streak, daysSinceLogin) {
  if (daysSinceLogin >= 14) return 'sleeping';
  if (daysSinceLogin >= 7)  return 'hungry';
  if (daysSinceLogin >= 3)  return 'sad';
  if (streak >= 7)  return 'ecstatic';
  if (streak >= 3)  return 'happy';
  if (streak >= 1)  return 'neutral';
  return 'sad';
}

function _accessories(user) {
  const acc = [];
  if (user.streak_best >= 7)   acc.push('hat');
  if (user.level >= 5)         acc.push('glasses');
  if (user.xp >= 5000)         acc.push('crown');
  if (user.level >= 10)        acc.push('star');
  return acc;
}

function _quests(userId, streakCurrent) {
  const today = new Date().toISOString().slice(0, 10);
  const xpToday = db.prepare(
    "SELECT COALESCE(SUM(amount),0) AS total FROM xp_log WHERE user_id=? AND date(created_at)=date(?)"
  ).get(userId, today)?.total || 0;
  const testsToday = db.prepare(
    "SELECT COUNT(*) AS cnt FROM test_sessions WHERE user_id=? AND status='completed' AND date(finished_at)=date(?)"
  ).get(userId, today)?.cnt || 0;
  return [
    { id:'xp30',    icon:'⭐', label:'Набери 30 XP сегодня', done: xpToday >= 30,      progress: Math.min(xpToday, 30),   goal: 30 },
    { id:'test1',   icon:'📝', label:'Пройди 1 тест',        done: testsToday >= 1,   progress: Math.min(testsToday, 1), goal: 1  },
    { id:'streak2', icon:'🔥', label:'Серия 2+ дней',        done: streakCurrent >= 2 },
  ];
}

function _moodForecast(daysSince) {
  if (daysSince >= 7) return null; // already hungry/sleeping
  if (daysSince >= 3) return { mood: 'hungry', inDays: Math.max(0, 7 - daysSince) };
  const toSad = Math.max(0, 3 - daysSince);
  if (toSad <= 2) return { mood: 'sad', inDays: toSad };
  return null;
}

/* ── GET /api/pet ─────────────────────────────────────────────────────── */
function getPet(req, res) {
  const user = db.prepare(
    `SELECT xp, level, streak_current, streak_best, streak_date, coins,
            pet_name, last_login, pet_color, pet_last_petted, pet_petting_streak,
            pet_bg, pet_bg_owned, pet_last_fed
     FROM users WHERE id = ?`
  ).get(req.user.id);

  if (!user) return res.status(404).json({ error: 'User not found' });

  const now = new Date();

  const lastXpRow   = db.prepare("SELECT MAX(created_at) AS t FROM xp_log WHERE user_id = ?").get(req.user.id);
  const lastSessRow = db.prepare("SELECT MAX(finished_at) AS t FROM test_sessions WHERE user_id = ? AND status = 'completed'").get(req.user.id);
  const candidates  = [
    user.last_login  ? new Date(user.last_login)  : null,
    lastXpRow?.t     ? new Date(lastXpRow.t)       : null,
    lastSessRow?.t   ? new Date(lastSessRow.t)     : null,
  ].filter(Boolean);
  const lastActive = candidates.length ? new Date(Math.max(...candidates.map(d => d.getTime()))) : now;
  const daysSince  = Math.max(0, Math.floor((now - lastActive) / 86400000));

  const since7   = new Date(now - 7 * 86400000).toISOString();
  const recentXP = db.prepare(
    'SELECT COALESCE(SUM(amount),0) AS total FROM xp_log WHERE user_id = ? AND created_at >= ?'
  ).get(req.user.id, since7)?.total || 0;

  const petLvl      = _petLevel(user.xp);
  const mood        = _mood(user.streak_current, daysSince);
  const accessories = _accessories(user);

  const thresholds = [0, 500, 2000, 5000, 10000, 20000, 40000, 80000, Infinity];
  const xpForNext  = thresholds[petLvl] ?? Infinity;
  const xpForCurr  = thresholds[petLvl - 1] ?? 0;

  const d0 = new Date(now); d0.setDate(d0.getDate() - 6);
  const weekStart = d0.toISOString().slice(0, 10);
  const weekRows = db.prepare(
    "SELECT date(created_at) AS d, COALESCE(SUM(amount),0) AS xp FROM xp_log WHERE user_id = ? AND date(created_at) >= date(?) GROUP BY date(created_at)"
  ).all(req.user.id, weekStart);
  const weekMap = new Map(weekRows.map(r => [r.d, r.xp]));
  const weeklyXP = [];
  for (let i = 6; i >= 0; i--) {
    const d = new Date(now); d.setDate(d.getDate() - i);
    const dateStr = d.toISOString().slice(0, 10);
    weeklyXP.push({ day: d.toLocaleDateString('ru', { weekday: 'short' }), xp: weekMap.get(dateStr) || 0 });
  }

  const SOURCE_LABELS = {
    hangman_win: 'Виселица', crossword_win: 'Кроссворд',
    test_answer: 'Тест', challenge: 'Задание',
    lab_activity: 'Лаборатория', assignment: 'Домашнее задание',
    pet_petting: 'Поглаживание', pet_feeding: 'Кормёжка питомца',
    mood_ecstatic: 'Бонус настроения', correct_answers: 'Тест',
    test_complete: 'Тест', 'test_90+': 'Тест 90%+', test_perfect: 'Тест 100%',
  };
  const rawActivity = db.prepare(
    "SELECT amount, reason, created_at FROM xp_log WHERE user_id = ? ORDER BY created_at DESC LIMIT 6"
  ).all(req.user.id);
  const recentActivity = rawActivity.map(r => ({
    xp: r.amount, label: SOURCE_LABELS[r.reason] || r.reason, at: r.created_at,
  }));

  const pettingCooldown = user.pet_last_petted
    ? Math.max(0, 60 - Math.floor((now - new Date(user.pet_last_petted)) / 1000))
    : 0;
  const feedCooldown = user.pet_last_fed
    ? Math.max(0, 1800 - Math.floor((now - new Date(user.pet_last_fed)) / 1000))
    : 0;

  res.json({
    petName:        user.pet_name || 'Квантик',
    petLevel:       petLvl,
    petColor:       user.pet_color || 'purple',
    mood,
    daysSinceLogin: daysSince,
    accessories,
    xp:             user.xp,
    level:          user.level,
    streakCurrent:  user.streak_current,
    streakBest:     user.streak_best,
    coins:          user.coins,
    pettingStreak:  user.pet_petting_streak || 0,
    recentXP,
    weeklyXP,
    recentActivity,
    xpForCurrLevel: xpForCurr,
    xpForNextLevel: xpForNext === Infinity ? null : xpForNext,
    quests:         _quests(req.user.id, user.streak_current),
    moodForecast:   _moodForecast(daysSince),
    pettingCooldown,
    feedCooldown,
    petBg:    user.pet_bg || 'default',
    petBgOwned: _parseOwned(user.pet_bg_owned),
  });
}

/* ── POST /api/pet/pet ────────────────────────────────────────────────── */
function petAction(req, res) {
  const user = db.prepare('SELECT pet_last_petted, pet_petting_streak FROM users WHERE id=?').get(req.user.id);
  if (!user) return res.status(404).json({ error: 'not found' });

  const now = new Date();
  if (user.pet_last_petted) {
    const diff = (now - new Date(user.pet_last_petted)) / 1000;
    if (diff < 60) return res.status(429).json({ error: 'cooldown', remaining: Math.ceil(60 - diff) });
  }

  const today     = now.toISOString().slice(0, 10);
  const yesterday = new Date(now - 86400000).toISOString().slice(0, 10);
  let streak      = user.pet_petting_streak || 0;
  if (user.pet_last_petted) {
    const lastDate = user.pet_last_petted.slice(0, 10);
    if (lastDate === today)          { /* same day, keep */ }
    else if (lastDate === yesterday) streak++;
    else                             streak = 1;
  } else {
    streak = 1;
  }

  try { awardCoins(req.user.id, 2, 'pet_petting'); } catch {}
  db.prepare('UPDATE users SET pet_last_petted=?, pet_petting_streak=? WHERE id=?')
    .run(now.toISOString(), streak, req.user.id);

  res.json({ ok: true, coins: 2, pettingStreak: streak });
}

/* ── PATCH /api/pet/name ──────────────────────────────────────────────── */
function renamePet(req, res) {
  let { name } = req.body;
  if (!name || typeof name !== 'string') return res.status(400).json({ error: 'name required' });
  name = name.trim().slice(0, 24);
  if (!name) return res.status(400).json({ error: 'name required' });
  db.prepare('UPDATE users SET pet_name = ? WHERE id = ?').run(name, req.user.id);
  res.json({ ok: true, name });
}

/* ── PATCH /api/pet/color ─────────────────────────────────────────────── */
function updateColor(req, res) {
  const { color } = req.body;
  const VALID = ['purple', 'cyan', 'gold', 'red', 'green', 'blue'];
  if (!VALID.includes(color)) return res.status(400).json({ error: 'invalid color' });
  db.prepare('UPDATE users SET pet_color = ? WHERE id = ?').run(color, req.user.id);
  res.json({ ok: true, color });
}

/* ── GET /api/pet/shop ────────────────────────────────────────────────── */
function getShop(req, res) {
  const user = db.prepare('SELECT coins, pet_bg, pet_bg_owned FROM users WHERE id=?').get(req.user.id);
  if (!user) return res.status(404).json({ error: 'not found' });
  const owned = _parseOwned(user.pet_bg_owned);
  res.json({
    coins:     user.coins || 0,
    currentBg: user.pet_bg || 'default',
    items:     BG_SHOP.map(item => ({ ...item, owned: owned.includes(item.id) })),
  });
}

/* ── POST /api/pet/shop/buy ───────────────────────────────────────────── */
function buyBg(req, res) {
  const { id } = req.body;
  const item = BG_SHOP.find(b => b.id === id);
  if (!item) return res.status(400).json({ error: 'invalid item' });
  const user = db.prepare('SELECT coins, pet_bg_owned FROM users WHERE id=?').get(req.user.id);
  if (!user) return res.status(404).json({ error: 'not found' });
  const owned = _parseOwned(user.pet_bg_owned);
  if (!owned.includes(id)) {
    // Atomic conditional UPDATE — race-safe even under concurrent purchase attempts
    const result = db.prepare(
      'UPDATE users SET coins = coins - ?, pet_bg_owned = ?, pet_bg = ? WHERE id = ? AND coins >= ?'
    ).run(item.price, JSON.stringify([...owned, id]), id, req.user.id, item.price);
    if (result.changes === 0) return res.status(400).json({ error: 'insufficient_coins' });
  } else {
    db.prepare('UPDATE users SET pet_bg=? WHERE id=?').run(id, req.user.id);
  }
  const updated = db.prepare('SELECT coins FROM users WHERE id=?').get(req.user.id);
  res.json({ ok: true, bg: id, coins: updated.coins });
}

/* ── PATCH /api/pet/bg ────────────────────────────────────────────────── */
function setBg(req, res) {
  const { id } = req.body;
  if (id !== 'default') {
    const owned = _parseOwned(db.prepare('SELECT pet_bg_owned FROM users WHERE id=?').get(req.user.id)?.pet_bg_owned);
    if (!owned.includes(id)) return res.status(403).json({ error: 'not owned' });
  }
  db.prepare('UPDATE users SET pet_bg=? WHERE id=?').run(id, req.user.id);
  res.json({ ok: true, bg: id });
}

/* ── POST /api/pet/star ───────────────────────────────────────────────── */
function starCatch(req, res) {
  const user = db.prepare('SELECT pet_last_star FROM users WHERE id=?').get(req.user.id);
  if (!user) return res.status(404).json({ error: 'not found' });
  const now = new Date();
  if (user.pet_last_star) {
    const diff = (now - new Date(user.pet_last_star)) / 1000;
    if (diff < 3600) return res.status(429).json({ error: 'cooldown', remaining: Math.ceil(3600 - diff) });
  }
  try { awardCoins(req.user.id, 5, 'star_catch'); } catch {}
  db.prepare('UPDATE users SET pet_last_star=? WHERE id=?').run(now.toISOString(), req.user.id);
  res.json({ ok: true, coins: 5 });
}

/* ── POST /api/pet/feed ───────────────────────────────────────────────── */
// Called when mini-game "feed pet" is correctly answered on frontend.
// 30-minute cooldown; awards 15 XP + marks pet as fed.
function feedPet(req, res) {
  const user = db.prepare('SELECT pet_last_fed FROM users WHERE id=?').get(req.user.id);
  if (!user) return res.status(404).json({ error: 'not found' });
  const now = new Date();
  const COOLDOWN_SEC = 1800; // 30 minutes
  if (user.pet_last_fed) {
    const diff = (now - new Date(user.pet_last_fed)) / 1000;
    if (diff < COOLDOWN_SEC) {
      return res.status(429).json({ error: 'cooldown', remaining: Math.ceil(COOLDOWN_SEC - diff) });
    }
  }
  try {
    const { awardXP } = require('./gamificationController');
    awardXP(req.user.id, 15, 'pet_feeding');
  } catch (e) { console.error('[feedPet] awardXP:', e.message); }
  db.prepare('UPDATE users SET pet_last_fed=? WHERE id=?').run(now.toISOString(), req.user.id);
  const updated = db.prepare('SELECT xp, coins FROM users WHERE id=?').get(req.user.id);
  res.json({ ok: true, xpAwarded: 15, xp: updated.xp, coins: updated.coins });
}

module.exports = { getPet, renamePet, petAction, updateColor, starCatch, getShop, buyBg, setBg, feedPet };
