import { Modal } from '@/Components/ui';
import { cn } from '@/lib/cn';
import { ChevronRight, Loader2, X } from 'lucide-react';
import { useEffect, useRef, useState } from 'react';
import { startCheckout } from './checkout';
import { GATEWAY_ICONS } from './gateway-icons';
import type { CurrencyRow, Period, PlanRow, ProviderRow } from './types';

type Props = {
    open: boolean;
    plan: PlanRow;
    period: Period;
    currency: CurrencyRow;
    amountMinor: number;
    /** Already filtered to gateways with an active price for (plan, currency, period). */
    gateways: ProviderRow[];
    onClose: () => void;
};

function formatAmount(amountMinor: number, currency: CurrencyRow): string {
    const major = amountMinor / 100;
    const isWhole = major % 1 === 0;
    const locale = currency.code === 'INR' ? 'en-IN' : 'en-US';
    return `${currency.symbol}${major.toLocaleString(locale, {
        minimumFractionDigits: isWhole ? 0 : 2,
        maximumFractionDigits: 2,
    })}`;
}

/**
 * Gateway picker modal — opens after the user clicks "Choose plan" on a
 * card. One button per gateway with an active price for this (plan,
 * currency, period). Click → POST /checkout → full-page redirect to the
 * gateway's hosted checkout (Stripe session URL or Razorpay /checkout/razorpay/{id}
 * shim that pops the inline modal).
 *
 * Even when only a single gateway is available we still render the modal
 * so the user knows which provider's terms they're about to agree to —
 * skipping the modal felt like a black-box redirect in Appshot testing.
 */
export default function GatewayPickerModal({
    open,
    plan,
    period,
    currency,
    amountMinor,
    gateways,
    onClose,
}: Props) {
    const [busyKey, setBusyKey] = useState<string | null>(null);
    const [error, setError] = useState<string | null>(null);
    const closeBtnRef = useRef<HTMLButtonElement | null>(null);
    // Flips true right before `window.location.href = checkout_url` so the
    // history-cleanup effect skips its history.back() — we don't want to
    // race the gateway redirect with a same-tick history pop.
    const navigatingAwayRef = useRef(false);

    // Reset transient state on (re)open and focus the close button so Esc/
    // tab-to-action works without a confusing initial focus on a checkout
    // button (which would Enter-trigger a payment redirect). Esc handling
    // + body-overflow lock are owned by <Modal>; the busy-state gate stays
    // local via `closeOnEsc` toggling.
    useEffect(() => {
        if (!open) return;
        setBusyKey(null);
        setError(null);
        closeBtnRef.current?.focus();
    }, [open]);

    // Tie modal lifecycle to the browser back button so back closes the
    // modal instead of leaving /pricing entirely. Implementation notes:
    //
    // The previous version pushed a sentinel state on mount + called
    // `history.back()` on cleanup. Under React 19 Strict Mode (dev),
    // useEffect runs mount → cleanup → mount, which fired history.back()
    // between mounts, queued a popstate event, and the second mount's
    // listener caught it → called onClose immediately → modal closed
    // the moment it opened. Symptom: "section hides and shows" without
    // ever rendering the modal.
    //
    // Fix: don't auto-pop on cleanup. Push the sentinel only if not
    // already present (idempotent under Strict Mode double-mount); on
    // popstate (real back press) just call onClose. Programmatic close
    // (Cancel / Esc / backdrop) leaves the sentinel in history, which
    // a subsequent forward button could navigate back into — minor
    // UX edge, acceptable trade vs the strict-mode race.
    useEffect(() => {
        if (!open) return;

        const state = window.history.state as { modal?: unknown } | null;
        const alreadyAtModalState =
            typeof state === 'object'
            && state !== null
            && state.modal === 'gateway-picker';

        // Skip the push if we're already at the modal state — happens
        // in Strict Mode's double-mount (second mount sees the state
        // left over from the first push).
        if (!alreadyAtModalState) {
            window.history.pushState({ modal: 'gateway-picker' }, '');
        }

        const onPop = () => {
            onClose();
        };
        window.addEventListener('popstate', onPop);
        return () => {
            window.removeEventListener('popstate', onPop);
            // Intentionally no history.back() here — see comment block.
        };
        // onClose intentionally omitted from deps — it's a parent setter
        // and including it would re-fire the effect and push duplicate
        // history entries every render.
        // eslint-disable-next-line react-hooks/exhaustive-deps
    }, [open]);

    const onPick = async (gatewayKey: string) => {
        setBusyKey(gatewayKey);
        setError(null);
        const result = await startCheckout({
            planKey: plan.key,
            period,
            provider: gatewayKey,
            currencyId: currency.id,
        });
        if (!result.ok) {
            setError(result.message);
            setBusyKey(null);
            return;
        }
        navigatingAwayRef.current = true;
        window.location.href = result.checkoutUrl;
    };

    return (
        <Modal
            open={open}
            onClose={onClose}
            closeOnBackdrop={false}
            closeOnEsc={busyKey === null}
            size="md"
            data-testid="pricing-gateway-modal"
            aria-labelledby="gateway-picker-title"
        >
            <Modal.Header
                title="Choose payment method"
                titleId="gateway-picker-title"
                subtitle={
                    <>
                        {plan.name} · {formatAmount(amountMinor, currency)}/
                        {period === 'yearly' ? 'year' : 'month'}
                    </>
                }
                actions={
                    <button
                        ref={closeBtnRef}
                        type="button"
                        onClick={onClose}
                        disabled={busyKey !== null}
                        aria-label="Close"
                        className="inline-flex h-8 w-8 shrink-0 items-center justify-center rounded-md text-muted-foreground transition-colors hover:bg-muted hover:text-foreground disabled:opacity-50"
                    >
                        <X className="h-4 w-4" strokeWidth={2.5} aria-hidden />
                    </button>
                }
            />
            <Modal.Body>
                {gateways.length === 0 ? (
                    <p className="text-sm text-muted-foreground">
                        No payment methods available for this plan and currency.
                    </p>
                ) : (
                    <div className="divide-y divide-border overflow-hidden rounded-lg border border-border">
                        {gateways.map((g) => {
                            const busy = busyKey === g.key;
                            const otherBusy = busyKey !== null && !busy;
                            return (
                                <button
                                    key={g.key}
                                    type="button"
                                    onClick={() => onPick(g.key)}
                                    disabled={busyKey !== null}
                                    data-testid={`pricing-gateway-${g.key}`}
                                    className={cn(
                                        'flex w-full items-center justify-between gap-3 bg-card px-4 py-2.5 text-left text-sm font-medium text-foreground transition-colors',
                                        'hover:bg-muted',
                                        'focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-ring',
                                        busyKey !== null && 'cursor-not-allowed',
                                        otherBusy && 'opacity-50',
                                    )}
                                >
                                    <span className="flex items-center gap-3">
                                        <GatewayLogo gatewayKey={g.key} label={g.label} />
                                        <span>{g.label}</span>
                                    </span>
                                    {busy ? (
                                        <Loader2
                                            className="h-4 w-4 animate-spin text-muted-foreground"
                                            strokeWidth={2.5}
                                            aria-hidden
                                        />
                                    ) : (
                                        <ChevronRight
                                            className="h-4 w-4 text-muted-foreground"
                                            strokeWidth={2.5}
                                            aria-hidden
                                        />
                                    )}
                                </button>
                            );
                        })}
                    </div>
                )}

                {error && (
                    <div
                        role="alert"
                        data-testid="pricing-gateway-error"
                        className="rounded-md border border-destructive/30 bg-destructive/5 px-3 py-2 text-xs text-destructive"
                    >
                        {error}
                    </div>
                )}
            </Modal.Body>
        </Modal>
    );
}

/* Gateway badge. Brand SVGs registered in `GATEWAY_ICONS` render on a white
 * tile so each provider's native color holds up against the surrounding
 * theme. Unknown gateway keys fall back to a neutral monogram so a freshly
 * added provider that hasn't been registered yet still renders cleanly. */
const GATEWAY_FALLBACK_PALETTE: Record<string, { bg: string; text: string }> = {
    stripe: { bg: 'bg-[#635BFF]', text: 'text-white' },
    razorpay: { bg: 'bg-[#3395FF]', text: 'text-white' },
    paypal: { bg: 'bg-[#003087]', text: 'text-white' },
};

function GatewayLogo({ gatewayKey, label }: { gatewayKey: string; label: string }) {
    const Icon = GATEWAY_ICONS[gatewayKey];

    if (Icon) {
        return (
            <span className="flex h-9 w-9 shrink-0 items-center justify-center rounded-md border border-border/60 bg-white">
                <Icon className="h-5 w-5" />
            </span>
        );
    }

    const palette = GATEWAY_FALLBACK_PALETTE[gatewayKey] ?? {
        bg: 'bg-muted',
        text: 'text-muted-foreground',
    };
    return (
        <span
            className={cn(
                'flex h-9 w-9 shrink-0 items-center justify-center rounded-md font-semibold',
                palette.bg,
                palette.text,
            )}
            aria-hidden
        >
            {label.charAt(0).toUpperCase()}
        </span>
    );
}
