"use server";

import { eq } from "drizzle-orm";
import { revalidatePath } from "next/cache";
import { z } from "zod";

import { requireOrgMember } from "@/lib/auth-guards";
import { isSupportedCurrency, type Currency } from "@/lib/billing/currency";
import { ensureStripeCustomer } from "@/lib/billing/customer";
import {
  BILLABLE_PLANS,
  SEAT_ADDON,
  type BillablePlan,
  type BillingInterval,
} from "@/lib/billing/plans";
import { getStripe } from "@/lib/billing/stripe";
import { db } from "@/lib/db";
import { orgSubscription } from "@/lib/db/schema";
import { env } from "@/lib/env";

const CheckoutSchema = z.object({
  plan: z.enum(["starter", "pro", "business"]),
  interval: z.enum(["month", "year"]),
  currency: z.string().refine(isSupportedCurrency, {
    message: "Unsupported currency — must be one of eur / usd / gbp / jpy / cad",
  }),
  extraSeats: z.number().int().min(0).max(100).optional().default(0),
});

function getOrigin(): string {
  return env.BETTER_AUTH_URL || "http://localhost:3000";
}

/* Creates a Stripe Checkout Session for the requested plan + interval.
   Owner-only. Returns { url } for client redirect.

   Multi-currency: `currency` is forwarded to Stripe so it can resolve the
   matching `currency_option` on each Price.
   Extra seats: when `extraSeats > 0`, a second line item is appended
   referencing the SEAT_ADDON Price for the chosen interval. */
export async function createCheckoutSession(input: {
  plan: BillablePlan;
  interval: BillingInterval;
  currency: Currency;
  extraSeats?: number;
}): Promise<{ url: string }> {
  const { activeOrganizationId } = await requireOrgMember({ minRole: "owner" });
  const { plan, interval, currency, extraSeats } = CheckoutSchema.parse(input);

  const cfg = BILLABLE_PLANS[plan];
  const tierPriceId = cfg.stripePriceIds?.[interval];
  if (!tierPriceId) {
    throw new Error(`No Stripe price configured for ${plan}/${interval}`);
  }

  const lineItems: Array<{ price: string; quantity: number }> = [
    { price: tierPriceId, quantity: 1 },
  ];

  if (extraSeats > 0) {
    const seatPriceId = SEAT_ADDON.stripePriceIds[interval];
    if (!seatPriceId) {
      throw new Error(
        `Seat add-on price not configured for interval ${interval} — run stripe-bootstrap.ts`,
      );
    }
    lineItems.push({ price: seatPriceId, quantity: extraSeats });
  }

  const customerId = await ensureStripeCustomer(activeOrganizationId);
  const origin = getOrigin();
  const successQuery = `session_id={CHECKOUT_SESSION_ID}&currency=${currency}&extraSeats=${extraSeats}`;
  const cancelQuery = `currency=${currency}&extraSeats=${extraSeats}`;

  const session = await getStripe().checkout.sessions.create({
    mode: "subscription",
    customer: customerId,
    currency,
    // CRITICAL: do NOT pass payment_method_types — let Stripe pick dynamically.
    line_items: lineItems,
    client_reference_id: activeOrganizationId,
    metadata: {
      orgId: activeOrganizationId,
      plan,
      interval,
      currency,
      extraSeats: String(extraSeats),
    },
    success_url: `${origin}/settings/billing/success?${successQuery}`,
    cancel_url: `${origin}/settings/billing?${cancelQuery}`,
    allow_promotion_codes: true,
    billing_address_collection: "required",
    automatic_tax: { enabled: env.STRIPE_TAX_ENABLED },
  });

  if (!session.url) throw new Error("Stripe did not return a checkout URL");
  return { url: session.url };
}

/* Opens a Customer Portal session for self-service (cancel, update card,
   invoices). Owner-only. */
export async function openCustomerPortal(): Promise<{ url: string }> {
  const { activeOrganizationId } = await requireOrgMember({ minRole: "owner" });
  const customerId = await ensureStripeCustomer(activeOrganizationId);
  const origin = getOrigin();

  const portal = await getStripe().billingPortal.sessions.create({
    customer: customerId,
    return_url: `${origin}/settings/billing`,
  });
  return { url: portal.url };
}

/* Schedules the subscription to cancel at the end of the current period.
   Reactivation = call again with { cancel: false } (or via Portal). */
export async function cancelAtPeriodEnd(input: { cancel: boolean }): Promise<void> {
  const { activeOrganizationId } = await requireOrgMember({ minRole: "owner" });
  const cancel = z.boolean().parse(input.cancel);

  const sub = await db
    .select({ stripeSubscriptionId: orgSubscription.stripeSubscriptionId })
    .from(orgSubscription)
    .where(eq(orgSubscription.organizationId, activeOrganizationId))
    .limit(1);
  const subId = sub[0]?.stripeSubscriptionId;
  if (!subId) throw new Error("No active subscription to cancel");

  await getStripe().subscriptions.update(subId, {
    cancel_at_period_end: cancel,
  });
  revalidatePath("/settings/billing");
  revalidatePath("/", "layout");
}

/* Polled by /settings/billing/success — returns current plan from DB.
   Caller must be a member of the requested org (we re-verify via
   requireOrgMember to prevent enumeration). */
export async function getCurrentPlanForOrg(organizationId: string): Promise<string> {
  const { activeOrganizationId } = await requireOrgMember();
  if (activeOrganizationId !== organizationId) {
    throw new Error("Forbidden");
  }
  const [sub] = await db
    .select({ plan: orgSubscription.plan })
    .from(orgSubscription)
    .where(eq(orgSubscription.organizationId, organizationId))
    .limit(1);
  return sub?.plan ?? "free";
}
