import { Router } from 'express';
import { prisma } from '../db.js';
import { requireAuth, publicUser } from '../middleware.js';
import { checkAchievements } from '../achievements.js';
import { adCooldownMs } from '../prestige.js';
import { centsJson, creditUserBalance } from '../cents.js';
import {
  getCreditPack,
  paymentsProvider,
  scaleCreditPack,
  scaleCreditPacks,
} from '../shopCatalog.js';
import { listEnabledShopOsuPacks, isOsuTopupsSectionEnabled } from '../shopOsu.js';
import {
  confirmMockOsuPayment,
  createOsuCheckout,
  getOrderForUser,
} from '../payments.js';

const router = Router();

const AD_REWARD_MIN = 10000; // 100.00 cr
const AD_REWARD_MAX = 20000; // 200.00 cr

function adPayload(user, overrides = {}) {
  const cooldownMs = adCooldownMs(user);
  const lastAdAt = user.lastAdAt ? new Date(user.lastAdAt).getTime() : 0;
  const nextAdAt = lastAdAt + cooldownMs;
  const adReady = Date.now() >= nextAdAt;
  return {
    cooldownMs,
    rewardMin: AD_REWARD_MIN,
    rewardMax: AD_REWARD_MAX,
    ready: adReady,
    nextAdAt: adReady ? null : new Date(nextAdAt).toISOString(),
    lastAdAt: user.lastAdAt,
    ...overrides,
  };
}

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 packages = scaleCreditPacks(user.balance);
    const sectionEnabled = await isOsuTopupsSectionEnabled();
    const osuTopups = sectionEnabled ? await listEnabledShopOsuPacks() : [];
    const provider = paymentsProvider();

    res.json({
      packages,
      osuTopups,
      osuTopupsEnabled: sectionEnabled && osuTopups.length > 0,
      payments: {
        provider,
        currency: 'eur',
        mock: provider === 'mock',
        publishableKey:
          provider === 'stripe' ? process.env.STRIPE_PUBLISHABLE_KEY || '' : '',
      },
      user: publicUser(user),
      ad: adPayload(user),
    });
  } catch (err) {
    console.error(err);
    res.status(500).json({ error: 'Failed to load shop' });
  }
});

/** Spend osu → credits (amount scaled to live balance at purchase time). */
router.post('/buy', requireAuth, async (req, res) => {
  try {
    const packageId = String(req.body.packageId || '');
    const packDef = getCreditPack(packageId);
    if (!packDef) {
      return res.status(400).json({ error: 'Unknown package' });
    }

    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 = 404;
        throw err;
      }
      const pack = scaleCreditPack(packDef, user.balance);
      const osuCost = pack.osuCost;
      if (user.osuBalance < osuCost) {
        const err = new Error('Not enough osu');
        err.status = 400;
        throw err;
      }

      await tx.user.update({
        where: { id: user.id },
        data: { osuBalance: { decrement: osuCost } },
      });
      const updated = await creditUserBalance(tx, user.id, pack.credits);

      await tx.transaction.create({
        data: {
          userId: user.id,
          type: 'shop_purchase',
          amount: centsJson(pack.credits),
          meta: JSON.stringify({
            packageId: pack.id,
            osuCost,
            credits: pack.credits,
            balancePct: pack.balancePct,
            balanceBefore: String(user.balance),
            scaled: pack.scaled,
          }),
        },
      });

      return { user: updated, osuCost, pack };
    });

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

    res.json({
      user: publicUser(result.user),
      package: result.pack,
    });
  } catch (err) {
    const status = err.status || 500;
    if (status >= 500) console.error(err);
    res.status(status).json({ error: err.message || 'Purchase failed' });
  }
});

/** Start real-money → osu checkout (mock or Stripe Checkout). */
router.post('/osu/checkout', requireAuth, async (req, res) => {
  try {
    const packId = String(req.body.packId || '');
    const checkout = await createOsuCheckout(req.session.userId, packId);
    res.json(checkout);
  } catch (err) {
    const status = err.status || 500;
    if (status >= 500) console.error(err);
    res.status(status).json({ error: err.message || 'Checkout failed' });
  }
});

/** Complete mock payment and credit osu. */
router.post('/osu/confirm', requireAuth, async (req, res) => {
  try {
    const orderId = String(req.body.orderId || '');
    const result = await confirmMockOsuPayment(req.session.userId, orderId);
    checkAchievements(result.user.id).catch((err) => console.error('achievements', err));
    res.json({
      user: publicUser(result.user),
      order: result.order,
      alreadyPaid: result.alreadyPaid,
      osuGranted: result.order.osuAmount,
    });
  } catch (err) {
    const status = err.status || 500;
    if (status >= 500) console.error(err);
    res.status(status).json({ error: err.message || 'Payment confirmation failed' });
  }
});

/** Poll order status after Stripe redirect (webhook may lag a second). */
router.get('/osu/orders/:id', requireAuth, async (req, res) => {
  try {
    const order = await getOrderForUser(req.session.userId, req.params.id);
    if (!order) return res.status(404).json({ error: 'Order not found' });
    res.json({ order });
  } catch (err) {
    console.error(err);
    res.status(500).json({ error: 'Failed to load order' });
  }
});

router.post('/ad', requireAuth, async (req, res) => {
  try {
    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 = 404;
        throw err;
      }

      const cooldownMs = adCooldownMs(user);
      const lastAdAt = user.lastAdAt ? new Date(user.lastAdAt).getTime() : 0;
      const elapsed = Date.now() - lastAdAt;
      if (elapsed < cooldownMs) {
        const waitSec = Math.ceil((cooldownMs - elapsed) / 1000);
        const err = new Error(`Ad available in ${waitSec}s`);
        err.status = 429;
        err.nextAdAt = new Date(lastAdAt + cooldownMs).toISOString();
        throw err;
      }

      const reward =
        AD_REWARD_MIN + Math.floor(Math.random() * (AD_REWARD_MAX - AD_REWARD_MIN + 1));
      const now = new Date();

      await tx.user.update({
        where: { id: user.id },
        data: { lastAdAt: now },
      });
      const updated = await creditUserBalance(tx, user.id, reward);

      await tx.transaction.create({
        data: {
          userId: user.id,
          type: 'ad_reward',
          amount: centsJson(reward),
          meta: JSON.stringify({ reward }),
        },
      });

      return { user: updated, reward, cooldownMs };
    });

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

    res.json({
      user: publicUser(result.user),
      reward: result.reward,
      ad: adPayload(result.user, {
        ready: false,
        nextAdAt: new Date(Date.now() + result.cooldownMs).toISOString(),
      }),
    });
  } catch (err) {
    const status = err.status || 500;
    if (status >= 500) console.error(err);
    res.status(status).json({
      error: err.message || 'Ad reward failed',
      nextAdAt: err.nextAdAt || null,
    });
  }
});

export default router;
