'use client';

import { Button } from '@/components/ui/button';
import { apiFetch } from '@/lib/api';
import { EmbeddedCheckout, EmbeddedCheckoutProvider } from '@stripe/react-stripe-js';
import { loadStripe } from '@stripe/stripe-js';
import { Loader2, X } from 'lucide-react';
import Link from 'next/link';
import { useRouter, useSearchParams } from 'next/navigation';
import { Suspense, useCallback, useEffect, useState } from 'react';

// Load Stripe.js once at module scope (Stripe's recommendation). Null when the
// publishable key isn't baked into the build — the modal then shows a clear
// "not configured" message instead of throwing.
const STRIPE_PK = process.env.NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY;
const stripePromise = STRIPE_PK ? loadStripe(STRIPE_PK) : null;

type Plan = 'hobby' | 'pro' | 'team' | 'enterprise';
type Tier = 'pro_monthly' | 'pro_yearly' | 'team_monthly' | 'team_yearly';

interface SubscriptionInfo {
  id: string;
  status: string;
  currentPeriodEnd: number;
  cancelAtPeriodEnd: boolean;
  priceId: string | null;
  amount: number | null;
  currency: string | null;
  interval: string | null;
}

interface Invoice {
  id: string;
  number: string | null;
  status: string | null;
  amountPaid: number;
  currency: string;
  created: number;
  pdfUrl: string | null;
  hostedUrl: string | null;
}

interface BillingStatus {
  plan: Plan;
  hasCustomer: boolean;
  hasSubscription: boolean;
  suspended: boolean;
  suspendedReason: string | null;
  subscription?: SubscriptionInfo;
  invoices?: Invoice[];
  _stripeError?: boolean;
}

const PLAN_LABEL: Record<Plan, string> = {
  hobby: 'Hobby (free)',
  pro: 'Pro',
  team: 'Team',
  enterprise: 'Enterprise',
};

function formatMoney(amount: number | null, currency: string | null): string {
  if (amount === null || currency === null) return '—';
  return new Intl.NumberFormat(undefined, {
    style: 'currency',
    currency: currency.toUpperCase(),
  }).format(amount / 100);
}

function BillingInner() {
  const router = useRouter();
  const searchParams = useSearchParams();
  const justSubscribed = searchParams.get('success') === 'true';
  const cancelledCheckout = searchParams.get('cancelled') === 'true';
  const autoUpgradeTier = searchParams.get('tier') as Tier | null;

  const [status, setStatus] = useState<BillingStatus | null>(null);
  const [error, setError] = useState<string | null>(null);
  const [busy, setBusy] = useState<string | null>(null);
  // When set, the in-app embedded Stripe checkout modal is open.
  const [clientSecret, setClientSecret] = useState<string | null>(null);

  const loadStatus = useCallback(() => {
    apiFetch<BillingStatus>('/v1/billing/status')
      .then(setStatus)
      .catch((e) => {
        const err = e as { status?: number };
        if (err.status === 401) router.push('/login?returnTo=/settings/billing');
        else setError((e as Error).message);
      });
  }, [router]);

  useEffect(() => {
    loadStatus();
  }, [loadStatus]);

  useEffect(() => {
    if (!justSubscribed) return;
    let tries = 0;
    const id = setInterval(() => {
      tries += 1;
      loadStatus();
      if (tries >= 6) clearInterval(id);
    }, 1500);
    return () => clearInterval(id);
  }, [justSubscribed, loadStatus]);

  const startCheckout = useCallback(async (tier: Tier) => {
    setBusy(tier);
    setError(null);
    try {
      const res = await apiFetch<{ clientSecret: string }>('/v1/billing/checkout-session', {
        method: 'POST',
        body: JSON.stringify({ tier }),
      });
      // Open the embedded checkout in-app instead of redirecting to Stripe.
      setClientSecret(res.clientSecret);
    } catch (e) {
      const detail = (e as { detail?: { detail?: string; error?: string } }).detail;
      setError(detail?.detail ?? detail?.error ?? (e as Error).message);
    } finally {
      setBusy(null);
    }
  }, []);

  useEffect(() => {
    if (!autoUpgradeTier || !status) return;
    if (status.hasSubscription) return;
    void startCheckout(autoUpgradeTier);
  }, [autoUpgradeTier, status, startCheckout]);

  async function changePlan(tier: Tier) {
    if (!confirm(`Switch to ${tier.replace('_', ' ')}? Prorated charges apply immediately.`)) return;
    setBusy(`change-${tier}`);
    setError(null);
    try {
      await apiFetch('/v1/billing/change-plan', {
        method: 'POST',
        body: JSON.stringify({ tier }),
      });
      // Webhook will update plan asynchronously; poll briefly.
      let tries = 0;
      const id = setInterval(() => {
        tries += 1;
        loadStatus();
        if (tries >= 6) clearInterval(id);
      }, 1500);
    } catch (e) {
      const detail = (e as { detail?: { detail?: string; error?: string } }).detail;
      setError(detail?.detail ?? detail?.error ?? (e as Error).message);
    } finally {
      setBusy(null);
    }
  }

  async function cancelSubscription() {
    if (!confirm('Cancel subscription at the end of the current billing period?')) return;
    setBusy('cancel');
    setError(null);
    try {
      await apiFetch('/v1/billing/cancel', { method: 'POST', body: '{}' });
      loadStatus();
    } catch (e) {
      setError((e as Error).message);
    } finally {
      setBusy(null);
    }
  }

  async function reactivateSubscription() {
    setBusy('reactivate');
    setError(null);
    try {
      await apiFetch('/v1/billing/reactivate', { method: 'POST', body: '{}' });
      loadStatus();
    } catch (e) {
      setError((e as Error).message);
    } finally {
      setBusy(null);
    }
  }

  if (!status && !error) {
    return (
      <div className="mx-auto max-w-3xl px-6 py-12 text-center">
        <Loader2 className="mx-auto animate-spin text-[--color-fg-muted]" size={20} />
      </div>
    );
  }

  const sub = status?.subscription;
  const hasSub = Boolean(status?.hasSubscription && sub);
  const planValue = status?.plan ?? 'hobby';
  const currentPlanIsPro = planValue === 'pro';
  const currentPlanIsTeam = planValue === 'team';

  return (
    <div className="mx-auto max-w-3xl px-6 py-10">
      {clientSecret && (
        <CheckoutModal
          clientSecret={clientSecret}
          onClose={() => {
            setClientSecret(null);
            setBusy(null);
          }}
        />
      )}
      <div>
        <h1 className="text-[22px] font-semibold tracking-tight">Billing</h1>
        <p className="mt-1 text-[13px] text-[--color-fg-muted]">
          Plan, renewal, invoices and cancellation — everything in-app.
        </p>
      </div>

      {cancelledCheckout && (
        <Alert tone="muted">Checkout cancelled. No charge made.</Alert>
      )}
      {justSubscribed && (
        <Alert tone="success">
          Subscription active. Plan updates within a few seconds — refreshing…
        </Alert>
      )}
      {status?.suspended && (
        <Alert tone="warn">
          <strong>Subscription paused</strong> — {status.suspendedReason ?? 'payment issue'}. New
          servers + previews are blocked until payment succeeds; existing servers keep running.
        </Alert>
      )}
      {status?._stripeError && (
        <Alert tone="warn">
          Live billing data temporarily unavailable — showing local state only. Try again in a
          minute.
        </Alert>
      )}
      {error && <Alert tone="error">{error}</Alert>}

      {status && (
        <div className="panel mt-6 p-5">
          <div className="flex flex-wrap items-start justify-between gap-3">
            <div>
              <div className="text-[11px] uppercase tracking-wider text-[--color-fg-subtle]">
                Current plan
              </div>
              <div className="mt-1 flex items-baseline gap-2">
                <span className="text-[22px] font-semibold tracking-tight">
                  {PLAN_LABEL[status.plan]}
                </span>
                {sub?.amount !== null && sub?.amount !== undefined && (
                  <span className="text-[13px] text-[--color-fg-muted]">
                    {formatMoney(sub.amount, sub.currency)} / {sub.interval}
                  </span>
                )}
              </div>
            </div>
            {sub && (
              <div className="text-right text-[12px]">
                <div className="text-[--color-fg-subtle]">
                  {sub.cancelAtPeriodEnd ? 'Cancels' : 'Renews'}
                </div>
                <div className="mt-0.5 mono text-[--color-fg]">
                  {new Date(sub.currentPeriodEnd * 1000).toLocaleDateString()}
                </div>
              </div>
            )}
          </div>

          {hasSub && sub && (
            <div className="mt-5 flex flex-wrap gap-2 border-t border-[--color-border] pt-4">
              {sub.cancelAtPeriodEnd ? (
                <>
                  <p className="w-full text-[12.5px] text-[--color-fg-muted]">
                    Scheduled to cancel on{' '}
                    <span className="mono text-[--color-fg]">
                      {new Date(sub.currentPeriodEnd * 1000).toLocaleDateString()}
                    </span>
                    . You keep paid features until then.
                  </p>
                  <Button
                    variant="primary"
                    size="md"
                    onClick={reactivateSubscription}
                    disabled={busy === 'reactivate'}
                  >
                    {busy === 'reactivate' ? 'Reactivating…' : 'Keep subscription'}
                  </Button>
                </>
              ) : (
                <Button
                  variant="ghost"
                  size="md"
                  onClick={cancelSubscription}
                  disabled={busy === 'cancel'}
                >
                  {busy === 'cancel' ? 'Cancelling…' : 'Cancel subscription'}
                </Button>
              )}
            </div>
          )}
        </div>
      )}

      {status && !status.hasSubscription && (
        <>
          <h2 className="mt-10 text-[15px] font-semibold tracking-tight">Choose a plan</h2>
          <div className="mt-3 grid gap-3 md:grid-cols-2">
            <TierCard
              name="Pro"
              monthly={49}
              yearly={490}
              features={['5 MCP servers', '1M tool calls / mo', 'Custom domain', 'Claude Haiku 4.5']}
              busy={busy}
              onSubscribe={startCheckout}
              monthlyTier="pro_monthly"
              yearlyTier="pro_yearly"
              highlight
            />
            <TierCard
              name="Team"
              monthly={199}
              yearly={1990}
              features={[
                '25 MCP servers',
                '10M tool calls / mo',
                'RBAC + audit log',
                'Claude Sonnet 4.6',
              ]}
              busy={busy}
              onSubscribe={startCheckout}
              monthlyTier="team_monthly"
              yearlyTier="team_yearly"
            />
          </div>
          <p className="mt-4 text-[12px] text-[--color-fg-subtle]">
            Annual saves 2 months. VAT calculated automatically based on your billing address.
            Cancel anytime from this page — service continues until end of period.
          </p>
        </>
      )}

      {status && status.hasSubscription && (
        <>
          <h2 className="mt-10 text-[15px] font-semibold tracking-tight">Switch plan</h2>
          <p className="mt-1 text-[12.5px] text-[--color-fg-muted]">
            Prorated immediately. The new amount is added to your next invoice.
          </p>
          <div className="mt-3 grid gap-2 md:grid-cols-2">
            {!currentPlanIsPro && (
              <PlanSwitch
                label="Pro — €49 / month"
                onClick={() => changePlan('pro_monthly')}
                busy={busy === 'change-pro_monthly'}
              />
            )}
            <PlanSwitch
              label={currentPlanIsPro ? 'Pro — €490 / year (2 months free)' : 'Pro — yearly'}
              onClick={() => changePlan('pro_yearly')}
              busy={busy === 'change-pro_yearly'}
            />
            {!currentPlanIsTeam && (
              <PlanSwitch
                label="Team — €199 / month"
                onClick={() => changePlan('team_monthly')}
                busy={busy === 'change-team_monthly'}
              />
            )}
            <PlanSwitch
              label={currentPlanIsTeam ? 'Team — €1990 / year (2 months free)' : 'Team — yearly'}
              onClick={() => changePlan('team_yearly')}
              busy={busy === 'change-team_yearly'}
            />
          </div>
        </>
      )}

      {status?.invoices && status.invoices.length > 0 && (
        <>
          <h2 className="mt-10 text-[15px] font-semibold tracking-tight">Invoices</h2>
          <div className="panel mt-3 divide-y divide-[--color-border]">
            {status.invoices.map((inv) => (
              <div key={inv.id} className="flex items-center justify-between px-4 py-3 text-[12.5px]">
                <div>
                  <div className="mono text-[--color-fg]">{inv.number ?? inv.id}</div>
                  <div className="text-[11px] text-[--color-fg-subtle]">
                    {new Date(inv.created * 1000).toLocaleDateString()} · {inv.status ?? 'unknown'}
                  </div>
                </div>
                <div className="flex items-center gap-3">
                  <span className="mono text-[--color-fg]">
                    {formatMoney(inv.amountPaid, inv.currency)}
                  </span>
                  {inv.pdfUrl && (
                    <a
                      href={inv.pdfUrl}
                      target="_blank"
                      rel="noreferrer"
                      className="text-[11.5px] text-[--color-accent] hover:underline"
                    >
                      PDF
                    </a>
                  )}
                  {inv.hostedUrl && (
                    <a
                      href={inv.hostedUrl}
                      target="_blank"
                      rel="noreferrer"
                      className="text-[11.5px] text-[--color-fg-muted] hover:text-[--color-fg]"
                    >
                      View →
                    </a>
                  )}
                </div>
              </div>
            ))}
          </div>
        </>
      )}

      <p className="mt-10 text-[12px] text-[--color-fg-subtle]">
        Payment-method updates and other rare actions:{' '}
        <button
          type="button"
          onClick={async () => {
            try {
              const r = await apiFetch<{ url: string }>('/v1/billing/portal', {
                method: 'POST',
                body: '{}',
              });
              window.location.href = r.url;
            } catch {
              setError('Portal could not open');
            }
          }}
          className="text-[--color-accent] hover:underline"
        >
          open Stripe billing portal
        </button>{' '}
        ·{' '}
        <Link href="/pricing" className="hover:text-[--color-fg]">
          compare plans
        </Link>
      </p>
    </div>
  );
}

function CheckoutModal({
  clientSecret,
  onClose,
}: {
  clientSecret: string;
  onClose: () => void;
}) {
  return (
    <div
      className="fixed inset-0 z-50 flex items-start justify-center overflow-y-auto bg-black/60 p-4 backdrop-blur-sm sm:p-8"
      role="dialog"
      aria-modal="true"
    >
      <div className="relative my-auto w-full max-w-xl rounded-lg border border-[--color-border] bg-[--color-bg] p-1 shadow-xl">
        <button
          type="button"
          onClick={onClose}
          aria-label="Close checkout"
          className="absolute right-2 top-2 z-10 rounded-md p-1.5 text-[--color-fg-muted] hover:bg-[--color-bg-subtle] hover:text-[--color-fg]"
        >
          <X size={18} />
        </button>
        {stripePromise ? (
          <EmbeddedCheckoutProvider stripe={stripePromise} options={{ clientSecret }}>
            <EmbeddedCheckout />
          </EmbeddedCheckoutProvider>
        ) : (
          <div className="p-6">
            <Alert tone="error">
              Payments aren’t configured (missing Stripe publishable key). Please contact support.
            </Alert>
          </div>
        )}
      </div>
    </div>
  );
}

function Alert({
  tone,
  children,
}: {
  tone: 'muted' | 'success' | 'warn' | 'error';
  children: React.ReactNode;
}) {
  const cls =
    tone === 'success'
      ? 'border-emerald-500/30 bg-emerald-500/10'
      : tone === 'warn'
        ? 'border-amber-500/40 bg-amber-500/10'
        : tone === 'error'
          ? 'border-[--color-danger]/40 bg-[--color-danger]/10'
          : 'border-[--color-border] bg-[--color-bg-subtle]';
  return (
    <div className={`mt-4 rounded-md border px-3.5 py-2.5 text-[12.5px] ${cls}`}>
      {children}
    </div>
  );
}

function TierCard({
  name,
  monthly,
  yearly,
  features,
  busy,
  onSubscribe,
  monthlyTier,
  yearlyTier,
  highlight,
}: {
  name: string;
  monthly: number;
  yearly: number;
  features: string[];
  busy: string | null;
  onSubscribe: (tier: Tier) => void;
  monthlyTier: Tier;
  yearlyTier: Tier;
  highlight?: boolean;
}) {
  return (
    <div
      className={`panel flex h-full flex-col p-4 ${highlight ? 'border-[--color-accent]/40' : ''}`}
    >
      <div className="text-[11px] uppercase tracking-wider text-[--color-fg-subtle]">{name}</div>
      <div className="mt-1 flex items-baseline gap-1">
        <span className="text-[24px] font-semibold tracking-tight">€{monthly}</span>
        <span className="text-[12px] text-[--color-fg-subtle]">/ month</span>
      </div>
      <ul className="mt-3 space-y-1 text-[12.5px] text-[--color-fg-muted]">
        {features.map((f) => (
          <li key={f}>— {f}</li>
        ))}
      </ul>
      <div className="mt-4 flex flex-col gap-2">
        <Button
          variant={highlight ? 'primary' : 'secondary'}
          size="md"
          onClick={() => onSubscribe(monthlyTier)}
          disabled={Boolean(busy)}
        >
          {busy === monthlyTier ? 'Loading…' : `Subscribe — €${monthly}/mo`}
        </Button>
        <Button
          variant="ghost"
          size="md"
          onClick={() => onSubscribe(yearlyTier)}
          disabled={Boolean(busy)}
        >
          {busy === yearlyTier ? 'Loading…' : `Or €${yearly}/year — 2 months free`}
        </Button>
      </div>
    </div>
  );
}

function PlanSwitch({
  label,
  onClick,
  busy,
}: {
  label: string;
  onClick: () => void;
  busy: boolean;
}) {
  return (
    <button
      type="button"
      onClick={onClick}
      disabled={busy}
      className="panel flex items-center justify-between p-3 text-left text-[12.5px] transition-colors hover:bg-[--color-bg-subtle] disabled:opacity-60"
    >
      <span className="text-[--color-fg]">{label}</span>
      <span className="text-[--color-fg-muted]">{busy ? '…' : '→'}</span>
    </button>
  );
}

export default function BillingPage() {
  return (
    <Suspense
      fallback={
        <div className="mx-auto max-w-3xl px-6 py-12 text-center">
          <Loader2 className="mx-auto animate-spin text-[--color-fg-muted]" size={20} />
        </div>
      }
    >
      <BillingInner />
    </Suspense>
  );
}
