import { Router, type IRouter } from "express";
import {
  db,
  membershipsTable,
  membershipPlansTable,
  paymentMethodsTable,
  membershipPaymentsTable,
  usersTable,
} from "@workspace/db";
import { eq, desc, and } from "drizzle-orm";
import { SubscribeMembershipBody } from "@workspace/api-zod";
import { requireAuth } from "../middlewares/auth";

const router: IRouter = Router();

router.get("/memberships/my", requireAuth, async (req, res): Promise<void> => {
  const userId = req.user!.id;
  const [user] = await db.select().from(usersTable).where(eq(usersTable.id, userId));
  let [membership] = await db.select().from(membershipsTable).where(eq(membershipsTable.userId, userId));
  if (!membership) {
    const [created] = await db.insert(membershipsTable).values({ userId, tier: "free" }).returning();
    membership = created;
  }
  res.json({
    id: membership.id,
    userId: membership.userId,
    userName: user.name,
    userEmail: user.email,
    tier: membership.tier,
    expiresAt: membership.expiresAt,
    createdAt: membership.createdAt,
  });
});

router.get("/memberships/tiers", requireAuth, async (_req, res): Promise<void> => {
  const plans = await db
    .select()
    .from(membershipPlansTable)
    .where(eq(membershipPlansTable.isActive, true))
    .orderBy(membershipPlansTable.sortOrder);
  res.json(plans);
});

router.get("/memberships/payment-methods", requireAuth, async (_req, res): Promise<void> => {
  const methods = await db
    .select()
    .from(paymentMethodsTable)
    .where(eq(paymentMethodsTable.isActive, true))
    .orderBy(paymentMethodsTable.sortOrder);
  res.json(methods);
});

router.get("/memberships/my-payments", requireAuth, async (req, res): Promise<void> => {
  const userId = req.user!.id;
  const rows = await db
    .select({
      payment: membershipPaymentsTable,
      planName: membershipPlansTable.name,
    })
    .from(membershipPaymentsTable)
    .leftJoin(membershipPlansTable, eq(membershipPaymentsTable.planId, membershipPlansTable.id))
    .where(eq(membershipPaymentsTable.userId, userId))
    .orderBy(desc(membershipPaymentsTable.createdAt));
  res.json(rows.map(({ payment, planName }) => ({ ...payment, planName: planName ?? "" })));
});

router.post("/memberships/subscribe", requireAuth, async (req, res): Promise<void> => {
  const parsed = SubscribeMembershipBody.safeParse(req.body);
  if (!parsed.success) {
    res.status(400).json({ error: "Missing or invalid required fields" });
    return;
  }
  const userId = req.user!.id;
  const { planId, method, transactionId } = parsed.data;

  const [plan] = await db.select().from(membershipPlansTable).where(eq(membershipPlansTable.id, planId));
  if (!plan || !plan.isActive) {
    res.status(404).json({ error: "Plan not found" });
    return;
  }

  const [paymentMethod] = await db
    .select()
    .from(paymentMethodsTable)
    .where(eq(paymentMethodsTable.method, method));
  if (!paymentMethod || !paymentMethod.isActive) {
    res.status(400).json({ error: "Invalid payment method" });
    return;
  }

  const trimmedTrx = transactionId.trim();
  if (!trimmedTrx) {
    res.status(400).json({ error: "Transaction ID is required" });
    return;
  }

  // Prevent duplicate pending submissions for the same user. A partial unique
  // index (one pending per user) enforces this atomically; the pre-check just
  // gives a friendly response in the common (non-racing) case.
  const [existingPending] = await db
    .select()
    .from(membershipPaymentsTable)
    .where(and(eq(membershipPaymentsTable.userId, userId), eq(membershipPaymentsTable.status, "pending")));
  if (existingPending) {
    res.status(409).json({ error: "You already have a pending payment under review." });
    return;
  }

  let created;
  try {
    [created] = await db
      .insert(membershipPaymentsTable)
      .values({
        userId,
        planId: plan.id,
        method: paymentMethod.method,
        transactionId: trimmedTrx,
        amount: plan.price,
        paymentNumber: paymentMethod.receivingNumber,
        status: "pending",
      })
      .returning();
  } catch (err) {
    if (err instanceof Error && "code" in err && (err as { code?: string }).code === "23505") {
      res.status(409).json({ error: "You already have a pending payment under review." });
      return;
    }
    throw err;
  }

  res.status(201).json({ ...created, planName: plan.name });
});

export default router;
