import { Router, type IRouter } from "express";
import {
  db, profilesTable, locationsTable, bookingsTable, membershipsTable, usersTable,
  membershipPlansTable, paymentMethodsTable, membershipPaymentsTable,
  walletsTable, walletDepositsTable, withdrawalsTable, walletTransactionsTable,
  orderProofsTable,
} from "@workspace/db";
import { eq, desc, and, gte, sql, inArray } from "drizzle-orm";
import { requireAdmin } from "../middlewares/auth";
import {
  AdminCreateProfileBody, AdminUpdateProfileParams, AdminUpdateProfileBody, AdminDeleteProfileParams,
  AdminCreateLocationBody, AdminUpdateLocationParams, AdminUpdateLocationBody, AdminDeleteLocationParams,
  AdminUpdateMembershipParams, AdminUpdateMembershipBody,
  AdminCreateMembershipPlanBody, AdminUpdateMembershipPlanParams, AdminUpdateMembershipPlanBody, AdminDeleteMembershipPlanParams,
  AdminUpdatePaymentMethodParams, AdminUpdatePaymentMethodBody,
  AdminReviewMembershipPaymentParams, AdminReviewMembershipPaymentBody,
  AdminCreditWalletParams, AdminCreditWalletBody,
  AdminReviewDepositParams, AdminReviewDepositBody,
  AdminReviewWithdrawalParams, AdminReviewWithdrawalBody,
  AdminReviewProofParams, AdminReviewProofBody,
} from "@workspace/api-zod";
import { serializeProfile, insertProfileWithGeneratedId } from "../lib/profile";
import { adjustBalance, InsufficientFundsError, getPlatformUserId, splitCommission } from "../lib/wallet";

const router: IRouter = Router();

// Profiles
router.get("/admin/profiles", requireAdmin, async (_req, res): Promise<void> => {
  const profiles = await db.select().from(profilesTable).orderBy(profilesTable.createdAt);
  res.json(profiles.map(serializeProfile));
});

router.post("/admin/profiles", requireAdmin, async (req, res): Promise<void> => {
  const parsed = AdminCreateProfileBody.safeParse(req.body);
  if (!parsed.success) {
    res.status(400).json({ error: parsed.error.message });
    return;
  }
  const { name, city, photoUrl, bio, isFeatured, tags } = parsed.data;
  const profile = await insertProfileWithGeneratedId({
    name,
    city,
    photoUrl: photoUrl ?? undefined,
    bio: bio ?? undefined,
    isFeatured: isFeatured ?? false,
    tags: tags ?? [],
    approvalStatus: "approved",
  });
  res.status(201).json(serializeProfile(profile));
});

router.patch("/admin/profiles/:id", requireAdmin, async (req, res): Promise<void> => {
  const params = AdminUpdateProfileParams.safeParse({ id: req.params.id });
  if (!params.success) {
    res.status(400).json({ error: "Invalid ID" });
    return;
  }
  const parsed = AdminUpdateProfileBody.safeParse(req.body);
  if (!parsed.success) {
    res.status(400).json({ error: parsed.error.message });
    return;
  }
  const updateData: Record<string, unknown> = {};
  const d = parsed.data;
  if (d.name !== undefined) updateData.name = d.name;
  if (d.city !== undefined) updateData.city = d.city;
  if (d.photoUrl !== undefined) updateData.photoUrl = d.photoUrl;
  if (d.bio !== undefined) updateData.bio = d.bio;
  if (d.proStatus !== undefined) updateData.proStatus = d.proStatus;
  if (d.price1h !== undefined) updateData.price1h = d.price1h;
  if (d.price2h !== undefined) updateData.price2h = d.price2h;
  if (d.priceFullDay !== undefined) updateData.priceFullDay = d.priceFullDay;
  if (d.services !== undefined) updateData.services = d.services;
  if (d.contactPlatform !== undefined) updateData.contactPlatform = d.contactPlatform;
  if (d.whatsappNumber !== undefined) updateData.whatsappNumber = d.whatsappNumber;
  if (d.approvalStatus !== undefined) updateData.approvalStatus = d.approvalStatus;
  if (d.isFeatured !== undefined) updateData.isFeatured = d.isFeatured;
  if (d.isActive !== undefined) updateData.isActive = d.isActive;
  if (d.tags !== undefined) updateData.tags = d.tags;

  const [profile] = await db.update(profilesTable).set(updateData).where(eq(profilesTable.id, params.data.id)).returning();
  if (!profile) {
    res.status(404).json({ error: "Profile not found" });
    return;
  }
  res.json(serializeProfile(profile));
});

router.delete("/admin/profiles/:id", requireAdmin, async (req, res): Promise<void> => {
  const params = AdminDeleteProfileParams.safeParse({ id: req.params.id });
  if (!params.success) {
    res.status(400).json({ error: "Invalid ID" });
    return;
  }
  await db.delete(profilesTable).where(eq(profilesTable.id, params.data.id));
  res.sendStatus(204);
});

// Locations
router.get("/admin/locations", requireAdmin, async (_req, res): Promise<void> => {
  const locations = await db.select().from(locationsTable).orderBy(locationsTable.createdAt);
  res.json(locations.map(l => ({
    id: l.id, name: l.name, city: l.city, type: l.type, rating: l.rating,
    photoUrl: l.photoUrl, description: l.description, address: l.address,
    isFeatured: l.isFeatured, isActive: l.isActive, createdAt: l.createdAt,
  })));
});

router.post("/admin/locations", requireAdmin, async (req, res): Promise<void> => {
  const parsed = AdminCreateLocationBody.safeParse(req.body);
  if (!parsed.success) {
    res.status(400).json({ error: parsed.error.message });
    return;
  }
  const d = parsed.data;
  const [location] = await db.insert(locationsTable).values({
    name: d.name,
    city: d.city,
    type: d.type as "hotel" | "venue" | "safehouse",
    rating: d.rating ?? 4.0,
    photoUrl: d.photoUrl ?? undefined,
    description: d.description ?? undefined,
    address: d.address ?? undefined,
    isFeatured: d.isFeatured ?? false,
  }).returning();
  res.status(201).json({
    id: location.id, name: location.name, city: location.city, type: location.type, rating: location.rating,
    photoUrl: location.photoUrl, description: location.description, address: location.address,
    isFeatured: location.isFeatured, isActive: location.isActive, createdAt: location.createdAt,
  });
});

router.patch("/admin/locations/:id", requireAdmin, async (req, res): Promise<void> => {
  const params = AdminUpdateLocationParams.safeParse({ id: req.params.id });
  if (!params.success) {
    res.status(400).json({ error: "Invalid ID" });
    return;
  }
  const parsed = AdminUpdateLocationBody.safeParse(req.body);
  if (!parsed.success) {
    res.status(400).json({ error: parsed.error.message });
    return;
  }
  const updateData: Record<string, unknown> = {};
  const d = parsed.data;
  if (d.name !== undefined) updateData.name = d.name;
  if (d.city !== undefined) updateData.city = d.city;
  if (d.type !== undefined) updateData.type = d.type;
  if (d.rating !== undefined) updateData.rating = d.rating;
  if (d.photoUrl !== undefined) updateData.photoUrl = d.photoUrl;
  if (d.description !== undefined) updateData.description = d.description;
  if (d.address !== undefined) updateData.address = d.address;
  if (d.isFeatured !== undefined) updateData.isFeatured = d.isFeatured;
  if (d.isActive !== undefined) updateData.isActive = d.isActive;

  const [location] = await db.update(locationsTable).set(updateData).where(eq(locationsTable.id, params.data.id)).returning();
  if (!location) {
    res.status(404).json({ error: "Location not found" });
    return;
  }
  res.json({
    id: location.id, name: location.name, city: location.city, type: location.type, rating: location.rating,
    photoUrl: location.photoUrl, description: location.description, address: location.address,
    isFeatured: location.isFeatured, isActive: location.isActive, createdAt: location.createdAt,
  });
});

router.delete("/admin/locations/:id", requireAdmin, async (req, res): Promise<void> => {
  const params = AdminDeleteLocationParams.safeParse({ id: req.params.id });
  if (!params.success) {
    res.status(400).json({ error: "Invalid ID" });
    return;
  }
  await db.delete(locationsTable).where(eq(locationsTable.id, params.data.id));
  res.sendStatus(204);
});

// Memberships
router.get("/admin/memberships", requireAdmin, async (_req, res): Promise<void> => {
  const memberships = await db.select({
    membership: membershipsTable,
    userName: usersTable.name,
    userEmail: usersTable.email,
  }).from(membershipsTable)
    .leftJoin(usersTable, eq(membershipsTable.userId, usersTable.id));

  res.json(memberships.map(({ membership, userName, userEmail }) => ({
    id: membership.id, userId: membership.userId,
    userName: userName ?? "", userEmail: userEmail ?? "",
    tier: membership.tier, expiresAt: membership.expiresAt, createdAt: membership.createdAt,
  })));
});

router.patch("/admin/memberships/:userId", requireAdmin, async (req, res): Promise<void> => {
  const params = AdminUpdateMembershipParams.safeParse({ userId: req.params.userId });
  if (!params.success) {
    res.status(400).json({ error: "Invalid user ID" });
    return;
  }
  const parsed = AdminUpdateMembershipBody.safeParse(req.body);
  if (!parsed.success) {
    res.status(400).json({ error: parsed.error.message });
    return;
  }
  const updateData: Record<string, unknown> = { tier: parsed.data.tier };
  if (parsed.data.expiresAt) updateData.expiresAt = new Date(parsed.data.expiresAt);

  let [membership] = await db.select().from(membershipsTable).where(eq(membershipsTable.userId, params.data.userId));
  if (!membership) {
    const [created] = await db.insert(membershipsTable).values({ userId: params.data.userId, tier: parsed.data.tier }).returning();
    membership = created;
  } else {
    const [updated] = await db.update(membershipsTable).set(updateData).where(eq(membershipsTable.userId, params.data.userId)).returning();
    membership = updated;
  }

  const [user] = await db.select().from(usersTable).where(eq(usersTable.id, params.data.userId));
  res.json({
    id: membership.id, userId: membership.userId,
    userName: user?.name ?? "", userEmail: user?.email ?? "",
    tier: membership.tier, expiresAt: membership.expiresAt, createdAt: membership.createdAt,
  });
});

// Membership plans
router.get("/admin/membership-plans", requireAdmin, async (_req, res): Promise<void> => {
  const plans = await db.select().from(membershipPlansTable).orderBy(membershipPlansTable.sortOrder);
  res.json(plans);
});

router.post("/admin/membership-plans", requireAdmin, async (req, res): Promise<void> => {
  const parsed = AdminCreateMembershipPlanBody.safeParse(req.body);
  if (!parsed.success) {
    res.status(400).json({ error: parsed.error.message });
    return;
  }
  const existing = await db.select().from(membershipPlansTable).where(eq(membershipPlansTable.key, parsed.data.key));
  if (existing.length > 0) {
    res.status(409).json({ error: "A plan with this key already exists." });
    return;
  }
  const [created] = await db.insert(membershipPlansTable).values({
    key: parsed.data.key,
    name: parsed.data.name,
    price: parsed.data.price,
    durationDays: parsed.data.durationDays ?? 30,
    description: parsed.data.description ?? null,
    features: parsed.data.features ?? [],
    sortOrder: parsed.data.sortOrder ?? 0,
    isActive: parsed.data.isActive ?? true,
  }).returning();
  res.status(201).json(created);
});

router.patch("/admin/membership-plans/:id", requireAdmin, async (req, res): Promise<void> => {
  const params = AdminUpdateMembershipPlanParams.safeParse({ id: req.params.id });
  if (!params.success) {
    res.status(400).json({ error: "Invalid plan ID" });
    return;
  }
  const parsed = AdminUpdateMembershipPlanBody.safeParse(req.body);
  if (!parsed.success) {
    res.status(400).json({ error: parsed.error.message });
    return;
  }
  const updateData: Record<string, unknown> = {};
  for (const key of ["name", "price", "durationDays", "description", "features", "sortOrder", "isActive"] as const) {
    if (parsed.data[key] !== undefined) updateData[key] = parsed.data[key];
  }
  const [updated] = await db.update(membershipPlansTable).set(updateData).where(eq(membershipPlansTable.id, params.data.id)).returning();
  if (!updated) {
    res.status(404).json({ error: "Plan not found" });
    return;
  }
  res.json(updated);
});

router.delete("/admin/membership-plans/:id", requireAdmin, async (req, res): Promise<void> => {
  const params = AdminDeleteMembershipPlanParams.safeParse({ id: req.params.id });
  if (!params.success) {
    res.status(400).json({ error: "Invalid plan ID" });
    return;
  }
  const [referencing] = await db.select().from(membershipPaymentsTable).where(eq(membershipPaymentsTable.planId, params.data.id));
  if (referencing) {
    res.status(409).json({ error: "Cannot delete a plan that has payment records. Deactivate it instead." });
    return;
  }
  await db.delete(membershipPlansTable).where(eq(membershipPlansTable.id, params.data.id));
  res.sendStatus(204);
});

// Payment methods
router.get("/admin/payment-methods", requireAdmin, async (_req, res): Promise<void> => {
  const methods = await db.select().from(paymentMethodsTable).orderBy(paymentMethodsTable.sortOrder);
  res.json(methods);
});

router.patch("/admin/payment-methods/:id", requireAdmin, async (req, res): Promise<void> => {
  const params = AdminUpdatePaymentMethodParams.safeParse({ id: req.params.id });
  if (!params.success) {
    res.status(400).json({ error: "Invalid payment method ID" });
    return;
  }
  const parsed = AdminUpdatePaymentMethodBody.safeParse(req.body);
  if (!parsed.success) {
    res.status(400).json({ error: parsed.error.message });
    return;
  }
  const updateData: Record<string, unknown> = {};
  for (const key of ["label", "receivingNumber", "instructions", "isActive", "sortOrder"] as const) {
    if (parsed.data[key] !== undefined) updateData[key] = parsed.data[key];
  }
  const [updated] = await db.update(paymentMethodsTable).set(updateData).where(eq(paymentMethodsTable.id, params.data.id)).returning();
  if (!updated) {
    res.status(404).json({ error: "Payment method not found" });
    return;
  }
  res.json(updated);
});

// Membership payments (verification)
router.get("/admin/membership-payments", requireAdmin, async (_req, res): Promise<void> => {
  const rows = await db.select({
    payment: membershipPaymentsTable,
    userName: usersTable.name,
    userEmail: usersTable.email,
    planName: membershipPlansTable.name,
  }).from(membershipPaymentsTable)
    .leftJoin(usersTable, eq(membershipPaymentsTable.userId, usersTable.id))
    .leftJoin(membershipPlansTable, eq(membershipPaymentsTable.planId, membershipPlansTable.id))
    .orderBy(desc(membershipPaymentsTable.createdAt));

  res.json(rows.map(({ payment, userName, userEmail, planName }) => ({
    ...payment,
    userName: userName ?? "",
    userEmail: userEmail ?? "",
    planName: planName ?? "",
  })));
});

router.patch("/admin/membership-payments/:id", requireAdmin, async (req, res): Promise<void> => {
  const params = AdminReviewMembershipPaymentParams.safeParse({ id: req.params.id });
  if (!params.success) {
    res.status(400).json({ error: "Invalid payment ID" });
    return;
  }
  const parsed = AdminReviewMembershipPaymentBody.safeParse(req.body);
  if (!parsed.success) {
    res.status(400).json({ error: parsed.error.message });
    return;
  }

  const [payment] = await db.select().from(membershipPaymentsTable).where(eq(membershipPaymentsTable.id, params.data.id));
  if (!payment) {
    res.status(404).json({ error: "Payment not found" });
    return;
  }
  if (payment.status !== "pending") {
    res.status(409).json({ error: "This payment has already been reviewed." });
    return;
  }

  const updated = await db.transaction(async (tx) => {
    // Atomic status transition: only flips a still-pending payment, so concurrent
    // reviews cannot both succeed.
    const [row] = await tx.update(membershipPaymentsTable).set({
      status: parsed.data.status,
      note: parsed.data.note ?? null,
      reviewedAt: new Date(),
    }).where(and(eq(membershipPaymentsTable.id, params.data.id), eq(membershipPaymentsTable.status, "pending"))).returning();
    if (!row) return null;

    // On approval, activate the user's membership within the same transaction.
    if (parsed.data.status === "approved") {
      const [plan] = await tx.select().from(membershipPlansTable).where(eq(membershipPlansTable.id, payment.planId));
      const expiresAt = new Date();
      expiresAt.setDate(expiresAt.getDate() + (plan?.durationDays ?? 30));
      const [existing] = await tx.select().from(membershipsTable).where(eq(membershipsTable.userId, payment.userId));
      if (!existing) {
        await tx.insert(membershipsTable).values({ userId: payment.userId, tier: plan?.key ?? "free", expiresAt });
      } else {
        await tx.update(membershipsTable).set({ tier: plan?.key ?? "free", expiresAt }).where(eq(membershipsTable.userId, payment.userId));
      }
    }
    return row;
  });

  if (!updated) {
    res.status(409).json({ error: "This payment has already been reviewed." });
    return;
  }

  const [user] = await db.select().from(usersTable).where(eq(usersTable.id, payment.userId));
  const [planRow] = await db.select().from(membershipPlansTable).where(eq(membershipPlansTable.id, payment.planId));
  res.json({
    ...updated,
    userName: user?.name ?? "",
    userEmail: user?.email ?? "",
    planName: planRow?.name ?? "",
  });
});

// Bookings
router.get("/admin/bookings", requireAdmin, async (_req, res): Promise<void> => {
  const bookings = await db.select({
    booking: bookingsTable,
    profileName: profilesTable.name,
    locationName: locationsTable.name,
  }).from(bookingsTable)
    .leftJoin(profilesTable, eq(bookingsTable.profileId, profilesTable.id))
    .leftJoin(locationsTable, eq(bookingsTable.locationId, locationsTable.id))
    .orderBy(bookingsTable.createdAt);

  res.json(bookings.map(({ booking, profileName, locationName }) => ({
    id: booking.id, userId: booking.userId, profileId: booking.profileId,
    locationId: booking.locationId, profileName, locationName,
    status: booking.status, notes: booking.notes, scheduledAt: booking.scheduledAt, createdAt: booking.createdAt,
  })));
});

// Wallets
router.get("/admin/wallets", requireAdmin, async (_req, res): Promise<void> => {
  const rows = await db.select({
    wallet: walletsTable,
    userName: usersTable.name,
    userEmail: usersTable.email,
    userRole: usersTable.role,
  }).from(walletsTable)
    .leftJoin(usersTable, eq(walletsTable.userId, usersTable.id))
    .orderBy(desc(walletsTable.balance));
  res.json(rows.map(({ wallet, userName, userEmail, userRole }) => ({
    id: wallet.id, userId: wallet.userId, balance: wallet.balance,
    userName: userName ?? "", userEmail: userEmail ?? "", userRole: userRole ?? "",
    createdAt: wallet.createdAt,
  })));
});

router.post("/admin/wallets/:userId/credit", requireAdmin, async (req, res): Promise<void> => {
  const params = AdminCreditWalletParams.safeParse({ userId: req.params.userId });
  if (!params.success) {
    res.status(400).json({ error: "Invalid user ID" });
    return;
  }
  const parsed = AdminCreditWalletBody.safeParse(req.body);
  if (!parsed.success) {
    res.status(400).json({ error: parsed.error.message });
    return;
  }
  const { amount, note } = parsed.data;
  if (!Number.isInteger(amount) || amount === 0) {
    res.status(400).json({ error: "Amount must be a non-zero whole number" });
    return;
  }
  const [user] = await db.select().from(usersTable).where(eq(usersTable.id, params.data.userId));
  if (!user) {
    res.status(404).json({ error: "User not found" });
    return;
  }
  try {
    const wallet = await adjustBalance({
      userId: params.data.userId,
      amount,
      type: amount > 0 ? "deposit" : "withdrawal",
      description: note?.trim() || (amount > 0 ? "Admin credit" : "Admin debit"),
    });
    res.json({ id: wallet.id, userId: wallet.userId, balance: wallet.balance });
  } catch (err) {
    if (err instanceof InsufficientFundsError) {
      res.status(400).json({ error: "Cannot debit more than the wallet balance." });
      return;
    }
    throw err;
  }
});

// Deposit requests
router.get("/admin/deposits", requireAdmin, async (_req, res): Promise<void> => {
  const rows = await db.select({
    deposit: walletDepositsTable,
    userName: usersTable.name,
    userEmail: usersTable.email,
  }).from(walletDepositsTable)
    .leftJoin(usersTable, eq(walletDepositsTable.userId, usersTable.id))
    .orderBy(desc(walletDepositsTable.createdAt));
  res.json(rows.map(({ deposit, userName, userEmail }) => ({
    ...deposit, userName: userName ?? "", userEmail: userEmail ?? "",
  })));
});

router.patch("/admin/deposits/:id", requireAdmin, async (req, res): Promise<void> => {
  const params = AdminReviewDepositParams.safeParse({ id: req.params.id });
  if (!params.success) {
    res.status(400).json({ error: "Invalid deposit ID" });
    return;
  }
  const parsed = AdminReviewDepositBody.safeParse(req.body);
  if (!parsed.success) {
    res.status(400).json({ error: parsed.error.message });
    return;
  }
  const [deposit] = await db.select().from(walletDepositsTable).where(eq(walletDepositsTable.id, params.data.id));
  if (!deposit) {
    res.status(404).json({ error: "Deposit not found" });
    return;
  }
  if (deposit.status !== "pending") {
    res.status(409).json({ error: "This deposit has already been reviewed." });
    return;
  }

  const updated = await db.transaction(async (tx) => {
    const [row] = await tx.update(walletDepositsTable).set({
      status: parsed.data.status,
      note: parsed.data.note ?? null,
      reviewedAt: new Date(),
    }).where(and(eq(walletDepositsTable.id, params.data.id), eq(walletDepositsTable.status, "pending"))).returning();
    if (!row) return null;
    if (parsed.data.status === "approved") {
      await adjustBalance({
        userId: deposit.userId,
        amount: deposit.amount,
        type: "deposit",
        description: `Deposit approved (${deposit.method})`,
        tx,
      });
    }
    return row;
  });
  if (!updated) {
    res.status(409).json({ error: "This deposit has already been reviewed." });
    return;
  }
  res.json(updated);
});

// Withdrawal requests
router.get("/admin/withdrawals", requireAdmin, async (_req, res): Promise<void> => {
  const rows = await db.select({
    withdrawal: withdrawalsTable,
    userName: usersTable.name,
    userEmail: usersTable.email,
  }).from(withdrawalsTable)
    .leftJoin(usersTable, eq(withdrawalsTable.userId, usersTable.id))
    .orderBy(desc(withdrawalsTable.createdAt));
  res.json(rows.map(({ withdrawal, userName, userEmail }) => ({
    ...withdrawal, userName: userName ?? "", userEmail: userEmail ?? "",
  })));
});

router.patch("/admin/withdrawals/:id", requireAdmin, async (req, res): Promise<void> => {
  const params = AdminReviewWithdrawalParams.safeParse({ id: req.params.id });
  if (!params.success) {
    res.status(400).json({ error: "Invalid withdrawal ID" });
    return;
  }
  const parsed = AdminReviewWithdrawalBody.safeParse(req.body);
  if (!parsed.success) {
    res.status(400).json({ error: parsed.error.message });
    return;
  }
  const [withdrawal] = await db.select().from(withdrawalsTable).where(eq(withdrawalsTable.id, params.data.id));
  if (!withdrawal) {
    res.status(404).json({ error: "Withdrawal not found" });
    return;
  }
  if (withdrawal.status !== "pending") {
    res.status(409).json({ error: "This withdrawal has already been reviewed." });
    return;
  }

  const updated = await db.transaction(async (tx) => {
    const [row] = await tx.update(withdrawalsTable).set({
      status: parsed.data.status,
      note: parsed.data.note ?? null,
      reviewedAt: new Date(),
    }).where(and(eq(withdrawalsTable.id, params.data.id), eq(withdrawalsTable.status, "pending"))).returning();
    if (!row) return null;
    // Funds were debited when the request was created. Approval just confirms the
    // payout; rejection returns the held funds to the model's wallet.
    if (parsed.data.status === "rejected") {
      await adjustBalance({
        userId: withdrawal.userId,
        amount: withdrawal.amount,
        type: "refund",
        description: "Withdrawal request rejected — funds returned",
        tx,
      });
    }
    return row;
  });
  if (!updated) {
    res.status(409).json({ error: "This withdrawal has already been reviewed." });
    return;
  }
  res.json(updated);
});

// Commission stats
router.get("/admin/commission", requireAdmin, async (_req, res): Promise<void> => {
  const now = new Date();
  const startOfDay = new Date(now.getFullYear(), now.getMonth(), now.getDate());
  const startOfMonth = new Date(now.getFullYear(), now.getMonth(), 1);

  const sumSince = async (since?: Date) => {
    const conds = [eq(walletTransactionsTable.type, "commission")];
    if (since) conds.push(gte(walletTransactionsTable.createdAt, since));
    const [row] = await db
      .select({ total: sql<number>`coalesce(sum(${walletTransactionsTable.amount}), 0)` })
      .from(walletTransactionsTable)
      .where(and(...conds));
    return Number(row?.total ?? 0);
  };

  const [today, thisMonth, total] = await Promise.all([
    sumSince(startOfDay),
    sumSince(startOfMonth),
    sumSince(),
  ]);
  res.json({ today, thisMonth, total });
});

// Escrow stats — funds currently held + the platform admin wallet balance.
router.get("/admin/escrow", requireAdmin, async (_req, res): Promise<void> => {
  const [held] = await db
    .select({ total: sql<number>`coalesce(sum(${bookingsTable.totalPrice}), 0)` })
    .from(bookingsTable)
    .where(inArray(bookingsTable.status, ["paid", "proof_uploaded"]));

  const platformUserId = await getPlatformUserId();
  let balance = 0;
  if (platformUserId) {
    const [w] = await db.select().from(walletsTable).where(eq(walletsTable.userId, platformUserId));
    balance = w?.balance ?? 0;
  }
  res.json({ held: Number(held?.total ?? 0), balance });
});

// Order proofs — list submissions for review.
router.get("/admin/proofs", requireAdmin, async (_req, res): Promise<void> => {
  const rows = await db.select({
    proof: orderProofsTable,
    bookingStatus: bookingsTable.status,
    totalPrice: bookingsTable.totalPrice,
    duration: bookingsTable.duration,
    profileName: profilesTable.name,
    customerName: usersTable.name,
  }).from(orderProofsTable)
    .leftJoin(bookingsTable, eq(orderProofsTable.bookingId, bookingsTable.id))
    .leftJoin(profilesTable, eq(bookingsTable.profileId, profilesTable.id))
    .leftJoin(usersTable, eq(bookingsTable.userId, usersTable.id))
    .orderBy(desc(orderProofsTable.createdAt));

  res.json(rows.map((r) => ({
    id: r.proof.id,
    bookingId: r.proof.bookingId,
    userId: r.proof.userId,
    photoUrl: r.proof.photoUrl,
    status: r.proof.status,
    note: r.proof.note,
    reviewedAt: r.proof.reviewedAt,
    createdAt: r.proof.createdAt,
    bookingStatus: r.bookingStatus,
    totalPrice: r.totalPrice,
    duration: r.duration,
    profileName: r.profileName,
    customerName: r.customerName,
  })));
});

// Thrown inside the proof-review transaction when the booking row has already
// moved out of "proof_uploaded" (concurrent update), forcing the whole
// transaction to roll back so the proof status and booking status stay in sync.
class ProofReviewConflictError extends Error {}

// Review an order proof — approve (settle escrow → completed) or reject (→ paid).
router.patch("/admin/proofs/:id", requireAdmin, async (req, res): Promise<void> => {
  const params = AdminReviewProofParams.safeParse({ id: req.params.id });
  if (!params.success) {
    res.status(400).json({ error: "Invalid ID" });
    return;
  }
  const parsed = AdminReviewProofBody.safeParse(req.body);
  if (!parsed.success) {
    res.status(400).json({ error: parsed.error.message });
    return;
  }
  const reviewStatus = parsed.data.status;
  const note = parsed.data.note ?? null;

  const [proof] = await db.select().from(orderProofsTable).where(eq(orderProofsTable.id, params.data.id));
  if (!proof) {
    res.status(404).json({ error: "Proof not found" });
    return;
  }
  if (proof.status !== "pending") {
    res.status(409).json({ error: "This proof has already been reviewed." });
    return;
  }
  const [booking] = await db.select().from(bookingsTable).where(eq(bookingsTable.id, proof.bookingId));
  if (!booking) {
    res.status(404).json({ error: "Order not found" });
    return;
  }
  if (booking.status !== "proof_uploaded") {
    res.status(409).json({ error: "This order is not awaiting proof review." });
    return;
  }

  let modelUserId: number | null = null;
  if (booking.profileId) {
    const [profile] = await db.select().from(profilesTable).where(eq(profilesTable.id, booking.profileId));
    modelUserId = profile?.userId ?? null;
  }
  const platformUserId = await getPlatformUserId();
  const amount = booking.totalPrice ?? 0;

  let updatedProof;
  try {
    updatedProof = await db.transaction(async (tx) => {
      const [pr] = await tx
        .update(orderProofsTable)
        .set({ status: reviewStatus, note, reviewedAt: new Date() })
        .where(and(eq(orderProofsTable.id, proof.id), eq(orderProofsTable.status, "pending")))
        .returning();
      if (!pr) return null;

      if (reviewStatus === "approved") {
        const [b] = await tx
          .update(bookingsTable)
          .set({ status: "completed" })
          .where(and(eq(bookingsTable.id, booking.id), eq(bookingsTable.status, "proof_uploaded")))
          .returning();
        if (!b) throw new ProofReviewConflictError();
        // Settle escrow: release the hold, pay the model 90%, keep 10% commission.
        const { commission, modelAmount } = splitCommission(amount);
        if (amount > 0) {
          if (!platformUserId) throw new Error("No platform account configured for escrow");
          await adjustBalance({ userId: platformUserId, amount: -amount, type: "escrow_out", bookingId: booking.id, description: `Escrow release for order #${booking.id}`, tx });
          if (modelUserId && modelAmount > 0) {
            await adjustBalance({ userId: modelUserId, amount: modelAmount, type: "earning", bookingId: booking.id, description: `Earnings from order #${booking.id}`, tx });
          }
          if (commission > 0) {
            await adjustBalance({ userId: platformUserId, amount: commission, type: "commission", bookingId: booking.id, description: `Commission from order #${booking.id}`, tx });
          }
        }
      } else {
        // Rejected: send the order back to "paid" for re-upload. Funds stay in escrow.
        const [b] = await tx
          .update(bookingsTable)
          .set({ status: "paid" })
          .where(and(eq(bookingsTable.id, booking.id), eq(bookingsTable.status, "proof_uploaded")))
          .returning();
        if (!b) throw new ProofReviewConflictError();
      }
      return pr;
    });
  } catch (err) {
    if (err instanceof InsufficientFundsError) {
      res.status(409).json({ error: "Cannot settle this order due to insufficient escrow funds." });
      return;
    }
    if (err instanceof ProofReviewConflictError) {
      res.status(409).json({ error: "This order was already updated. Please refresh." });
      return;
    }
    throw err;
  }
  if (!updatedProof) {
    res.status(409).json({ error: "This proof was already reviewed. Please refresh." });
    return;
  }

  const [profile] = booking.profileId
    ? await db.select().from(profilesTable).where(eq(profilesTable.id, booking.profileId))
    : [null];
  const [customer] = await db.select().from(usersTable).where(eq(usersTable.id, booking.userId));
  res.json({
    id: updatedProof.id,
    bookingId: updatedProof.bookingId,
    userId: updatedProof.userId,
    photoUrl: updatedProof.photoUrl,
    status: updatedProof.status,
    note: updatedProof.note,
    reviewedAt: updatedProof.reviewedAt,
    createdAt: updatedProof.createdAt,
    bookingStatus: reviewStatus === "approved" ? "completed" : "paid",
    totalPrice: booking.totalPrice,
    duration: booking.duration,
    profileName: profile?.name ?? null,
    customerName: customer?.name ?? null,
  });
});

// Users
router.get("/admin/users", requireAdmin, async (_req, res): Promise<void> => {
  const users = await db.select().from(usersTable).orderBy(usersTable.createdAt);
  res.json(users.map(u => ({
    id: u.id, email: u.email, name: u.name, role: u.role,
    isOnline: u.isOnline, avatarUrl: u.avatarUrl, bio: u.bio, createdAt: u.createdAt,
  })));
});

export default router;
