import { Router, type IRouter } from "express";
import {
  db,
  walletTransactionsTable,
  walletDepositsTable,
  withdrawalsTable,
} from "@workspace/db";
import { eq, desc } from "drizzle-orm";
import { requireAuth, requireRole } from "../middlewares/auth";
import { CreateDepositBody, CreateWithdrawalBody } from "@workspace/api-zod";
import { getOrCreateWallet, adjustBalance, InsufficientFundsError } from "../lib/wallet";

const router: IRouter = Router();

router.get("/wallet", requireAuth, async (req, res): Promise<void> => {
  const userId = req.user!.id;
  const wallet = await getOrCreateWallet(userId);
  const transactions = await db
    .select()
    .from(walletTransactionsTable)
    .where(eq(walletTransactionsTable.userId, userId))
    .orderBy(desc(walletTransactionsTable.createdAt))
    .limit(20);
  res.json({ balance: wallet.balance, transactions });
});

router.get("/wallet/transactions", requireAuth, async (req, res): Promise<void> => {
  const userId = req.user!.id;
  const transactions = await db
    .select()
    .from(walletTransactionsTable)
    .where(eq(walletTransactionsTable.userId, userId))
    .orderBy(desc(walletTransactionsTable.createdAt));
  res.json(transactions);
});

// Deposits — user submits proof of payment; admin approves to credit the wallet.
router.get("/wallet/deposits", requireAuth, async (req, res): Promise<void> => {
  const rows = await db
    .select()
    .from(walletDepositsTable)
    .where(eq(walletDepositsTable.userId, req.user!.id))
    .orderBy(desc(walletDepositsTable.createdAt));
  res.json(rows);
});

router.post("/wallet/deposits", requireAuth, async (req, res): Promise<void> => {
  const parsed = CreateDepositBody.safeParse(req.body);
  if (!parsed.success) {
    res.status(400).json({ error: parsed.error.message });
    return;
  }
  const { amount, method, transactionId } = parsed.data;
  if (!Number.isInteger(amount) || amount <= 0) {
    res.status(400).json({ error: "Amount must be a positive whole number" });
    return;
  }
  const trimmed = transactionId.trim();
  if (!trimmed) {
    res.status(400).json({ error: "Transaction ID is required" });
    return;
  }
  const [created] = await db
    .insert(walletDepositsTable)
    .values({ userId: req.user!.id, amount, method, transactionId: trimmed, status: "pending" })
    .returning();
  res.status(201).json(created);
});

// Withdrawals — model-only payout requests. Funds are held (debited) on request
// and refunded if the admin rejects.
router.get("/wallet/withdrawals", requireAuth, async (req, res): Promise<void> => {
  const rows = await db
    .select()
    .from(withdrawalsTable)
    .where(eq(withdrawalsTable.userId, req.user!.id))
    .orderBy(desc(withdrawalsTable.createdAt));
  res.json(rows);
});

router.post("/wallet/withdrawals", requireRole("model"), async (req, res): Promise<void> => {
  const parsed = CreateWithdrawalBody.safeParse(req.body);
  if (!parsed.success) {
    res.status(400).json({ error: parsed.error.message });
    return;
  }
  const { amount, method, accountNumber } = parsed.data;
  if (!Number.isInteger(amount) || amount <= 0) {
    res.status(400).json({ error: "Amount must be a positive whole number" });
    return;
  }
  if (method !== "bkash" && method !== "nagad" && method !== "rocket") {
    res.status(400).json({ error: "Invalid payment method" });
    return;
  }
  if (!accountNumber.trim()) {
    res.status(400).json({ error: "Account number is required" });
    return;
  }

  try {
    const created = await db.transaction(async (tx) => {
      await adjustBalance({
        userId: req.user!.id,
        amount: -amount,
        type: "withdrawal",
        description: `Withdrawal request (${method})`,
        tx,
      });
      const [row] = await tx
        .insert(withdrawalsTable)
        .values({ userId: req.user!.id, amount, method, accountNumber: accountNumber.trim(), status: "pending" })
        .returning();
      return row;
    });
    res.status(201).json(created);
  } catch (err) {
    if (err instanceof InsufficientFundsError) {
      res.status(400).json({ error: "Insufficient balance for this withdrawal." });
      return;
    }
    throw err;
  }
});

export default router;
