import { Router } from 'express';
import { prisma } from '../db.js';
import { requireAuth, publicUser } from '../middleware.js';
import {
  PRESTIGE_COST_CENTS,
  PRESTIGE_SKILLS,
  computePrReward,
  estimatePrReward,
  getUnlockedInventoryValueCents,
  isPrestigePageVisible,
  isValidSkill,
  serializePrestige,
  serializeSkillTree,
  skillLevelFromUser,
  skillLevelsRemaining,
  skillUpgradeCostBulk,
  toPrBigInt,
  prJson,
} from '../prestige.js';
import { checkAchievements } from '../achievements.js';
import { centsJson, toCentsBigInt, addCents, spendUserBalance } from '../cents.js';

const router = Router();
const COST = PRESTIGE_COST_CENTS;

router.get('/', requireAuth, async (req, res) => {
  try {
    const user = await prisma.user.findUnique({ where: { id: req.session.userId } });
    if (!user) return res.status(404).json({ error: 'User not found' });

    const [lockedCount, inventoryValue, logs] = await Promise.all([
      prisma.inventoryItem.count({
        where: { userId: user.id, locked: true },
      }),
      getUnlockedInventoryValueCents(prisma, user.id),
      prisma.prestigeLog.findMany({
        where: { userId: user.id },
        orderBy: { createdAt: 'desc' },
        take: 20,
      }),
    ]);

    const balance = toCentsBigInt(user.balance);
    const totalCents = balance + inventoryValue;
    const pageVisible = isPrestigePageVisible(user, totalCents);
    const canAfford = balance >= COST;
    const canPrestige = pageVisible && canAfford && lockedCount === 0;
    const previewPr = canAfford
      ? estimatePrReward(user.balance, user.prestigePrBonus ?? 0)
      : 0;

    res.json({
      prestige: serializePrestige(user),
      skills: serializeSkillTree(user),
      pageVisible,
      totalCents: totalCents.toString(),
      canAfford,
      lockedItems: lockedCount,
      canPrestige,
      previewPr,
      history: logs.map((l) => ({
        id: l.id,
        choice: l.choice,
        prGranted: prJson(l.prGranted ?? 0),
        createdAt: l.createdAt,
      })),
    });
  } catch (err) {
    console.error(err);
    res.status(500).json({ error: 'Failed to load prestige' });
  }
});

router.post('/', requireAuth, async (req, res) => {
  try {
    const userId = req.session.userId;

    // Pay out pending vault yield before wipe (own transaction)
    const { settleVaultYield } = await import('../vault.js');
    await settleVaultYield(userId, 'offline');

    const result = await prisma.$transaction(async (tx) => {
      const user = await tx.user.findUnique({ where: { id: userId } });
      if (!user) {
        const err = new Error('User not found');
        err.status = 401;
        throw err;
      }
      if (user.role === 'admin') {
        const err = new Error('Admins cannot prestige');
        err.status = 403;
        throw err;
      }

      const lockedCount = await tx.inventoryItem.count({
        where: { userId: user.id, locked: true },
      });
      if (lockedCount > 0) {
        const err = new Error(
          'Withdraw items from battles before prestiging (locked inventory)'
        );
        err.status = 400;
        throw err;
      }

      // Lock funds atomically (balance >= COST). Remainder wiped with the reset below.
      await spendUserBalance(tx, user.id, COST, {
        message: 'Insufficient balance for prestige',
      });

      const fresh = await tx.user.findUnique({ where: { id: user.id } });
      const previousBalance = toCentsBigInt(user.balance);
      const prGranted = computePrReward(previousBalance, user.prestigePrBonus ?? 0);
      const prestigeCount = (user.prestigeCount ?? 0) + 1;

      await tx.inventoryItem.deleteMany({ where: { userId: user.id } });
      // Keep lifetime opens-by-case stats; reset key progress only.
      await tx.caseKeyProgress.updateMany({
        where: { userId: user.id },
        data: { keys: 1, opensSinceLastKey: 0 },
      });

      await tx.transaction.create({
        data: {
          userId: user.id,
          type: 'prestige',
          amount: centsJson(-COST),
          meta: JSON.stringify({
            prestigeCount,
            prGranted,
            previousBalance: centsJson(previousBalance),
          }),
        },
      });

      await tx.prestigeLog.create({
        data: {
          userId: user.id,
          choice: 'reset',
          prGranted: prJson(prGranted),
        },
      });

      // Wipe leftover credits after the COST spend (prestige resets economy).
      const updated = await tx.user.update({
        where: { id: user.id },
        data: {
          prestigeCount,
          balance: '0',
          prBalance: addCents(fresh.prBalance, prGranted),
          vaultYieldAt: null,
        },
      });

      return { user: updated, prGranted: prJson(prGranted) };
    });

    checkAchievements(result.user.id).catch((err) => console.error('achievements', err));

    res.json({
      user: publicUser(result.user),
      prestige: serializePrestige(result.user),
      skills: serializeSkillTree(result.user),
      prGranted: result.prGranted,
    });
  } catch (err) {
    console.error(err);
    const status = err.status || 500;
    res.status(status).json({ error: err.message || 'Failed to prestige' });
  }
});

router.post('/upgrade', requireAuth, async (req, res) => {
  try {
    const skillId = String(req.body?.skillId || '');
    if (!isValidSkill(skillId)) {
      return res.status(400).json({ error: 'Unknown skill' });
    }

    let count = Number(req.body?.count ?? 1);
    if (!Number.isFinite(count)) count = 1;
    count = Math.max(1, Math.min(10, Math.floor(count)));

    const skill = PRESTIGE_SKILLS[skillId];

    const result = await prisma.$transaction(async (tx) => {
      const user = await tx.user.findUnique({ where: { id: req.session.userId } });
      if (!user) {
        const err = new Error('User not found');
        err.status = 401;
        throw err;
      }
      if (user.role === 'admin') {
        const err = new Error('Admins cannot buy prestige skills');
        err.status = 403;
        throw err;
      }

      const level = skillLevelFromUser(user, skillId);
      const room = skillLevelsRemaining(user, skillId);
      if (room <= 0) {
        const err = new Error('Skill is already at max level');
        err.status = 400;
        throw err;
      }
      count = Math.min(count, room);

      const cost = skillUpgradeCostBulk(skillId, level, count);
      if (toPrBigInt(user.prBalance) < BigInt(cost)) {
        const err = new Error('Not enough Pr');
        err.status = 400;
        throw err;
      }

      const updated = await tx.user.update({
        where: { id: user.id },
        data: {
          prBalance: addCents(user.prBalance, -cost),
          [skill.field]: { increment: skill.delta * count },
        },
      });

      await tx.transaction.create({
        data: {
          userId: user.id,
          type: 'prestige_upgrade',
          amount: '0',
          meta: JSON.stringify({
            skillId,
            cost,
            count,
            delta: skill.delta * count,
            field: skill.field,
          }),
        },
      });

      return updated;
    });

    checkAchievements(result.id).catch((err) => console.error('achievements', err));

    res.json({
      user: publicUser(result),
      prestige: serializePrestige(result),
      skills: serializeSkillTree(result),
    });
  } catch (err) {
    console.error(err);
    const status = err.status || 500;
    res.status(status).json({ error: err.message || 'Failed to upgrade skill' });
  }
});

export default router;
