import { Router } from 'express';
import bcrypt from 'bcrypt';
import { prisma } from '../db.js';
import { requireAuth, publicUser } from '../middleware.js';
import { getCompletedCatalogBadges } from '../catalog.js';
import { serializePrestige } from '../prestige.js';
import { serializePresence } from '../presence.js';
import {
  checkAchievements,
  getUnlockedAchievements,
} from '../achievements.js';
import { toCentsBigInt, centsJson } from '../cents.js';
import { createUploader, processUploadedImage, safeExt } from '../upload.js';

const router = Router();

const upload = createUploader('avatar', {
  maxBytes: 2 * 1024 * 1024,
  filename: (req, file, cb) => {
    cb(null, `avatar-${req.session.userId}-${Date.now()}${safeExt(file.originalname, file.mimetype)}`);
  },
});

async function loadCaseOpenStats(userId) {
  const rows = await prisma.caseKeyProgress.findMany({
    where: { userId, totalOpens: { gt: 0 } },
    include: {
      case: { select: { id: true, name: true, imageUrl: true } },
    },
    orderBy: { totalOpens: 'desc' },
  });

  return rows.map((row) => ({
    caseId: row.caseId,
    caseName: row.case.name,
    imageUrl: row.case.imageUrl || '',
    count: row.totalOpens,
    keys: row.keys,
  }));
}

function serializeInv(inv) {
  return {
    id: inv.id,
    obtainedAt: inv.obtainedAt,
    locked: inv.locked,
    floatValue: inv.floatValue,
    wear: inv.wear,
    paintSeed: inv.paintSeed,
    valueCents: inv.valueCents,
    favorite: Boolean(inv.favorite),
    staked: Boolean(inv.staked),
    item: {
      id: inv.item.id,
      name: inv.item.name,
      imageUrl: inv.item.imageUrl,
      rarity: inv.item.rarity,
      marketValue: inv.item.marketValue,
      valueCents: inv.valueCents,
      floatValue: inv.floatValue,
      wear: inv.wear,
      paintSeed: inv.paintSeed,
    },
  };
}

router.get('/player/:username', async (req, res) => {
  try {
    const username = String(req.params.username || '').trim();
    if (!username) {
      return res.status(400).json({ error: 'Username required' });
    }

    const user = await prisma.user.findFirst({
      where: { username, role: 'user' },
      include: {
        inventory: {
          where: { locked: false },
          include: { item: true },
          orderBy: { obtainedAt: 'desc' },
        },
      },
    });
    if (!user) return res.status(404).json({ error: 'Player not found' });

    const inventoryValue = user.inventory.reduce(
      (sum, inv) => sum + toCentsBigInt(inv.valueCents),
      0n
    );

    await checkAchievements(user.id);

    const [jackpotWins, casesOpened, caseOpens, catalog, achievements] =
      await Promise.all([
        prisma.transaction.count({
          where: { userId: user.id, type: 'jackpot_win' },
        }),
        prisma.transaction.count({
          where: { userId: user.id, type: 'open_case' },
        }),
        loadCaseOpenStats(user.id),
        getCompletedCatalogBadges(user.id),
        getUnlockedAchievements(user.id),
      ]);

    res.json({
      user: publicUser(user),
      profile: {
        createdAt: user.createdAt,
        inventoryValue: centsJson(inventoryValue),
        inventoryCount: user.inventory.length,
        jackpotWins,
        casesOpened,
        caseOpens,
        catalogCompleted: catalog.completed,
        catalogLuckPercent: catalog.luckPercent,
        prestige: serializePrestige(user),
        presence: serializePresence(user),
        achievements,
      },
      inventory: user.inventory.map(serializeInv),
    });
  } catch (err) {
    console.error(err);
    res.status(500).json({ error: 'Failed to load player profile' });
  }
});

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

    const inventoryValue = user.inventory.reduce(
      (sum, inv) => sum + (inv.locked ? 0n : toCentsBigInt(inv.valueCents)),
      0n
    );

    await checkAchievements(user.id);

    const [jackpotWins, casesOpened, caseOpens, catalog, achievements] =
      await Promise.all([
        prisma.transaction.count({
          where: { userId: user.id, type: 'jackpot_win' },
        }),
        prisma.transaction.count({
          where: { userId: user.id, type: 'open_case' },
        }),
        loadCaseOpenStats(user.id),
        getCompletedCatalogBadges(user.id),
        getUnlockedAchievements(user.id),
      ]);

    res.json({
      user: publicUser(user),
      profile: {
        createdAt: user.createdAt,
        inventoryValue: centsJson(inventoryValue),
        inventoryCount: user.inventory.filter((i) => !i.locked).length,
        jackpotWins,
        casesOpened,
        caseOpens,
        catalogCompleted: catalog.completed,
        catalogLuckPercent: catalog.luckPercent,
        prestige: serializePrestige(user),
        presence: serializePresence(user),
        achievements,
      },
    });
  } catch (err) {
    console.error(err);
    res.status(500).json({ error: 'Failed to load profile' });
  }
});

router.patch('/', requireAuth, async (req, res) => {
  try {
    const bio = String(req.body.bio ?? '').slice(0, 280);
    const user = await prisma.user.update({
      where: { id: req.session.userId },
      data: { bio },
    });
    res.json({ user: publicUser(user) });
  } catch (err) {
    console.error(err);
    res.status(500).json({ error: 'Failed to update profile' });
  }
});

router.post('/password', requireAuth, async (req, res) => {
  try {
    const { currentPassword, newPassword } = req.body || {};
    if (!currentPassword || !newPassword) {
      return res.status(400).json({ error: 'Current and new password required' });
    }
    if (String(newPassword).length < 6) {
      return res.status(400).json({ error: 'New password must be at least 6 characters' });
    }

    const user = await prisma.user.findUnique({ where: { id: req.session.userId } });
    if (!user) return res.status(404).json({ error: 'User not found' });

    const ok = await bcrypt.compare(String(currentPassword), user.passwordHash);
    if (!ok) return res.status(400).json({ error: 'Current password is incorrect' });

    const passwordHash = await bcrypt.hash(String(newPassword), 10);
    await prisma.user.update({
      where: { id: user.id },
      data: { passwordHash },
    });

    res.json({ ok: true });
  } catch (err) {
    console.error(err);
    res.status(500).json({ error: 'Failed to change password' });
  }
});

router.post('/avatar', requireAuth, (req, res) => {
  upload.single('avatar')(req, res, async (err) => {
    if (err) {
      return res.status(400).json({ error: err.message || 'Upload failed' });
    }
    if (!req.file) {
      return res.status(400).json({ error: 'No file uploaded' });
    }

    try {
      await processUploadedImage(req.file);
      const avatarUrl = `/uploads/${req.file.filename}`;
      const user = await prisma.user.update({
        where: { id: req.session.userId },
        data: { avatarUrl },
      });
      res.json({ user: publicUser(user) });
    } catch (e) {
      console.error(e);
      res.status(500).json({ error: 'Failed to save avatar' });
    }
  });
});

export default router;
