/* Plan-cards renderer for the /pricing page. Moved out of `Pages/Pricing.tsx`
 * in the sections-CMS refactor (Phase 2). Owns the currency-aware data
 * lifecycle entirely: fetches `/pricing.json` on mount, picks the initial
 * currency from subscription pin → locale → default, then filters plans
 * client-side by (currency, period).
 *
 * Section payload toggles:
 *   - `showPeriodToggle: false` hides the monthly/yearly tabs but the
 *     internal period stays at the default ('monthly') so the cards still
 *     render a price.
 *   - `showCurrencyPicker: false` hides the multi-currency dropdown; the
 *     auto-detected currency is still used internally.
 *
 * `eyebrow`/`heading`/`subhead` render above the cards when admin sets them
 * (all optional — empty hides). Rails (CrossMark + border-x) are passed in
 * via `showRails` because the chrome decision (AppNav vs marketing Nav)
 * lives in the page-level dispatcher.
 */
import { Button } from '@/Components/ui';
import { cn } from '@/lib/cn';
import type { PricingGridSection as Data } from '@/marketing/cms-types';
import { SanitizedHtmlBody, SanitizedHtmlInline } from '@/marketing/sanitized-html';
import { CrossMark, Reveal } from '@/marketing/shared';
import CurrencyPicker from '@/pricing/CurrencyPicker';
import GatewayPickerModal from '@/pricing/GatewayPickerModal';
import PlanCard from '@/pricing/PlanCard';
import { startChangePlan, startCheckout } from '@/pricing/checkout';
import type {
    CurrencyRow,
    Period,
    PlanRow,
    PriceRow,
    PricingPayload,
    ProviderRow,
} from '@/pricing/types';
import type { AuthUser } from '@/types/inertia';
import { usePage } from '@inertiajs/react';
import { AlertCircle, Loader2 } from 'lucide-react';
import { useEffect, useMemo, useState, type ReactNode } from 'react';

// Sentinel currency for the empty-state render path — used when no providers/
// prices are configured so the Free card still renders. PlanCard requires a
// CurrencyRow but only reads `code`/`symbol` for paid plans; Free's CTA is
// currency-agnostic and ignores this value.
const EMPTY_CURRENCY: CurrencyRow = {
    id: -1,
    code: '',
    symbol: '',
    name: '',
    is_default: false,
};

// Country (ISO 3166) → currency (ISO 4217). Only countries with a natively-
// supported provider in the admin matrix are listed; everything else falls
// through to the deployment default. Mirrors the same table on the server
// (PricingController + StripeProvider currency detection).
const COUNTRY_CURRENCY: Record<string, string> = {
    IN: 'INR',
    GB: 'GBP', JE: 'GBP', GG: 'GBP', IM: 'GBP',
    US: 'USD',
    AU: 'AUD',
    AD: 'EUR', AT: 'EUR', BE: 'EUR', CY: 'EUR', DE: 'EUR', EE: 'EUR',
    ES: 'EUR', FI: 'EUR', FR: 'EUR', GR: 'EUR', HR: 'EUR', IE: 'EUR',
    IT: 'EUR', LT: 'EUR', LU: 'EUR', LV: 'EUR', MC: 'EUR', ME: 'EUR',
    MT: 'EUR', NL: 'EUR', PT: 'EUR', SI: 'EUR', SK: 'EUR', SM: 'EUR',
    VA: 'EUR', XK: 'EUR',
};

function detectPreferredCurrency(): string | null {
    if (typeof navigator === 'undefined') return null;
    const tags = [navigator.language, ...(navigator.languages ?? [])].filter(
        (t): t is string => typeof t === 'string' && t !== '',
    );
    for (const tag of tags) {
        try {
            const region = (new Intl.Locale(tag).region ?? '').toUpperCase();
            if (region && COUNTRY_CURRENCY[region]) {
                return COUNTRY_CURRENCY[region];
            }
        } catch {
            // Old browser; ignore.
        }
    }
    return null;
}

/**
 * Pick the initial currency on first hydration:
 *   1. Subscribers are pinned to their existing currency_id (picker disables).
 *      The `?switch_provider=1` URL flag suppresses currentSubscription
 *      server-side, which routes us back to the fresh-signup branch.
 *   2. Locale region → currency table → match a row in `currencies`.
 *   3. Deployment default (admin's site.default_currency Setting).
 *   4. First currency in the list (last-resort fallback when the default
 *      doesn't appear in `currencies` — e.g. only INR plans are seeded but
 *      default is USD).
 */
function pickInitialCurrencyId(payload: PricingPayload): number | null {
    if (payload.currencies.length === 0) return null;

    const sub = payload.currentSubscription;
    if (sub?.currency_id) {
        const pinned = payload.currencies.find((c) => c.id === sub.currency_id);
        if (pinned) return pinned.id;
    }

    const localeCode = detectPreferredCurrency();
    if (localeCode) {
        const match = payload.currencies.find((c) => c.code === localeCode);
        if (match) return match.id;
    }

    const def = payload.currencies.find((c) => c.code === payload.defaultCurrency);
    if (def) return def.id;

    return payload.currencies[0]?.id ?? null;
}

export function PricingGridSectionRenderer({
    section,
    user,
    showRails = true,
}: {
    section: Data;
    user: AuthUser | null;
    showRails?: boolean;
}) {
    const page = usePage();

    // `?switch_provider=1` is server-side handled (suppresses
    // currentSubscription in the JSON payload). We only need it client-side
    // to surface the explanatory banner. Inertia's page.url is the
    // request-relative path including query string.
    const switchProviderMode = useMemo(() => {
        const haystack =
            typeof window !== 'undefined' && window.location?.search
                ? window.location.search
                : (page.url ?? '');
        return haystack.includes('switch_provider=1');
    }, [page.url]);

    const [payload, setPayload] = useState<PricingPayload | null>(null);
    const [loadError, setLoadError] = useState<string | null>(null);
    const [period, setPeriod] = useState<Period>('monthly');
    const [currencyId, setCurrencyId] = useState<number | null>(null);
    const [modalPlanId, setModalPlanId] = useState<number | null>(null);
    // Fast-path checkout state — when there's only 1 gateway active for
    // a (plan, currency, period), we skip the modal and POST /checkout
    // directly. The pending plan id pins the card's CTA to a spinner so
    // the user has visual feedback during the redirect.
    const [fastPathPlanId, setFastPathPlanId] = useState<number | null>(null);
    const [fastPathError, setFastPathError] = useState<string | null>(null);

    useEffect(() => {
        let cancelled = false;
        (async () => {
            try {
                const res = await fetch('/pricing.json', {
                    credentials: 'same-origin',
                    headers: { Accept: 'application/json' },
                });
                if (!res.ok) {
                    throw new Error(`HTTP ${res.status}`);
                }
                const body = (await res.json()) as PricingPayload;
                if (cancelled) return;
                setPayload(body);
                setCurrencyId(pickInitialCurrencyId(body));
                if (body.currentSubscription?.period) {
                    setPeriod(body.currentSubscription.period);
                }
            } catch (e) {
                if (cancelled) return;
                setLoadError(
                    e instanceof Error ? e.message : 'Failed to load pricing.',
                );
            }
        })();
        return () => {
            cancelled = true;
        };
        // Mount-only fetch; setters are stable and we don't refetch on
        // currency/period change (filtering is client-side).
        // eslint-disable-next-line react-hooks/exhaustive-deps
    }, []);

    const selectedCurrency: CurrencyRow | null = useMemo(() => {
        if (!payload || currencyId === null) return null;
        return payload.currencies.find((c) => c.id === currencyId) ?? null;
    }, [payload, currencyId]);

    // Plans visible at the current (currency, period). Free is always shown
    // since it's currency-agnostic — Stripe + Razorpay don't ever charge it.
    // Even when currencyId is null (zero providers / zero active prices, the
    // empty-state shape) we still surface Free so the page never renders
    // blank — the dropdown will be empty but Free's CTA is always actionable.
    const visiblePlans: PlanRow[] = useMemo(() => {
        if (!payload) return [];
        if (currencyId === null) {
            // No active prices (empty state) — only render Free so the
            // page isn't blank, but paid cards don't show without prices.
            return payload.plans.filter((p) => p.key === 'free');
        }
        const subscribableIds = new Set(
            payload.prices
                .filter(
                    (p) =>
                        p.is_active &&
                        p.currency_id === currencyId &&
                        p.period === period,
                )
                .map((p) => p.plan_id),
        );
        return payload.plans.filter(
            (p) => p.key === 'free' || subscribableIds.has(p.id),
        );
    }, [payload, currencyId, period]);

    const priceFor = (
        prices: PriceRow[],
        planId: number,
        currId: number,
        per: Period,
    ): PriceRow | null =>
        prices.find(
            (p) =>
                p.plan_id === planId &&
                p.currency_id === currId &&
                p.period === per &&
                p.is_active,
        ) ?? null;

    const gatewaysForPlan = (
        planId: number,
        currId: number,
        per: Period,
    ): ProviderRow[] => {
        if (!payload) return [];
        const matching = payload.prices.filter(
            (p) =>
                p.plan_id === planId &&
                p.currency_id === currId &&
                p.period === per &&
                p.is_active,
        );
        const keys = new Set(matching.map((p) => p.gateway_key));
        return payload.activeProviders.filter((g) => keys.has(g.key));
    };

    const modalPlan = modalPlanId !== null
        ? visiblePlans.find((p) => p.id === modalPlanId) ?? null
        : null;
    const modalGateways =
        modalPlan && payload && currencyId !== null
            ? gatewaysForPlan(modalPlan.id, currencyId, period)
            : [];
    const modalAmount =
        modalPlan && payload && currencyId !== null
            ? priceFor(payload.prices, modalPlan.id, currencyId, period)
            : null;

    const subscriberLocked =
        Boolean(payload?.currentSubscription) && !switchProviderMode;

    const hasHeaderCopy = Boolean(section.eyebrow || section.heading || section.subhead);

    return (
        <>
            <section className="ms-section-pricing border-t border-border/60">
                <div
                    className={cn(
                        'relative mx-auto max-w-6xl px-6 pb-20 pt-8 lg:px-8 lg:pb-24 lg:pt-10',
                        showRails && 'border-x border-border/60',
                    )}
                >
                    {showRails && <CrossMark className="left-0 top-0" />}
                    {showRails && <CrossMark className="left-full top-0" />}

                    {hasHeaderCopy && (
                        <Reveal>
                            <div className="ms-head mb-10 text-center">
                                {section.eyebrow && (
                                    <div className="ms-badge inline-flex items-center rounded-full bg-accent px-4 py-1.5 text-xs font-semibold uppercase tracking-wider text-accent-foreground">
                                        <SanitizedHtmlInline html={section.eyebrow} />
                                    </div>
                                )}
                                {section.heading && (
                                    <h2 className="mt-6 font-display text-4xl font-medium leading-[1.1] tracking-tight text-foreground sm:text-5xl [&_em]:font-display [&_em]:italic [&_em]:text-accent">
                                        <SanitizedHtmlInline html={section.heading} />
                                    </h2>
                                )}
                                {section.subhead && (
                                    <SanitizedHtmlBody
                                        html={section.subhead}
                                        className="mx-auto mt-5 max-w-2xl text-base"
                                    />
                                )}
                            </div>
                        </Reveal>
                    )}

                    {switchProviderMode && (
                        <div className="mb-8">
                            <SwitchProviderBanner />
                        </div>
                    )}

                    {loadError && (
                        <div className="mb-8">
                            <ErrorBanner message={loadError} />
                        </div>
                    )}

                    {!payload && !loadError && <LoadingBlock />}

                    {payload && !selectedCurrency && (
                        <div
                            className="grid gap-6 sm:grid-cols-2 lg:grid-cols-3"
                            data-testid="pricing-empty-state"
                        >
                            {visiblePlans.map((plan) => (
                                <PlanCard
                                    key={plan.key}
                                    plan={plan}
                                    amountMinor={null}
                                    monthlyAmountMinor={null}
                                    yearlyAmountMinor={null}
                                    currency={EMPTY_CURRENCY}
                                    period={period}
                                    isAuthed={Boolean(user)}
                                    currentSubscription={payload.currentSubscription}
                                    hasGateways={false}
                                    onChoose={() => undefined}
                                />
                            ))}
                        </div>
                    )}

                    {payload && selectedCurrency && (
                        <>
                            {(section.showPeriodToggle ||
                                (section.showCurrencyPicker &&
                                    payload.currencies.length > 1)) && (
                                <div className="flex flex-col items-center gap-4 sm:flex-row sm:justify-center sm:gap-6">
                                    {section.showPeriodToggle && (
                                        <PeriodToggle
                                            period={period}
                                            onChange={setPeriod}
                                        />
                                    )}
                                    {/* Currency picker only renders when admin
                                      * has enabled it AND there's a real choice.
                                      * A single-currency dropdown is just
                                      * visual noise — the user has nothing
                                      * to pick. */}
                                    {section.showCurrencyPicker &&
                                        payload.currencies.length > 1 && (
                                            <CurrencyPicker
                                                currencies={payload.currencies}
                                                value={selectedCurrency.id}
                                                onChange={setCurrencyId}
                                                disabled={subscriberLocked}
                                                lockReason={
                                                    subscriberLocked
                                                        ? 'Locked to your subscription currency. Use "Switch payment method" to change.'
                                                        : undefined
                                                }
                                            />
                                        )}
                                </div>
                            )}

                            <Reveal delay={0.05}>
                                <div className="mt-12 grid gap-6 sm:grid-cols-2 lg:grid-cols-3">
                                    {visiblePlans.map((plan) => {
                                        const monthlyRow = priceFor(
                                            payload.prices,
                                            plan.id,
                                            selectedCurrency.id,
                                            'monthly',
                                        );
                                        const yearlyRow = priceFor(
                                            payload.prices,
                                            plan.id,
                                            selectedCurrency.id,
                                            'yearly',
                                        );
                                        const activeRow =
                                            period === 'yearly'
                                                ? yearlyRow
                                                : monthlyRow;
                                        const gateways = gatewaysForPlan(
                                            plan.id,
                                            selectedCurrency.id,
                                            period,
                                        );
                                        return (
                                            <PlanCard
                                                key={plan.key}
                                                plan={plan}
                                                amountMinor={
                                                    activeRow?.amount_minor ?? null
                                                }
                                                monthlyAmountMinor={
                                                    monthlyRow?.amount_minor ?? null
                                                }
                                                yearlyAmountMinor={
                                                    yearlyRow?.amount_minor ?? null
                                                }
                                                currency={selectedCurrency}
                                                period={period}
                                                isAuthed={Boolean(user)}
                                                currentSubscription={
                                                    payload.currentSubscription
                                                }
                                                hasGateways={gateways.length > 0}
                                                onChoose={async () => {
                                                    setFastPathError(null);

                                                    // Upgrade / downgrade
                                                    // path: existing
                                                    // subscriber on the same
                                                    // provider → route through
                                                    // change-plan (revise
                                                    // in-place) instead of
                                                    // /checkout. /checkout
                                                    // refuses to create a
                                                    // parallel sub when the
                                                    // user is already
                                                    // subscribed (see
                                                    // CheckoutController guard
                                                    // tiered by intent).
                                                    const existingSub =
                                                        payload.currentSubscription;
                                                    const sameProviderUpgrade =
                                                        !switchProviderMode &&
                                                        existingSub !== null &&
                                                        // Plan/period the user
                                                        // picked actually
                                                        // differs from current
                                                        (existingSub.plan_key !==
                                                            plan.key ||
                                                            existingSub.period !==
                                                                period) &&
                                                        // Provider is the SAME
                                                        // — cross-provider goes
                                                        // through /checkout with
                                                        // switch_provider flag
                                                        // (handled below).
                                                        gateways.some(
                                                            (g) =>
                                                                g.key ===
                                                                existingSub.provider_key,
                                                        );

                                                    if (sameProviderUpgrade) {
                                                        setFastPathPlanId(plan.id);
                                                        const result =
                                                            await startChangePlan({
                                                                planKey: plan.key,
                                                                period,
                                                                currencyId:
                                                                    selectedCurrency.id,
                                                            });
                                                        if (!result.ok) {
                                                            setFastPathError(
                                                                result.message,
                                                            );
                                                            setFastPathPlanId(null);
                                                            return;
                                                        }
                                                        if (result.redirectUrl) {
                                                            window.location.href =
                                                                result.redirectUrl;
                                                        } else {
                                                            // Applied cleanly —
                                                            // bounce to
                                                            // /account/subscription
                                                            // so user sees the
                                                            // new plan reflected.
                                                            window.location.href =
                                                                '/account/subscription';
                                                        }
                                                        return;
                                                    }

                                                    // Fresh signup OR
                                                    // cross-provider switch.
                                                    // Fast-path skips the
                                                    // gateway picker modal when
                                                    // only one gateway is
                                                    // active for this (plan,
                                                    // currency, period) — a
                                                    // "Pay with…" modal listing
                                                    // a single button is two
                                                    // clicks where one would
                                                    // do.
                                                    if (gateways.length === 1) {
                                                        setFastPathPlanId(plan.id);
                                                        const result =
                                                            await startCheckout({
                                                                planKey: plan.key,
                                                                period,
                                                                provider:
                                                                    gateways[0].key,
                                                                currencyId:
                                                                    selectedCurrency.id,
                                                                switchProvider:
                                                                    switchProviderMode,
                                                            });
                                                        if (!result.ok) {
                                                            setFastPathError(
                                                                result.message,
                                                            );
                                                            setFastPathPlanId(null);
                                                            return;
                                                        }
                                                        window.location.href =
                                                            result.checkoutUrl;
                                                        return;
                                                    }
                                                    setModalPlanId(plan.id);
                                                }}
                                                pending={
                                                    fastPathPlanId === plan.id
                                                }
                                            />
                                        );
                                    })}
                                </div>
                            </Reveal>

                            {fastPathError && (
                                <p
                                    className="mx-auto mt-6 max-w-md rounded-lg border border-destructive/30 bg-destructive/5 px-3 py-2 text-center text-[12px] font-medium text-destructive"
                                    role="alert"
                                    data-testid="pricing-fast-path-error"
                                >
                                    {fastPathError}
                                </p>
                            )}

                            {section.trustLine && (
                                <p className="mt-12 text-center text-sm text-muted-foreground">
                                    <SanitizedHtmlInline html={section.trustLine} />
                                </p>
                            )}
                        </>
                    )}
                </div>
            </section>

            {modalPlan && selectedCurrency && modalAmount && (
                <GatewayPickerModal
                    open
                    plan={modalPlan}
                    period={period}
                    currency={selectedCurrency}
                    amountMinor={modalAmount.amount_minor}
                    gateways={modalGateways}
                    onClose={() => setModalPlanId(null)}
                />
            )}
        </>
    );
}

function PeriodToggle({
    period,
    onChange,
}: {
    period: Period;
    onChange: (p: Period) => void;
}) {
    return (
        <div
            role="tablist"
            aria-label="Billing period"
            className="ms-toggle inline-flex self-center rounded-full border border-border bg-card p-1 text-sm shadow-sm sm:self-auto"
        >
            <PeriodTab active={period === 'monthly'} onClick={() => onChange('monthly')}>
                Monthly
            </PeriodTab>
            <PeriodTab active={period === 'yearly'} onClick={() => onChange('yearly')}>
                Yearly
                <span className="ml-1.5 text-xs opacity-70">−10%</span>
            </PeriodTab>
        </div>
    );
}

function PeriodTab({
    active,
    onClick,
    children,
}: {
    active: boolean;
    onClick: () => void;
    children: ReactNode;
}) {
    return (
        <button
            role="tab"
            type="button"
            aria-selected={active}
            onClick={onClick}
            className={cn(
                'inline-flex items-center rounded-full px-5 py-2 font-medium transition-colors',
                active
                    ? 'ms-toggle-active bg-foreground text-background'
                    : 'text-muted-foreground hover:text-foreground',
            )}
        >
            {children}
        </button>
    );
}

function SwitchProviderBanner() {
    return (
        <div
            role="status"
            data-testid="pricing-switch-provider-banner"
            className="flex items-start gap-2 rounded-lg border border-accent/30 bg-accent/5 px-4 py-3 text-sm text-foreground"
        >
            <AlertCircle
                className="mt-0.5 h-4 w-4 shrink-0 text-accent"
                strokeWidth={2.5}
                aria-hidden
            />
            <p>
                You're switching payment methods. Your existing subscription stays
                active until the new one activates and you cancel the old one.
            </p>
        </div>
    );
}

function ErrorBanner({ message }: { message: string }) {
    return (
        <div
            role="alert"
            data-testid="pricing-load-error"
            className="flex items-start gap-2 rounded-lg border border-destructive/30 bg-destructive/5 px-4 py-3 text-sm text-destructive"
        >
            <AlertCircle
                className="mt-0.5 h-4 w-4 shrink-0"
                strokeWidth={2.5}
                aria-hidden
            />
            <div>
                <p className="font-medium">Could not load pricing.</p>
                <p className="mt-0.5 text-xs opacity-80">{message}</p>
                <Button
                    variant="secondary"
                    size="sm"
                    className="mt-2"
                    onClick={() => window.location.reload()}
                >
                    Try again
                </Button>
            </div>
        </div>
    );
}

function LoadingBlock() {
    return (
        <div
            className="flex items-center justify-center py-16"
            data-testid="pricing-loading"
        >
            <Loader2
                className="h-6 w-6 animate-spin text-muted-foreground"
                strokeWidth={2}
                aria-hidden
            />
        </div>
    );
}
