import { Router } from 'express';
import { prisma } from '../db.js';
import { requireAuth, publicUser } from '../middleware.js';
import { applyAutoSell } from '../autoSell.js';
import { applyGainMultiplier } from '../prestige.js';
import { toCentsBigInt, adjustUserBalance, centsJson, creditsToCents } from '../cents.js';
import {
  summarizeVaultState,
} from '../vault.js';
import { isConnected } from '../presence.js';

const router = Router();

function serializeInv(inv) {
  return {
    id: inv.id,
    obtainedAt: inv.obtainedAt,
    locked: inv.locked,
    staked: Boolean(inv.staked),
    stakedAt: inv.stakedAt || null,
    lastYieldAt: inv.lastYieldAt || null,
    floatValue: inv.floatValue,
    wear: inv.wear,
    paintSeed: inv.paintSeed,
    valueCents: inv.valueCents,
    favorite: Boolean(inv.favorite),
    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('/', requireAuth, async (req, res) => {
  try {
    const userId = req.session.userId;
    const [user, items] = await Promise.all([
      prisma.user.findUnique({ where: { id: userId } }),
      prisma.inventoryItem.findMany({
        where: { userId, locked: false },
        include: { item: true },
        orderBy: { obtainedAt: 'desc' },
      }),
    ]);
    if (!user) return res.status(404).json({ error: 'User not found' });

    const staked = items.filter((i) => i.staked);
    const mode = isConnected(userId) ? 'online' : 'offline';

    res.json({
      inventory: items.map(serializeInv),
      vault: summarizeVaultState(user, staked, mode),
    });
  } catch (err) {
    console.error(err);
    res.status(500).json({ error: 'Failed to load inventory' });
  }
});

router.patch('/auto-sell', requireAuth, async (req, res) => {
  try {
    const data = {};
    if (req.body.enabled != null) {
      data.autoSellEnabled = Boolean(req.body.enabled);
    }
    if (req.body.thresholdCredits != null) {
      const cents = creditsToCents(req.body.thresholdCredits);
      if (cents == null) {
        return res.status(400).json({ error: 'Invalid threshold' });
      }
      data.autoSellThresholdCents = centsJson(cents);
    }

    if (!Object.keys(data).length) {
      return res.status(400).json({ error: 'Nothing to update' });
    }

    const user = await prisma.user.update({
      where: { id: req.session.userId },
      data,
    });

    res.json({ user: publicUser(user) });
  } catch (err) {
    console.error(err);
    res.status(500).json({ error: 'Failed to update auto-sell settings' });
  }
});

router.post('/auto-sell/apply', requireAuth, async (req, res) => {
  try {
    const ids = [...new Set((req.body.inventoryItemIds || []).map(Number).filter(Boolean))];
    if (!ids.length) {
      return res.json({ user: publicUser(await prisma.user.findUnique({ where: { id: req.session.userId } })), soldIds: [], total: 0 });
    }

    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;
        }

        const invItems = await tx.inventoryItem.findMany({
          where: {
            id: { in: ids },
            userId: req.session.userId,
            locked: false,
            staked: false,
          },
        });

        const { sold, balanceDelta } = await applyAutoSell(tx, user, invItems);
        if (balanceDelta === 0n) {
          return { user, soldIds: [], total: 0n, sold: [] };
        }

        const updatedUser = await adjustUserBalance(
          tx,
          req.session.userId,
          user.balance,
          balanceDelta
        );

        const gainBonus = user.prestigeGainBonus ?? 0;
        return {
          user: updatedUser,
          soldIds: sold.map((inv) => inv.id),
          total: balanceDelta,
          sold: sold.map((inv) => ({
            id: inv.id,
            valueCents: centsJson(inv.valueCents),
            payoutCents: centsJson(applyGainMultiplier(inv.valueCents, gainBonus)),
          })),
        };
      },
      { timeout: 60_000, maxWait: 15_000 }
    );

    res.json({
      user: publicUser(result.user),
      soldIds: result.soldIds,
      total: centsJson(result.total),
      sold: result.sold || [],
    });
  } catch (err) {
    const status = err.status || 500;
    if (status >= 500) console.error(err);
    res.status(status).json({ error: err.message || 'Failed to apply auto-sell' });
  }
});

router.post('/sell', requireAuth, async (req, res) => {
  try {
    const ids = [...new Set((req.body.inventoryItemIds || []).map(Number).filter(Boolean))];
    if (!ids.length) {
      return res.status(400).json({ error: 'Select at least one item to sell' });
    }

    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;
        }

        const invItems = await tx.inventoryItem.findMany({
          where: {
            id: { in: ids },
            userId: req.session.userId,
            locked: false,
            staked: false,
          },
          select: {
            id: true,
            itemId: true,
            valueCents: true,
            favorite: true,
          },
        });

        if (invItems.length !== ids.length) {
          const err = new Error('Invalid, locked, or staked inventory items');
          err.status = 400;
          throw err;
        }

        if (invItems.some((inv) => inv.favorite)) {
          const err = new Error('Favorite items cannot be sold');
          err.status = 400;
          throw err;
        }

        const gainBonus = user.prestigeGainBonus ?? 0;
        let total = 0n;
        for (const inv of invItems) {
          total += applyGainMultiplier(inv.valueCents, gainBonus);
        }

        await tx.inventoryItem.deleteMany({
          where: {
            id: { in: ids },
            userId: req.session.userId,
            locked: false,
            staked: false,
          },
        });

        await tx.transaction.create({
          data: {
            userId: req.session.userId,
            type: 'sell_item',
            amount: centsJson(total),
            meta: JSON.stringify({
              count: invItems.length,
              inventoryItemIds: ids,
              prestigeGainBonus: gainBonus,
              payout: centsJson(total),
            }),
          },
        });

        const updatedUser = await adjustUserBalance(
          tx,
          req.session.userId,
          user.balance,
          total
        );

        return { user: updatedUser, total, soldCount: invItems.length };
      },
      { timeout: 60_000, maxWait: 15_000 }
    );

    res.json({
      user: publicUser(result.user),
      total: centsJson(result.total),
      soldCount: result.soldCount,
    });
  } catch (err) {
    const status = err.status || 500;
    if (status >= 500) console.error(err);
    res.status(status).json({ error: err.message || 'Failed to sell items' });
  }
});

router.patch('/favorite', requireAuth, async (req, res) => {
  try {
    const ids = [...new Set((req.body.inventoryItemIds || []).map(Number).filter(Boolean))];
    if (!ids.length) {
      return res.status(400).json({ error: 'Select at least one item' });
    }
    if (req.body.favorite == null) {
      return res.status(400).json({ error: 'favorite flag required' });
    }
    const favorite = Boolean(req.body.favorite);

    const result = await prisma.$transaction(async (tx) => {
      const invItems = await tx.inventoryItem.findMany({
        where: {
          id: { in: ids },
          userId: req.session.userId,
          locked: false,
        },
        include: { item: true },
      });

      if (invItems.length !== ids.length) {
        const err = new Error('Invalid or locked inventory items');
        err.status = 400;
        throw err;
      }

      await tx.inventoryItem.updateMany({
        where: { id: { in: ids }, userId: req.session.userId },
        data: { favorite },
      });

      const updated = await tx.inventoryItem.findMany({
        where: { id: { in: ids }, userId: req.session.userId },
        include: { item: true },
      });

      return updated;
    });

    res.json({ inventory: result.map(serializeInv) });
  } catch (err) {
    const status = err.status || 500;
    if (status >= 500) console.error(err);
    res.status(status).json({ error: err.message || 'Failed to update favorites' });
  }
});

export default router;
