import { Router } from 'express';
import { prisma } from '../db.js';
import { requireAuth, publicUser } from '../middleware.js';
import {
  maxVaultSlots,
  settleVaultYield,
  summarizeVaultState,
} from '../vault.js';
import { isConnected } from '../presence.js';

const router = Router();

router.post('/stake', 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 stake' });
    }

    const mode = isConnected(req.session.userId) ? 'online' : 'offline';

    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 maxSlots = maxVaultSlots(user);
      const used = await tx.inventoryItem.count({
        where: { userId: user.id, staked: true },
      });
      const free = maxSlots - used;
      if (ids.length > free) {
        const err = new Error(`Vault full (${used}/${maxSlots} slots)`);
        err.status = 400;
        throw err;
      }

      const invItems = await tx.inventoryItem.findMany({
        where: {
          id: { in: ids },
          userId: user.id,
          locked: false,
          staked: false,
        },
      });
      if (invItems.length !== ids.length) {
        const err = new Error('Invalid, locked, or already staked items');
        err.status = 400;
        throw err;
      }

      const now = new Date();
      await tx.inventoryItem.updateMany({
        where: { id: { in: ids }, userId: user.id },
        data: {
          staked: true,
          stakedAt: now,
          lastYieldAt: now,
        },
      });

      // Shared timer: start only when vault was empty
      let updatedUser = user;
      if (used === 0 || !user.vaultYieldAt) {
        updatedUser = await tx.user.update({
          where: { id: user.id },
          data: { vaultYieldAt: now },
        });
      }

      const staked = await tx.inventoryItem.findMany({
        where: { userId: user.id, staked: true },
        include: { item: true },
      });

      return {
        user: updatedUser,
        stakedIds: ids,
        vault: summarizeVaultState(updatedUser, staked, mode),
      };
    });

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

router.post('/unstake', 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 unstake' });
    }

    const mode = isConnected(req.session.userId) ? 'online' : 'offline';
    const settled = await settleVaultYield(req.session.userId, mode);

    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: user.id,
          staked: true,
        },
      });
      if (invItems.length !== ids.length) {
        const err = new Error('Invalid or not-staked items');
        err.status = 400;
        throw err;
      }

      await tx.inventoryItem.updateMany({
        where: { id: { in: ids }, userId: user.id },
        data: {
          staked: false,
          stakedAt: null,
          lastYieldAt: null,
        },
      });

      const remaining = await tx.inventoryItem.findMany({
        where: { userId: user.id, staked: true },
        include: { item: true },
      });

      let refreshed = user;
      if (remaining.length === 0 && user.vaultYieldAt) {
        refreshed = await tx.user.update({
          where: { id: user.id },
          data: { vaultYieldAt: null },
        });
      } else {
        refreshed = await tx.user.findUnique({ where: { id: user.id } });
      }

      return {
        user: refreshed,
        unstakedIds: ids,
        vault: summarizeVaultState(refreshed, remaining, mode),
      };
    });

    res.json({
      user: publicUser(result.user),
      unstakedIds: result.unstakedIds,
      yieldCredited: settled.credited,
      vault: result.vault,
    });
  } catch (err) {
    const status = err.status || 500;
    if (status >= 500) console.error(err);
    res.status(status).json({ error: err.message || 'Failed to unstake items' });
  }
});

export default router;
