import { NextRequest, NextResponse } from "next/server";
import { downgradeToFreePlan } from "@/lib/billing";
import { prisma } from "@/lib/db";
import { getSessionUser } from "@/lib/session";
import { getStripe, isBillingDevMock, isStripeConfigured } from "@/lib/stripe";

type CancelWhen = "immediate" | "period_end";
type CancelAction = "cancel" | "resume";

function periodEndIso(sub: {
  cancel_at?: number | null;
  current_period_end?: number;
  items?: { data?: Array<{ current_period_end?: number }> };
}): string | null {
  const fromItem = sub.items?.data?.[0]?.current_period_end;
  const ts = sub.cancel_at ?? fromItem ?? sub.current_period_end ?? null;
  return ts ? new Date(ts * 1000).toISOString() : null;
}

/**
 * Cancel Pro subscription via Stripe.
 * Body: { when: "immediate" | "period_end" } or { action: "resume" }
 */
export async function POST(req: NextRequest) {
  const user = await getSessionUser();
  if (!user) {
    return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
  }

  if (user.plan !== "PREMIUM") {
    return NextResponse.json({ error: "No active subscription to cancel" }, { status: 400 });
  }

  let action: CancelAction = "cancel";
  let when: CancelWhen = "immediate";
  try {
    const body = await req.json();
    if (body?.action === "resume") action = "resume";
    if (body?.when === "period_end" || body?.when === "immediate") when = body.when;
  } catch {
    // empty body → default immediate cancel (legacy)
  }

  const dbUser = await prisma.user.findUniqueOrThrow({
    where: { id: user.id },
    select: { stripeSubscriptionId: true },
  });

  const subId = dbUser.stripeSubscriptionId;
  const isMockSub = !subId || subId.startsWith("dev_sub_");
  const useStripe =
    Boolean(subId) &&
    !isMockSub &&
    isStripeConfigured() &&
    !isBillingDevMock();

  //  Resume (undo cancel-at-period-end) 
  if (action === "resume") {
    if (useStripe && subId) {
      try {
        const stripe = getStripe();
        const sub = await stripe.subscriptions.update(subId, {
          cancel_at_period_end: false,
        });
        return NextResponse.json({
          ok: true,
          action: "resume",
          cancelAtPeriodEnd: false,
          currentPeriodEnd: periodEndIso(sub as { current_period_end?: number }),
        });
      } catch (err) {
        console.error("[stripe] resume subscription failed", err);
        return NextResponse.json(
          { error: err instanceof Error ? err.message : "Failed to keep subscription" },
          { status: 502 },
        );
      }
    }
    return NextResponse.json({
      ok: true,
      action: "resume",
      cancelAtPeriodEnd: false,
      mocked: true,
    });
  }

  //  Cancel 
  if (when === "period_end") {
    if (useStripe && subId) {
      try {
        const stripe = getStripe();
        const sub = await stripe.subscriptions.update(subId, {
          cancel_at_period_end: true,
        });
        const endsAt = periodEndIso(
          sub as {
            cancel_at?: number | null;
            current_period_end?: number;
            items?: { data?: Array<{ current_period_end?: number }> };
          },
        );
        return NextResponse.json({
          ok: true,
          when: "period_end",
          cancelAtPeriodEnd: true,
          endsAt,
          // Keep Pro until Stripe sends customer.subscription.deleted
        });
      } catch (err) {
        console.error("[stripe] cancel at period end failed", err);
        return NextResponse.json(
          { error: err instanceof Error ? err.message : "Failed to schedule cancellation" },
          { status: 502 },
        );
      }
    }

    // Mock / no Stripe id: schedule locally by leaving Pro and reporting next month
    const endsAt = new Date();
    endsAt.setMonth(endsAt.getMonth() + 1);
    return NextResponse.json({
      ok: true,
      when: "period_end",
      cancelAtPeriodEnd: true,
      endsAt: endsAt.toISOString(),
      mocked: true,
    });
  }

  // immediate
  if (useStripe && subId) {
    try {
      const stripe = getStripe();
      await stripe.subscriptions.cancel(subId);
    } catch (err) {
      console.error("[stripe] cancel subscription failed", err);
      return NextResponse.json(
        {
          error:
            err instanceof Error
              ? err.message
              : "Stripe could not cancel the subscription. Your plan was not changed.",
        },
        { status: 502 },
      );
    }
  }

  await downgradeToFreePlan(user.id);
  return NextResponse.json({
    ok: true,
    when: "immediate",
    cancelAtPeriodEnd: false,
    plan: "FREE",
  });
}

/** Current Stripe subscription cancel status for settings UI. */
export async function GET() {
  const user = await getSessionUser();
  if (!user) {
    return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
  }

  if (user.plan !== "PREMIUM") {
    return NextResponse.json({
      plan: user.plan,
      cancelAtPeriodEnd: false,
      endsAt: null,
    });
  }

  const dbUser = await prisma.user.findUniqueOrThrow({
    where: { id: user.id },
    select: { stripeSubscriptionId: true },
  });

  const subId = dbUser.stripeSubscriptionId;
  if (
    !subId ||
    subId.startsWith("dev_sub_") ||
    !isStripeConfigured() ||
    isBillingDevMock()
  ) {
    return NextResponse.json({
      plan: "PREMIUM",
      cancelAtPeriodEnd: false,
      endsAt: null,
      mocked: true,
    });
  }

  try {
    const stripe = getStripe();
    const sub = await stripe.subscriptions.retrieve(subId);
    return NextResponse.json({
      plan: "PREMIUM",
      status: sub.status,
      cancelAtPeriodEnd: Boolean(sub.cancel_at_period_end),
      endsAt: periodEndIso(
        sub as {
          cancel_at?: number | null;
          current_period_end?: number;
          items?: { data?: Array<{ current_period_end?: number }> };
        },
      ),
    });
  } catch (err) {
    console.error("[stripe] retrieve subscription failed", err);
    return NextResponse.json({
      plan: "PREMIUM",
      cancelAtPeriodEnd: false,
      endsAt: null,
      error: "Could not load subscription status from Stripe",
    });
  }
}
