import { AdminPageHeader, adminLayout } from '@/Layouts/AdminLayout';
import { Button, Card, Checkbox, Input, Modal, Pill, Select } from '@/Components/ui';
import { cn } from '@/lib/cn';
import type { SharedProps } from '@/types/inertia';
import { Head, Link, router, usePage } from '@inertiajs/react';
import {
    AlertCircle,
    ChevronDown,
    ChevronRight,
    Loader2,
    Pencil,
    Plus,
    Trash2,
    X,
} from 'lucide-react';
import {
    useMemo,
    useState,
    type SubmitEvent as ReactSubmitEvent,
    type ReactNode,
} from 'react';

type PlanFeatures = {
    can_export?: boolean;
    project_limit?: number | null;
    storage_bytes_limit?: number | null;
    can_use_premium_kits?: boolean;
    can_use_ai?: boolean;
    ai_tokens_monthly?: number | null;
};

type PlanRow = {
    id: number;
    key: string;
    name: string;
    description: string | null;
    features: PlanFeatures | null;
    is_active: boolean;
    position: number;
    max_seats: number | null;
    active_subs_count: number;
};

const PLAN_TONES: Record<string, 'accent' | 'neutral'> = {
    pro: 'accent',
    scale: 'accent',
    free: 'neutral',
};

type ProviderRow = {
    key: string;
    label: string;
    is_active: boolean;
    is_registered: boolean;
    mode: 'test' | 'live';
    has_test_api_key: boolean;
    has_test_webhook_secret: boolean;
    has_live_api_key: boolean;
    has_live_webhook_secret: boolean;
    has_config: boolean;
    has_api_key: boolean;
    has_webhook_secret: boolean;
    position: number;
};

/**
 * Per-(plan, currency, gateway, period) row in the new currency-first
 * pricing model. Replaces the legacy `links` shape that bundled per-plan
 * display overrides into the provider-link rows.
 */
type PriceRow = {
    id: number;
    plan_id: number;
    currency_id: number;
    currency_code: string | null;
    gateway_key: string;
    period: 'monthly' | 'yearly';
    amount_minor: number;
    provider_price_id: string | null;
    is_active: boolean;
};

type CurrencyRow = {
    id: number;
    code: string;
    symbol: string;
    name: string;
    is_default: boolean;
};

type Props = {
    plans: PlanRow[];
    providers: ProviderRow[];
    /** Plan-key → list of price rows. */
    prices: Record<string, PriceRow[] | undefined>;
    /** Active currencies; drives the currency picker in the price modal. */
    currencies: CurrencyRow[];
};

const PERIODS: ReadonlyArray<'monthly' | 'yearly'> = ['monthly', 'yearly'];

const PRICE_ID_PLACEHOLDERS: Record<string, string> = {
    stripe: 'price_1AbCdEf...',
    razorpay: 'plan_AbCdEf...',
    paypal: 'P-1AB23456CD789012EF...',
};

// Currencies with no minor unit (1 unit = 1 minor). Add when needed.
const ZERO_DECIMAL_CURRENCIES = new Set(['JPY', 'KRW', 'VND', 'CLP', 'ISK']);

function decimalsFor(currencyCode: string): number {
    return ZERO_DECIMAL_CURRENCIES.has(currencyCode.toUpperCase()) ? 0 : 2;
}

function formatAmountMinor(minor: number, currency: CurrencyRow | null): string {
    if (!currency) return String(minor);
    const decimals = decimalsFor(currency.code);
    const major = decimals === 0 ? minor : minor / 100;
    const formatted = major.toLocaleString(undefined, {
        minimumFractionDigits: decimals === 0 ? 0 : major % 1 === 0 ? 0 : 2,
        maximumFractionDigits: decimals,
    });
    return `${currency.symbol}${formatted}`;
}

function formatBytes(bytes: number | null | undefined): string {
    if (bytes === null || bytes === undefined) return 'Unlimited';
    const gb = bytes / (1024 ** 3);
    if (gb >= 1) {
        return Number.isInteger(gb) ? `${gb} GB` : `${gb.toFixed(1)} GB`;
    }
    const mb = bytes / (1024 ** 2);
    if (mb >= 1) {
        return Number.isInteger(mb) ? `${mb} MB` : `${mb.toFixed(1)} MB`;
    }
    const kb = bytes / 1024;
    return `${Math.max(0, Math.round(kb))} KB`;
}

function csrfHeaders(): Record<string, string> {
    const xsrf = document
        .querySelector('meta[name="csrf-token"]')
        ?.getAttribute('content');
    const cookieMatch = document.cookie.match(/(?:^|;\s*)XSRF-TOKEN=([^;]+)/);
    const cookieToken = cookieMatch ? decodeURIComponent(cookieMatch[1]) : null;
    const headers: Record<string, string> = {
        'Content-Type': 'application/json',
        Accept: 'application/json',
        'X-Requested-With': 'XMLHttpRequest',
    };
    if (xsrf) headers['X-CSRF-TOKEN'] = xsrf;
    if (cookieToken) headers['X-XSRF-TOKEN'] = cookieToken;
    return headers;
}

function AdminBilling({
    plans,
    providers,
    prices,
    currencies,
}: Props) {
    const { auth } = usePage<SharedProps>().props;

    if (!auth.user?.is_admin) {
        return null;
    }

    return (
        <>
            <Head title="Billing administration" />
            <AdminPageHeader title="Plans" description="Manage plans and wire provider price IDs." />
            <GatewaysRelocatedNotice />
            <PlansSection
                plans={plans}
                providers={providers}
                prices={prices}
                currencies={currencies}
            />
        </>
    );
}

AdminBilling.layout = adminLayout;

export default AdminBilling;

function GatewaysRelocatedNotice() {
    const { billingProviders } = usePage<SharedProps>().props;
    return (
        <Card
            padding="md"
            className="mt-2 border-accent/20 bg-accent/[0.03]"
            data-testid="admin-billing-gateways-notice"
        >
            <div className="flex items-start justify-between gap-4">
                <div>
                    <h3 className="text-[13px] font-semibold tracking-tight text-foreground">
                        Looking for gateway credentials?
                    </h3>
                    <p className="mt-1 text-[12px] text-muted-foreground">
                        {billingProviders.labels_and} API keys, webhook
                        secrets, and the test/live mode toggle live under
                        <span className="px-1 font-mono text-[11px] text-foreground">
                            Settings → Payment Gateways
                        </span>
                        now.
                    </p>
                </div>
                <Link
                    href="/admin/settings?tab=gateways"
                    className="inline-flex shrink-0 items-center gap-1.5 rounded-md border border-accent/40 bg-card px-3 py-1.5 text-[11px] font-medium text-accent transition-colors hover:bg-accent/10"
                    data-testid="admin-billing-gateways-link"
                >
                    Open gateways
                </Link>
            </div>
        </Card>
    );
}

function PlansSection({
    plans,
    providers,
    prices,
    currencies,
}: {
    plans: PlanRow[];
    providers: ProviderRow[];
    prices: Record<string, PriceRow[] | undefined>;
    currencies: CurrencyRow[];
}) {
    const [editingKey, setEditingKey] = useState<string | null>(null);
    const [creating, setCreating] = useState(false);
    const editing = plans.find((p) => p.key === editingKey) ?? null;

    // Active gateways drive which gateway columns the price grid shows.
    // Hide inactive providers so the grid stays focused on what's live;
    // admin can come back to wire prices after activating a new provider.
    const activeProviders = providers.filter(
        (p) => p.is_registered && p.is_active,
    );

    return (
        <section className="mt-6 space-y-6">
            <div className="flex items-center justify-between gap-3">
                <h2 className="text-base font-semibold tracking-tight text-foreground">
                    Plans
                </h2>
                <Button
                    type="button"
                    variant="primary"
                    size="sm"
                    onClick={() => setCreating(true)}
                    data-testid="admin-billing-new-plan"
                >
                    New plan
                </Button>
            </div>

            <PlansSummaryTable plans={plans} onEdit={setEditingKey} />

            <div className="space-y-4">
                <h2 className="text-[11px] font-medium text-muted-foreground">
                    Provider prices
                </h2>
                {activeProviders.length === 0 && (
                    <Card padding="md" data-testid="admin-billing-no-providers">
                        <p className="text-[12px] text-muted-foreground">
                            No active payment gateways. Configure Stripe or
                            Razorpay under{' '}
                            <Link
                                href="/admin/settings?tab=gateways"
                                className="font-medium text-accent hover:underline"
                            >
                                Settings → Payment Gateways
                            </Link>{' '}
                            before adding plan prices.
                        </p>
                    </Card>
                )}
                {activeProviders.length > 0 &&
                    plans
                        .filter((p) => p.key !== 'free')
                        .map((plan) => (
                            <PlanPricesCard
                                key={plan.key}
                                plan={plan}
                                providers={activeProviders}
                                currencies={currencies}
                                prices={prices[plan.key] ?? []}
                            />
                        ))}
            </div>

            {editing && (
                <EditPlanModal
                    plan={editing}
                    onClose={() => setEditingKey(null)}
                />
            )}

            {creating && (
                <CreatePlanModal
                    onClose={() => setCreating(false)}
                />
            )}
        </section>
    );
}

function PlansSummaryTable({
    plans,
    onEdit,
}: {
    plans: PlanRow[];
    onEdit: (key: string) => void;
}) {
    return (
        <Card padding="none" className="overflow-hidden">
            <table className="w-full text-sm">
                <thead className="border-b border-border bg-muted/30">
                    <tr className="text-left text-[11px] font-medium text-muted-foreground">
                        <th className="px-4 py-2.5">Plan</th>
                        <th className="px-4 py-2.5">Export</th>
                        <th className="px-4 py-2.5">Premium kits</th>
                        <th className="px-4 py-2.5">Project limit</th>
                        <th className="px-4 py-2.5">Storage</th>
                        <th className="px-4 py-2.5 text-right">Active subs</th>
                        <th className="px-4 py-2.5">Status</th>
                        <th className="px-4 py-2.5 text-right">&nbsp;</th>
                    </tr>
                </thead>
                <tbody>
                    {plans.map((plan) => {
                        const features = plan.features ?? {};
                        const limit =
                            features.project_limit === null ||
                            features.project_limit === undefined
                                ? 'Unlimited'
                                : String(features.project_limit);
                        return (
                            <tr
                                key={plan.id}
                                className="border-b border-border/60 last:border-0"
                                data-testid={`admin-plan-row-${plan.key}`}
                            >
                                <td className="px-4 py-3">
                                    <div className="flex items-center gap-2">
                                        <Pill
                                            tone={PLAN_TONES[plan.key] ?? 'neutral'}
                                            size="sm"
                                        >
                                            {plan.name}
                                        </Pill>
                                        <span className="font-mono text-[10px] text-muted-foreground">
                                            {plan.key}
                                        </span>
                                    </div>
                                </td>
                                <td className="px-4 py-3 text-foreground/80">
                                    {features.can_export ? 'Yes' : 'No'}
                                </td>
                                <td className="px-4 py-3 text-foreground/80">
                                    {features.can_use_premium_kits ? 'Yes' : 'No'}
                                </td>
                                <td className="px-4 py-3 text-foreground/80">
                                    {limit}
                                </td>
                                <td
                                    className="px-4 py-3 tabular-nums text-foreground/80"
                                    data-testid={`admin-plan-row-${plan.key}-storage`}
                                >
                                    {formatBytes(features.storage_bytes_limit)}
                                </td>
                                <td className="px-4 py-3 text-right tabular-nums">
                                    {plan.active_subs_count > 0 ? (
                                        <Link
                                            href={`/admin/subscriptions?plan_key=${plan.key}&status=all`}
                                            className="text-foreground hover:text-accent"
                                            data-testid={`admin-plan-row-${plan.key}-subs`}
                                        >
                                            {plan.active_subs_count}
                                        </Link>
                                    ) : (
                                        <span className="text-muted-foreground">
                                            0
                                        </span>
                                    )}
                                </td>
                                <td className="px-4 py-3">
                                    <span
                                        className={cn(
                                            'rounded-full px-2 py-0.5 text-[10px] font-medium',
                                            plan.is_active
                                                ? 'border border-success/30 bg-success/10 text-success'
                                                : 'border border-border bg-muted text-muted-foreground',
                                        )}
                                    >
                                        {plan.is_active ? 'Active' : 'Inactive'}
                                    </span>
                                </td>
                                <td className="px-4 py-3 text-right">
                                    <Button
                                        variant="secondary"
                                        size="sm"
                                        onClick={() => onEdit(plan.key)}
                                        data-testid={`admin-plan-row-${plan.key}-edit`}
                                    >
                                        <Pencil
                                            className="h-3.5 w-3.5"
                                            strokeWidth={2}
                                        />
                                        Edit
                                    </Button>
                                </td>
                            </tr>
                        );
                    })}
                </tbody>
            </table>
        </Card>
    );
}

/* ------------------------------------------------------------------ *
 * Per-plan price grid (currency × gateway × period)
 *
 * Each cell either shows a configured price (amount + truncated price-id
 * + status pill + edit/delete) or an "Add" button that opens the modal
 * pre-filled with that cell's (currency, gateway, period). Inactive
 * cells render dimmed so admin can see the gap at a glance.
 * ------------------------------------------------------------------ */

type PriceModalIntent =
    | { mode: 'create'; currencyId: number; gatewayKey: string; period: 'monthly' | 'yearly' }
    | { mode: 'edit'; price: PriceRow };

function PlanPricesCard({
    plan,
    providers,
    currencies,
    prices,
}: {
    plan: PlanRow;
    providers: ProviderRow[];
    currencies: CurrencyRow[];
    prices: PriceRow[];
}) {
    const configuredCount = prices.filter(
        (p) => p.is_active && p.provider_price_id,
    ).length;

    const [open, setOpen] = useState(configuredCount === 0);
    const [intent, setIntent] = useState<PriceModalIntent | null>(null);
    const [deletingPrice, setDeletingPrice] = useState<PriceRow | null>(null);

    const currencyById = useMemo(() => {
        const map = new Map<number, CurrencyRow>();
        for (const c of currencies) map.set(c.id, c);
        return map;
    }, [currencies]);

    // Index existing prices by (currency_id, gateway_key, period) so the
    // grid lookup is O(1) per cell. Multiple matches per tuple should
    // never happen — DB has a unique index — but defensively pick the
    // first if it does.
    const priceByCell = useMemo(() => {
        const map = new Map<string, PriceRow>();
        for (const p of prices) {
            map.set(`${p.currency_id}:${p.gateway_key}:${p.period}`, p);
        }
        return map;
    }, [prices]);

    // Currencies that have at least one configured price for this plan,
    // unioned with the deployment's full currency list, so admin can add
    // prices for currencies that don't have any rows yet without leaving
    // this card. Sorts default currency first.
    const currencyRows = useMemo(() => {
        const sorted = [...currencies].sort((a, b) => {
            if (a.is_default && !b.is_default) return -1;
            if (!a.is_default && b.is_default) return 1;
            return a.code.localeCompare(b.code);
        });
        return sorted;
    }, [currencies]);

    const totalSlots = currencyRows.length * providers.length * PERIODS.length;

    return (
        <Card padding="lg" data-testid={`admin-billing-plan-${plan.key}-card`}>
            <button
                type="button"
                onClick={() => setOpen((v) => !v)}
                aria-expanded={open}
                data-testid={`admin-billing-plan-${plan.key}-toggle`}
                className="-m-1 flex w-full items-baseline justify-between gap-4 rounded p-1 text-left transition-colors hover:bg-muted/30"
            >
                <div className="flex items-baseline gap-3">
                    {open ? (
                        <ChevronDown
                            className="h-3.5 w-3.5 self-center text-muted-foreground"
                            strokeWidth={2}
                        />
                    ) : (
                        <ChevronRight
                            className="h-3.5 w-3.5 self-center text-muted-foreground"
                            strokeWidth={2}
                        />
                    )}
                    <div>
                        <h3 className="text-base font-semibold tracking-tight text-foreground">
                            {plan.name}
                        </h3>
                        <p className="mt-0.5 font-mono text-[11px] text-muted-foreground">
                            {plan.key}
                        </p>
                    </div>
                </div>
                <div className="flex items-center gap-2">
                    {/*
                      Hidden-from-/pricing pill: paid plans without a single
                      active price-id mapping under any active provider get
                      filtered out of /pricing — surface that here so admin
                      sees the consequence at a glance.
                    */}
                    {configuredCount === 0 && (
                        <span
                            className="rounded-full border border-warning/30 bg-warning/10 px-2 py-0.5 text-[10px] font-medium text-warning"
                            data-testid={`admin-billing-plan-${plan.key}-hidden`}
                            title="Hidden from /pricing — add at least one active price with a provider price id."
                        >
                            Hidden from /pricing
                        </span>
                    )}
                    <span
                        className={cn(
                            'text-[11px] font-medium',
                            configuredCount === 0
                                ? 'text-muted-foreground'
                                : configuredCount === totalSlots
                                  ? 'text-accent'
                                  : 'text-foreground/80',
                        )}
                        data-testid={`admin-billing-plan-${plan.key}-summary`}
                    >
                        {configuredCount} active price
                        {configuredCount === 1 ? '' : 's'}
                    </span>
                </div>
            </button>

            {open && (
                <div className="mt-5">
                    <p className="mb-3 text-[11px] text-muted-foreground/80">
                        Each cell maps a currency × gateway × period to a provider
                        price id. Stripe expects{' '}
                        <span className="font-mono text-[10px]">price_…</span>{' '}
                        from Dashboard → Products → your plan; Razorpay expects{' '}
                        <span className="font-mono text-[10px]">plan_…</span>{' '}
                        from Dashboard → Subscriptions → Plans.
                    </p>

                    <div className="overflow-hidden rounded-lg border border-border">
                        <table className="w-full text-sm">
                            <thead className="border-b border-border bg-muted/30">
                                <tr className="text-left text-[11px] font-medium text-muted-foreground">
                                    <th className="px-3 py-2">Currency · Period</th>
                                    {providers.map((provider) => (
                                        <th
                                            key={provider.key}
                                            className="px-3 py-2"
                                        >
                                            {provider.label}
                                        </th>
                                    ))}
                                </tr>
                            </thead>
                            <tbody>
                                {currencyRows.flatMap((currency) =>
                                    PERIODS.map((period) => (
                                        <tr
                                            key={`${currency.id}-${period}`}
                                            className="border-b border-border/60 last:border-0"
                                        >
                                            <td className="px-3 py-2.5 align-top">
                                                <div className="flex items-baseline gap-1.5">
                                                    <span className="font-mono text-[12px] font-medium text-foreground">
                                                        {currency.code}
                                                    </span>
                                                    <span className="text-[10px] text-muted-foreground/80">
                                                        {currency.symbol}
                                                    </span>
                                                </div>
                                                <span className="text-[11px] capitalize text-muted-foreground">
                                                    {period}
                                                </span>
                                            </td>
                                            {providers.map((provider) => {
                                                const cellKey = `${currency.id}:${provider.key}:${period}`;
                                                const row = priceByCell.get(cellKey);
                                                return (
                                                    <td
                                                        key={provider.key}
                                                        className="px-3 py-2.5 align-top"
                                                        data-testid={`admin-billing-price-row-${plan.key}-${currency.code}-${provider.key}-${period}`}
                                                    >
                                                        {row ? (
                                                            <ConfiguredPriceCell
                                                                row={row}
                                                                currency={currency}
                                                                onEdit={() =>
                                                                    setIntent({
                                                                        mode: 'edit',
                                                                        price: row,
                                                                    })
                                                                }
                                                                onDelete={() =>
                                                                    setDeletingPrice(row)
                                                                }
                                                            />
                                                        ) : (
                                                            <Button
                                                                type="button"
                                                                variant="ghost"
                                                                size="sm"
                                                                onClick={() =>
                                                                    setIntent({
                                                                        mode: 'create',
                                                                        currencyId: currency.id,
                                                                        gatewayKey: provider.key,
                                                                        period,
                                                                    })
                                                                }
                                                                data-testid={`admin-billing-price-add-${plan.key}-${currency.code}-${provider.key}-${period}`}
                                                                className="text-muted-foreground hover:text-foreground"
                                                            >
                                                                <Plus
                                                                    className="h-3.5 w-3.5"
                                                                    strokeWidth={2}
                                                                />
                                                                Add
                                                            </Button>
                                                        )}
                                                    </td>
                                                );
                                            })}
                                        </tr>
                                    )),
                                )}
                            </tbody>
                        </table>
                    </div>
                </div>
            )}

            {intent && (
                <PriceFormModal
                    plan={plan}
                    intent={intent}
                    providers={providers}
                    currencies={currencies}
                    onClose={() => setIntent(null)}
                    currencyById={currencyById}
                />
            )}

            {deletingPrice && (
                <DeletePriceConfirm
                    plan={plan}
                    price={deletingPrice}
                    currency={
                        currencyById.get(deletingPrice.currency_id) ?? null
                    }
                    onCancel={() => setDeletingPrice(null)}
                    onDeleted={() => setDeletingPrice(null)}
                />
            )}
        </Card>
    );
}

function ConfiguredPriceCell({
    row,
    currency,
    onEdit,
    onDelete,
}: {
    row: PriceRow;
    currency: CurrencyRow;
    onEdit: () => void;
    onDelete: () => void;
}) {
    const truncatedId = row.provider_price_id
        ? row.provider_price_id.length > 18
            ? `${row.provider_price_id.slice(0, 12)}…${row.provider_price_id.slice(-4)}`
            : row.provider_price_id
        : null;

    return (
        <div className={cn('space-y-1.5', !row.is_active && 'opacity-60')}>
            <div className="flex items-baseline gap-2">
                <span className="text-[13px] font-semibold tabular-nums text-foreground">
                    {formatAmountMinor(row.amount_minor, currency)}
                </span>
                {row.is_active ? (
                    <span className="rounded-full border border-success/30 bg-success/10 px-1.5 py-0.5 text-[10px] font-medium text-success">
                        Active
                    </span>
                ) : (
                    <span className="rounded-full border border-border bg-muted px-1.5 py-0.5 text-[10px] font-medium text-muted-foreground">
                        Inactive
                    </span>
                )}
            </div>
            {truncatedId && (
                <p
                    className="font-mono text-[10px] text-muted-foreground"
                    title={row.provider_price_id ?? undefined}
                >
                    {truncatedId}
                </p>
            )}
            <div className="flex items-center gap-1.5 pt-0.5">
                <Button
                    type="button"
                    variant="ghost"
                    size="sm"
                    onClick={onEdit}
                    data-testid={`admin-billing-price-edit-${row.id}`}
                    className="h-7 px-2 text-[11px]"
                >
                    <Pencil className="h-3 w-3" strokeWidth={2} />
                    Edit
                </Button>
                <Button
                    type="button"
                    variant="dangerGhost"
                    size="sm"
                    onClick={onDelete}
                    data-testid={`admin-billing-price-delete-${row.id}`}
                    className="h-7 px-2 text-[11px]"
                >
                    <Trash2 className="h-3 w-3" strokeWidth={2} />
                </Button>
            </div>
        </div>
    );
}

/* ------------------------------------------------------------------ *
 * Add / Edit price modal
 *
 * On `create` intent, the (currency, gateway, period) tuple is locked
 * (those columns make up the unique index — to change them you delete
 * + recreate). On `edit` intent, all three plus the `id` are read-only;
 * only amount_minor / provider_price_id / is_active are mutable.
 * ------------------------------------------------------------------ */

type PriceFormState = {
    currency_id: number;
    gateway_key: string;
    period: 'monthly' | 'yearly';
    /** Major-unit string the admin types (e.g. "7.50"). Converted to minor on save. */
    amount: string;
    provider_price_id: string;
    is_active: boolean;
};

function priceFormFromIntent(
    intent: PriceModalIntent,
    currencyById: Map<number, CurrencyRow>,
): PriceFormState {
    if (intent.mode === 'edit') {
        const currency = currencyById.get(intent.price.currency_id);
        const decimals = currency ? decimalsFor(currency.code) : 2;
        const major =
            decimals === 0
                ? String(intent.price.amount_minor)
                : (intent.price.amount_minor / 100).toFixed(2);
        return {
            currency_id: intent.price.currency_id,
            gateway_key: intent.price.gateway_key,
            period: intent.price.period,
            amount: major,
            provider_price_id: intent.price.provider_price_id ?? '',
            is_active: intent.price.is_active,
        };
    }
    return {
        currency_id: intent.currencyId,
        gateway_key: intent.gatewayKey,
        period: intent.period,
        amount: '',
        provider_price_id: '',
        is_active: true,
    };
}

function majorToMinor(major: string, currency: CurrencyRow | null): number | null {
    const trimmed = major.trim();
    if (trimmed === '') return null;
    if (!/^\d+(\.\d{1,2})?$/.test(trimmed)) return null;
    const n = parseFloat(trimmed);
    if (!Number.isFinite(n) || n < 0) return null;
    const decimals = currency ? decimalsFor(currency.code) : 2;
    if (decimals === 0) return Math.round(n);
    return Math.round(n * 100);
}

function PriceFormModal({
    plan,
    intent,
    providers,
    currencies,
    onClose,
    currencyById,
}: {
    plan: PlanRow;
    intent: PriceModalIntent;
    providers: ProviderRow[];
    currencies: CurrencyRow[];
    onClose: () => void;
    currencyById: Map<number, CurrencyRow>;
}) {
    const [form, setForm] = useState<PriceFormState>(() =>
        priceFormFromIntent(intent, currencyById),
    );
    const [saving, setSaving] = useState(false);
    const [errors, setErrors] = useState<Record<string, string>>({});
    const [globalError, setGlobalError] = useState<string | null>(null);

    const isEdit = intent.mode === 'edit';
    // Capture the price id outside the submit closure so TS narrowing on
    // the discriminated union sticks — `isEdit` boolean alone wouldn't
    // narrow `intent.price` after assignment, but a direct read does.
    const editPriceId = intent.mode === 'edit' ? intent.price.id : null;
    const lockedCurrency = currencyById.get(form.currency_id) ?? null;
    const lockedProvider = providers.find((p) => p.key === form.gateway_key) ?? null;

    const set = <K extends keyof PriceFormState>(
        key: K,
        value: PriceFormState[K],
    ) => {
        setForm((prev) => ({ ...prev, [key]: value }));
    };

    const errorFor = (field: string): string | undefined => errors[field];

    const onSubmit = async (e: ReactSubmitEvent<HTMLFormElement>) => {
        e.preventDefault();
        setSaving(true);
        setErrors({});
        setGlobalError(null);

        const minor = majorToMinor(form.amount, lockedCurrency);
        const localErrors: Record<string, string> = {};
        if (minor === null) {
            localErrors.amount_minor = 'Enter a valid amount like 7.50.';
        }
        if (
            form.is_active &&
            form.provider_price_id.trim() === ''
        ) {
            localErrors.is_active = 'Active rows require a provider price id.';
        }
        if (Object.keys(localErrors).length > 0) {
            setErrors(localErrors);
            setSaving(false);
            return;
        }

        try {
            const url =
                editPriceId !== null
                    ? `/admin/billing/prices/${editPriceId}`
                    : `/admin/billing/plans/${plan.key}/prices`;
            const method = editPriceId !== null ? 'PATCH' : 'POST';
            const body = editPriceId !== null
                ? {
                      amount_minor: minor,
                      provider_price_id:
                          form.provider_price_id.trim() === ''
                              ? null
                              : form.provider_price_id.trim(),
                      is_active: form.is_active,
                  }
                : {
                      currency_id: form.currency_id,
                      gateway_key: form.gateway_key,
                      period: form.period,
                      amount_minor: minor,
                      provider_price_id:
                          form.provider_price_id.trim() === ''
                              ? null
                              : form.provider_price_id.trim(),
                      is_active: form.is_active,
                  };

            const res = await fetch(url, {
                method,
                credentials: 'same-origin',
                headers: csrfHeaders(),
                body: JSON.stringify(body),
            });

            if (!res.ok) {
                const responseBody = await res.json().catch(() => null);
                const fieldErrors: Record<string, string> = {};
                if (
                    responseBody?.errors &&
                    typeof responseBody.errors === 'object'
                ) {
                    for (const [field, msgs] of Object.entries(
                        responseBody.errors,
                    )) {
                        const arr = Array.isArray(msgs) ? msgs : [msgs];
                        if (arr.length > 0 && typeof arr[0] === 'string') {
                            fieldErrors[field] = arr[0] as string;
                        }
                    }
                }
                setErrors(fieldErrors);
                setGlobalError(
                    typeof responseBody?.message === 'string'
                        ? responseBody.message
                        : `Save failed (${res.status}).`,
                );
                return;
            }

            router.reload({ only: ['prices'] });
            onClose();
        } catch {
            setGlobalError('Network error. Try again.');
        } finally {
            setSaving(false);
        }
    };

    const decimals = lockedCurrency ? decimalsFor(lockedCurrency.code) : 2;
    const amountStep = decimals === 0 ? 1 : 0.01;
    const amountPlaceholder = decimals === 0 ? '650' : '7.50';
    const pricePrefix =
        PRICE_ID_PLACEHOLDERS[form.gateway_key] ?? 'price id';

    return (
        <Modal
            open
            onClose={onClose}
            size="md"
            onSubmit={onSubmit}
            closeOnBackdrop={false}
            closeOnEsc={!saving}
            data-testid="admin-billing-price-modal"
        >
                <Modal.Header
                    title={isEdit ? 'Edit price' : 'Add price'}
                    subtitle={`${plan.name} · ${lockedCurrency?.code ?? '—'} ${form.period} · ${lockedProvider?.label ?? form.gateway_key}`}
                    actions={
                        <button
                            type="button"
                            onClick={onClose}
                            className="rounded-md p-1 text-muted-foreground hover:bg-muted hover:text-foreground"
                            aria-label="Close"
                            data-testid="admin-billing-price-close"
                        >
                            <X className="h-4 w-4" strokeWidth={2} />
                        </button>
                    }
                />

                <Modal.Body
                    fade={false}
                    className="space-y-4"
                    data-testid="admin-billing-price-form"
                >
                        {globalError && (
                            <div
                                className="flex items-start gap-2 rounded-lg border border-destructive/30 bg-destructive/5 px-3 py-2 text-[11px] font-medium text-destructive"
                                role="alert"
                            >
                                <AlertCircle
                                    className="mt-0.5 h-3.5 w-3.5"
                                    strokeWidth={2.5}
                                />
                                <span>{globalError}</span>
                            </div>
                        )}

                        <div className="grid grid-cols-3 gap-3">
                            <FieldRow label="Currency" error={errorFor('currency_id')}>
                                <Select
                                    value={form.currency_id}
                                    onChange={(e) =>
                                        set('currency_id', Number(e.target.value))
                                    }
                                    // Currency is part of the immutable
                                    // tuple — locked on edit. On create,
                                    // it's pre-filled from the cell's
                                    // currency but the admin can change
                                    // it before saving (the cell only
                                    // determined the starting row).
                                    disabled={isEdit}
                                    className={cn(
                                        'h-9 py-1.5 pl-2 text-[12px] uppercase',
                                        isEdit &&
                                            'cursor-not-allowed bg-muted/40 text-foreground/80',
                                    )}
                                    data-testid="admin-billing-price-currency"
                                >
                                    {currencies.map((c) => (
                                        <option key={c.id} value={c.id}>
                                            {c.code}
                                        </option>
                                    ))}
                                </Select>
                            </FieldRow>
                            <FieldRow label="Gateway" error={errorFor('gateway_key')}>
                                <Select
                                    value={form.gateway_key}
                                    onChange={(e) =>
                                        set('gateway_key', e.target.value)
                                    }
                                    disabled
                                    className="h-9 cursor-not-allowed bg-muted/40 py-1.5 pl-2 text-[12px] capitalize text-foreground/80"
                                    data-testid="admin-billing-price-gateway"
                                >
                                    {providers.map((p) => (
                                        <option key={p.key} value={p.key}>
                                            {p.label}
                                        </option>
                                    ))}
                                </Select>
                            </FieldRow>
                            <FieldRow label="Period" error={errorFor('period')}>
                                <Select
                                    value={form.period}
                                    onChange={(e) =>
                                        set(
                                            'period',
                                            e.target.value as 'monthly' | 'yearly',
                                        )
                                    }
                                    disabled
                                    className="h-9 cursor-not-allowed bg-muted/40 py-1.5 pl-2 text-[12px] capitalize text-foreground/80"
                                    data-testid="admin-billing-price-period"
                                >
                                    {PERIODS.map((p) => (
                                        <option key={p} value={p}>
                                            {p}
                                        </option>
                                    ))}
                                </Select>
                            </FieldRow>
                        </div>

                        <FieldRow
                            label={`Amount (${lockedCurrency?.code ?? ''})`}
                            error={errorFor('amount_minor')}
                            hint={
                                decimals === 0
                                    ? `Whole units. e.g. ${amountPlaceholder} = ${lockedCurrency?.symbol ?? ''}${amountPlaceholder}.`
                                    : '$0.01 = 1 minor unit. Major value here, converted to minor on save.'
                            }
                        >
                            <Input
                                type="number"
                                min={0}
                                step={amountStep}
                                inputMode="decimal"
                                value={form.amount}
                                onChange={(e) => set('amount', e.target.value)}
                                placeholder={amountPlaceholder}
                                invalid={!!errorFor('amount_minor')}
                                data-testid="admin-billing-price-amount"
                                className="font-mono text-[13px]"
                            />
                        </FieldRow>

                        <FieldRow
                            label="Provider price ID"
                            error={errorFor('provider_price_id')}
                            hint={
                                form.gateway_key === 'stripe'
                                    ? 'Stripe Price id from Dashboard → Products → your plan → recurring price.'
                                    : form.gateway_key === 'razorpay'
                                      ? 'Razorpay Plan id from Dashboard → Subscriptions → Plans.'
                                      : form.gateway_key === 'paypal'
                                        ? 'PayPal Plan id (P-XXX…) from Dashboard → Catalog → Plans. Create the Product + Plan on PayPal first.'
                                        : undefined
                            }
                        >
                            <Input
                                type="text"
                                value={form.provider_price_id}
                                onChange={(e) =>
                                    set('provider_price_id', e.target.value)
                                }
                                placeholder={pricePrefix}
                                invalid={!!errorFor('provider_price_id')}
                                data-testid="admin-billing-price-provider-id"
                                className="font-mono text-[12px]"
                            />
                        </FieldRow>

                        <label
                            className="flex items-center gap-2 text-[12px] font-medium text-foreground"
                            title={
                                errorFor('is_active') ??
                                'Active prices appear in /pricing for matching customers.'
                            }
                        >
                            <Checkbox
                                checked={form.is_active}
                                onChange={(e) => set('is_active', e.target.checked)}
                                data-testid="admin-billing-price-active"
                            />
                            Active (show this price to matching customers)
                        </label>
                        {errorFor('is_active') && (
                            <p className="-mt-2 text-[11px] font-medium text-destructive">
                                {errorFor('is_active')}
                            </p>
                        )}
                </Modal.Body>

                <Modal.Footer>
                        <Button
                            type="button"
                            variant="ghost"
                            size="sm"
                            onClick={onClose}
                            disabled={saving}
                            data-testid="admin-billing-price-cancel"
                        >
                            Cancel
                        </Button>
                        <Button
                            type="submit"
                            variant="primary"
                            size="sm"
                            disabled={saving}
                            data-testid="admin-billing-price-save"
                        >
                            {saving ? (
                                <Loader2
                                    className="h-3.5 w-3.5 animate-spin"
                                    strokeWidth={2}
                                />
                            ) : null}
                            {isEdit ? 'Save changes' : 'Add price'}
                        </Button>
                </Modal.Footer>
        </Modal>
    );
}

function DeletePriceConfirm({
    plan,
    price,
    currency,
    onCancel,
    onDeleted,
}: {
    plan: PlanRow;
    price: PriceRow;
    currency: CurrencyRow | null;
    onCancel: () => void;
    onDeleted: () => void;
}) {
    const [busy, setBusy] = useState(false);
    const [error, setError] = useState<string | null>(null);

    const onConfirm = async () => {
        setBusy(true);
        setError(null);
        try {
            const res = await fetch(`/admin/billing/prices/${price.id}`, {
                method: 'DELETE',
                credentials: 'same-origin',
                headers: csrfHeaders(),
            });
            if (!res.ok) {
                const body = await res.json().catch(() => null);
                setError(
                    typeof body?.message === 'string'
                        ? body.message
                        : `Delete failed (${res.status}).`,
                );
                return;
            }
            router.reload({ only: ['prices'] });
            onDeleted();
        } catch {
            setError('Network error.');
        } finally {
            setBusy(false);
        }
    };

    const label = `${currency?.code ?? '—'} ${price.period} ${price.gateway_key}`;

    return (
        <Modal
            open
            onClose={onCancel}
            size="md"
            closeOnBackdrop={false}
            closeOnEsc={!busy}
            data-testid="admin-billing-price-delete-modal"
        >
            <Modal.Header
                eyebrow="Delete price"
                eyebrowClassName="text-destructive"
                title={`Delete ${label} price for ${plan.name}?`}
            />
            <Modal.Body fade={false}>
                <p className="text-[12px] text-muted-foreground">
                    Removes this row from{' '}
                    <span className="font-mono">plan_prices</span>. Live
                    subscriptions referencing this exact (plan, currency,
                    gateway, period) tuple will block the delete with a 422.
                    Cannot be undone.
                </p>
                {price.provider_price_id && (
                    <p className="font-mono text-[11px] text-muted-foreground">
                        {price.provider_price_id}
                    </p>
                )}
                {error && (
                    <p
                        className="text-[12px] font-medium text-destructive"
                        data-testid="admin-billing-price-delete-error"
                    >
                        {error}
                    </p>
                )}
            </Modal.Body>
            <Modal.Footer>
                <Button
                    type="button"
                    variant="ghost"
                    size="sm"
                    onClick={onCancel}
                    disabled={busy}
                    data-testid="admin-billing-price-delete-cancel"
                >
                    Cancel
                </Button>
                <Button
                    type="button"
                    variant="danger"
                    size="sm"
                    onClick={onConfirm}
                    disabled={busy}
                    data-testid="admin-billing-price-delete-confirm"
                >
                    {busy ? (
                        <Loader2
                            className="h-3.5 w-3.5 animate-spin"
                            strokeWidth={2}
                        />
                    ) : null}
                    Delete price
                </Button>
            </Modal.Footer>
        </Modal>
    );
}

/* ------------------------------------------------------------------ *
 * Plan create / edit / delete (unchanged from the legacy editor —
 * those endpoints + flows still work; we only swapped the price model).
 * ------------------------------------------------------------------ */

type EditPlanFormState = {
    name: string;
    description: string;
    position: string;
    max_seats: string;
    is_active: boolean;
    can_export: boolean;
    can_use_premium_kits: boolean;
    can_use_ai: boolean;
    ai_tokens_monthly: string;
    ai_tokens_unlimited: boolean;
    project_limit: string;
    project_limit_unlimited: boolean;
    storage_limit_gb: string;
    storage_limit_unlimited: boolean;
};

/**
 * Convert a stored byte cap into the GB string the form input expects.
 * Trims trailing zeroes so 5_368_709_120 → "5", 1_610_612_736 → "1.5".
 */
function bytesToGbInput(bytes: number): string {
    const gb = bytes / (1024 ** 3);
    if (Number.isInteger(gb)) return String(gb);
    // 6 decimals is well past anything an admin would type; toString trims it.
    return parseFloat(gb.toFixed(6)).toString();
}

function planToFormState(plan: PlanRow): EditPlanFormState {
    const features = plan.features ?? {};
    const limit = features.project_limit;
    const storage = features.storage_bytes_limit;
    return {
        name: plan.name,
        description: plan.description ?? '',
        position: String(plan.position),
        // Empty string = unlimited (null). Free is forced to "1".
        max_seats: plan.key === 'free' ? '1' : plan.max_seats === null || plan.max_seats === undefined ? '' : String(plan.max_seats),
        is_active: plan.is_active,
        can_export: !!features.can_export,
        can_use_premium_kits: !!features.can_use_premium_kits,
        can_use_ai: !!features.can_use_ai,
        ai_tokens_monthly: features.ai_tokens_monthly === null || features.ai_tokens_monthly === undefined ? '' : String(features.ai_tokens_monthly),
        ai_tokens_unlimited: features.ai_tokens_monthly === null || features.ai_tokens_monthly === undefined,
        project_limit: limit === null || limit === undefined ? '' : String(limit),
        project_limit_unlimited: limit === null || limit === undefined,
        storage_limit_gb:
            storage === null || storage === undefined
                ? ''
                : bytesToGbInput(storage),
        storage_limit_unlimited: storage === null || storage === undefined,
    };
}

function EditPlanModal({
    plan,
    onClose,
}: {
    plan: PlanRow;
    onClose: () => void;
}) {
    const [form, setForm] = useState<EditPlanFormState>(() =>
        planToFormState(plan),
    );
    const [saving, setSaving] = useState(false);
    const [errors, setErrors] = useState<Record<string, string>>({});
    const [globalError, setGlobalError] = useState<string | null>(null);
    const [confirmingDelete, setConfirmingDelete] = useState(false);

    const isFreePlan = plan.key === 'free';

    const set = <K extends keyof EditPlanFormState>(
        key: K,
        value: EditPlanFormState[K],
    ) => {
        setForm((prev) => ({ ...prev, [key]: value }));
    };

    const errorFor = (field: string): string | undefined => errors[field];

    const onSubmit = async (e: ReactSubmitEvent<HTMLFormElement>) => {
        e.preventDefault();
        setSaving(true);
        setErrors({});
        setGlobalError(null);

        const localErrors: Record<string, string> = {};

        const storageBytes = form.storage_limit_unlimited
            ? null
            : (() => {
                  const gb = parseFloat(form.storage_limit_gb);
                  if (! Number.isFinite(gb) || gb < 0) return null;
                  return Math.round(gb * 1024 * 1024 * 1024);
              })();
        if (! form.storage_limit_unlimited && storageBytes === null) {
            localErrors['features.storage_bytes_limit'] =
                'Enter a non-negative number, or check Unlimited.';
        }

        if (Object.keys(localErrors).length > 0) {
            setErrors(localErrors);
            setSaving(false);
            return;
        }

        const payload = {
            name: form.name,
            description: form.description.trim() === '' ? null : form.description,
            is_active: form.is_active,
            position: parseInt(form.position, 10) || 0,
            // Empty input = unlimited (null). A typed value parses to >= 1.
            max_seats: form.max_seats.trim() === '' ? null : parseInt(form.max_seats, 10) || 1,
            features: {
                can_export: form.can_export,
                can_use_premium_kits: form.can_use_premium_kits,
                can_use_ai: form.can_use_ai,
                ai_tokens_monthly: form.ai_tokens_unlimited
                    ? null
                    : parseInt(form.ai_tokens_monthly, 10) || 0,
                project_limit: form.project_limit_unlimited
                    ? null
                    : parseInt(form.project_limit, 10) || null,
                storage_bytes_limit: storageBytes,
            },
        };

        try {
            const res = await fetch(`/admin/billing/plans/${plan.key}`, {
                method: 'PATCH',
                credentials: 'same-origin',
                headers: csrfHeaders(),
                body: JSON.stringify(payload),
            });

            if (!res.ok) {
                const body = await res.json().catch(() => null);
                const fieldErrors: Record<string, string> = {};
                if (body?.errors && typeof body.errors === 'object') {
                    for (const [field, msgs] of Object.entries(body.errors)) {
                        const arr = Array.isArray(msgs) ? msgs : [msgs];
                        if (arr.length > 0 && typeof arr[0] === 'string') {
                            fieldErrors[field] = arr[0] as string;
                        }
                    }
                }
                setErrors(fieldErrors);
                setGlobalError(
                    typeof body?.message === 'string'
                        ? body.message
                        : `Save failed (${res.status}).`,
                );
                return;
            }

            router.reload({ only: ['plans'] });
            onClose();
        } catch {
            setGlobalError('Network error. Try again.');
        } finally {
            setSaving(false);
        }
    };

    return (
        <>
        <Modal
            open
            onClose={onClose}
            size="md"
            onSubmit={onSubmit}
            data-testid={`admin-plan-${plan.key}-edit-modal`}
        >
                <Modal.Header
                    title="Edit plan"
                    subtitle={<span className="font-mono">{plan.key}</span>}
                    actions={
                        <button
                            type="button"
                            onClick={onClose}
                            className="rounded-md p-1 text-muted-foreground hover:bg-muted hover:text-foreground"
                            aria-label="Close"
                            data-testid={`admin-plan-${plan.key}-edit-close`}
                        >
                            <X className="h-4 w-4" strokeWidth={2} />
                        </button>
                    }
                />

                <Modal.Body
                    fade={false}
                    className="space-y-4"
                    data-testid={`admin-plan-${plan.key}-edit-form`}
                >
                        {globalError && (
                            <div
                                className="flex items-start gap-2 rounded-lg border border-destructive/30 bg-destructive/5 px-3 py-2 text-[11px] font-medium text-destructive"
                                role="alert"
                            >
                                <AlertCircle
                                    className="mt-0.5 h-3.5 w-3.5"
                                    strokeWidth={2.5}
                                />
                                <span>{globalError}</span>
                            </div>
                        )}

                        <FieldRow label="Name" error={errorFor('name')}>
                            <Input
                                value={form.name}
                                onChange={(e) => set('name', e.target.value)}
                                invalid={!!errorFor('name')}
                                data-testid={`admin-plan-${plan.key}-edit-name`}
                            />
                        </FieldRow>

                        <FieldRow label="Description" error={errorFor('description')}>
                            <Input
                                value={form.description}
                                onChange={(e) => set('description', e.target.value)}
                                invalid={!!errorFor('description')}
                                data-testid={`admin-plan-${plan.key}-edit-description`}
                            />
                        </FieldRow>

                        <FieldRow label="Position" error={errorFor('position')}>
                            <Input
                                type="number"
                                min={0}
                                value={form.position}
                                onChange={(e) => set('position', e.target.value)}
                                invalid={!!errorFor('position')}
                                data-testid={`admin-plan-${plan.key}-edit-position`}
                            />
                        </FieldRow>

                        <FieldRow label="Max seats" error={errorFor('max_seats')}>
                            <Input
                                type="number"
                                min={1}
                                value={form.max_seats}
                                onChange={(e) => set('max_seats', e.target.value)}
                                invalid={!!errorFor('max_seats')}
                                disabled={isFreePlan}
                                readOnly={isFreePlan}
                                data-testid={`admin-plan-${plan.key}-edit-max-seats`}
                            />
                            <p className="mt-1 text-[11px] text-muted-foreground">
                                {isFreePlan
                                    ? 'Locked at 1 — the Free plan is owner-only editing (viewers are always unlimited).'
                                    : 'Caps owner + admins + editors; empty = unlimited. Viewers never count toward seats.'}
                            </p>
                        </FieldRow>

                        <div className="space-y-2 rounded-lg border border-border/60 bg-muted/20 p-3">
                            <span className="text-[11px] font-medium text-muted-foreground">
                                Features
                            </span>

                            <label className="flex items-center gap-2 text-[11px] font-medium text-foreground">
                                <Checkbox
                                    checked={form.can_export}
                                    onChange={(e) =>
                                        set('can_export', e.target.checked)
                                    }
                                    data-testid={`admin-plan-${plan.key}-edit-can-export`}
                                />
                                Can export
                            </label>
                            {errorFor('features.can_export') && (
                                <p className="text-[11px] font-medium text-destructive">
                                    {errorFor('features.can_export')}
                                </p>
                            )}

                            <label
                                className="flex items-center gap-2 text-[11px] font-medium text-foreground"
                                title="Allow users on this plan to use kits tagged as Premium on /admin/kits."
                            >
                                <Checkbox
                                    checked={form.can_use_premium_kits}
                                    onChange={(e) =>
                                        set('can_use_premium_kits', e.target.checked)
                                    }
                                    data-testid={`admin-plan-${plan.key}-edit-can-use-premium-kits`}
                                />
                                Can use premium kits
                            </label>
                            {errorFor('features.can_use_premium_kits') && (
                                <p className="text-[11px] font-medium text-destructive">
                                    {errorFor('features.can_use_premium_kits')}
                                </p>
                            )}

                            <label className="flex items-center gap-2 text-[11px] font-medium text-foreground">
                                <Checkbox
                                    checked={form.can_use_ai}
                                    onChange={(e) => set('can_use_ai', e.target.checked)}
                                    data-testid={`admin-plan-${plan.key}-edit-can-use-ai`}
                                />
                                Can use AI generation
                            </label>
                            {errorFor('features.can_use_ai') && (
                                <p className="text-[11px] font-medium text-destructive">
                                    {errorFor('features.can_use_ai')}
                                </p>
                            )}

                            {form.can_use_ai && (
                                <div className="space-y-1.5 pl-6">
                                    <label className="block text-[11px] font-medium text-muted-foreground">
                                        AI tokens / month
                                    </label>
                                    <div className="flex items-center gap-3">
                                        <Input
                                            type="number"
                                            min={0}
                                            value={form.ai_tokens_monthly}
                                            disabled={form.ai_tokens_unlimited}
                                            onChange={(e) => set('ai_tokens_monthly', e.target.value)}
                                            placeholder={form.ai_tokens_unlimited ? 'Unlimited' : '300000'}
                                            invalid={!!errorFor('features.ai_tokens_monthly')}
                                        />
                                        <label className="inline-flex shrink-0 items-center gap-1.5 text-[11px] font-medium text-muted-foreground">
                                            <Checkbox
                                                checked={form.ai_tokens_unlimited}
                                                onChange={(e) => set('ai_tokens_unlimited', e.target.checked)}
                                            />
                                            Unlimited
                                        </label>
                                    </div>
                                    {errorFor('features.ai_tokens_monthly') && (
                                        <p className="text-[11px] font-medium text-destructive">
                                            {errorFor('features.ai_tokens_monthly')}
                                        </p>
                                    )}
                                </div>
                            )}

                            <div className="space-y-1.5">
                                <label className="block text-[11px] font-medium text-muted-foreground">
                                    Project limit
                                </label>
                                <div className="flex items-center gap-3">
                                    <Input
                                        type="number"
                                        min={1}
                                        value={form.project_limit}
                                        disabled={form.project_limit_unlimited}
                                        onChange={(e) =>
                                            set('project_limit', e.target.value)
                                        }
                                        invalid={
                                            !!errorFor('features.project_limit')
                                        }
                                        placeholder={
                                            form.project_limit_unlimited
                                                ? 'Unlimited'
                                                : '10'
                                        }
                                        data-testid={`admin-plan-${plan.key}-edit-project-limit`}
                                    />
                                    <label className="inline-flex shrink-0 items-center gap-1.5 text-[11px] font-medium text-muted-foreground">
                                        <Checkbox
                                            checked={form.project_limit_unlimited}
                                            onChange={(e) =>
                                                set(
                                                    'project_limit_unlimited',
                                                    e.target.checked,
                                                )
                                            }
                                            data-testid={`admin-plan-${plan.key}-edit-project-limit-unlimited`}
                                        />
                                        Unlimited
                                    </label>
                                </div>
                                {errorFor('features.project_limit') && (
                                    <p className="text-[11px] font-medium text-destructive">
                                        {errorFor('features.project_limit')}
                                    </p>
                                )}
                            </div>

                            <div className="space-y-1.5">
                                <label className="block text-[11px] font-medium text-muted-foreground">
                                    Storage limit (GB)
                                </label>
                                <div className="flex items-center gap-3">
                                    <Input
                                        type="number"
                                        min={0}
                                        step={0.1}
                                        value={form.storage_limit_gb}
                                        disabled={form.storage_limit_unlimited}
                                        onChange={(e) =>
                                            set(
                                                'storage_limit_gb',
                                                e.target.value,
                                            )
                                        }
                                        invalid={
                                            !!errorFor(
                                                'features.storage_bytes_limit',
                                            )
                                        }
                                        placeholder={
                                            form.storage_limit_unlimited
                                                ? 'Unlimited'
                                                : '5.0'
                                        }
                                        data-testid={`admin-plan-${plan.key}-edit-storage`}
                                    />
                                    <label className="inline-flex shrink-0 items-center gap-1.5 text-[11px] font-medium text-muted-foreground">
                                        <Checkbox
                                            checked={form.storage_limit_unlimited}
                                            onChange={(e) => {
                                                const checked = e.target.checked;
                                                setForm((prev) => ({
                                                    ...prev,
                                                    storage_limit_unlimited:
                                                        checked,
                                                    storage_limit_gb: checked
                                                        ? ''
                                                        : prev.storage_limit_gb,
                                                }));
                                            }}
                                            data-testid={`admin-plan-${plan.key}-edit-storage-unlimited`}
                                        />
                                        Unlimited
                                    </label>
                                </div>
                                {errorFor('features.storage_bytes_limit') && (
                                    <p className="text-[11px] font-medium text-destructive">
                                        {errorFor(
                                            'features.storage_bytes_limit',
                                        )}
                                    </p>
                                )}
                            </div>
                        </div>

                        <label
                            className="flex items-center gap-2 text-[11px] font-medium text-foreground"
                            title={
                                errorFor('is_active') ??
                                'Deactivating a plan with active subs is blocked.'
                            }
                        >
                            <Checkbox
                                checked={form.is_active}
                                onChange={(e) => set('is_active', e.target.checked)}
                                data-testid={`admin-plan-${plan.key}-edit-active`}
                            />
                            Active
                        </label>
                        {errorFor('is_active') && (
                            <p className="-mt-2 text-[11px] font-medium text-destructive">
                                {errorFor('is_active')}
                            </p>
                        )}
                </Modal.Body>
                <Modal.Footer className="!justify-between">
                        {!isFreePlan ? (
                            <Button
                                type="button"
                                variant="dangerGhost"
                                size="sm"
                                onClick={() => setConfirmingDelete(true)}
                                disabled={saving}
                                data-testid={`admin-plan-${plan.key}-edit-delete`}
                            >
                                Delete plan…
                            </Button>
                        ) : (
                            <span
                                className="text-[11px] text-muted-foreground"
                                data-testid={`admin-plan-${plan.key}-edit-undeletable`}
                            >
                                Free is the system fallback and cannot be deleted.
                            </span>
                        )}
                        <div className="flex items-center gap-2">
                            <Button
                                type="button"
                                variant="ghost"
                                size="sm"
                                onClick={onClose}
                                disabled={saving}
                                data-testid={`admin-plan-${plan.key}-edit-cancel`}
                            >
                                Cancel
                            </Button>
                            <Button
                                type="submit"
                                variant="primary"
                                size="sm"
                                disabled={saving}
                                data-testid={`admin-plan-${plan.key}-edit-save`}
                            >
                                {saving ? (
                                    <Loader2
                                        className="h-3.5 w-3.5 animate-spin"
                                        strokeWidth={2}
                                    />
                                ) : null}
                                Save changes
                            </Button>
                        </div>
                </Modal.Footer>
            </Modal>

            {confirmingDelete && (
                <DeletePlanConfirm
                    plan={plan}
                    onCancel={() => setConfirmingDelete(false)}
                    onDeleted={onClose}
                />
            )}
        </>
    );
}

function FieldRow({
    label,
    error,
    hint,
    children,
}: {
    label: string;
    error?: string;
    hint?: string;
    children: ReactNode;
}) {
    return (
        <div className="space-y-1.5">
            <label className="block text-[11px] font-medium text-muted-foreground">
                {label}
            </label>
            {children}
            {hint && !error && (
                <p className="text-[11px] text-muted-foreground/80">{hint}</p>
            )}
            {error && (
                <p className="text-[11px] font-medium text-destructive">
                    {error}
                </p>
            )}
        </div>
    );
}

function DeletePlanConfirm({
    plan,
    onCancel,
    onDeleted,
}: {
    plan: PlanRow;
    onCancel: () => void;
    onDeleted: () => void;
}) {
    const [busy, setBusy] = useState(false);
    const [error, setError] = useState<string | null>(null);

    const onConfirm = async () => {
        setBusy(true);
        setError(null);
        try {
            const res = await fetch(`/admin/billing/plans/${plan.key}`, {
                method: 'DELETE',
                credentials: 'same-origin',
                headers: csrfHeaders(),
            });
            if (!res.ok) {
                const body = await res.json().catch(() => null);
                setError(
                    typeof body?.message === 'string'
                        ? body.message
                        : `Delete failed (${res.status}).`,
                );
                return;
            }
            router.reload({ only: ['plans', 'prices'] });
            onDeleted();
        } catch {
            setError('Network error.');
        } finally {
            setBusy(false);
        }
    };

    return (
        <Modal
            open
            onClose={onCancel}
            size="md"
            closeOnBackdrop={false}
            closeOnEsc={!busy}
            data-testid={`admin-plan-${plan.key}-delete-modal`}
        >
            <Modal.Header
                eyebrow="Delete plan"
                eyebrowClassName="text-destructive"
                title={plan.name}
            />
            <Modal.Body fade={false}>
                <p className="text-sm text-muted-foreground">
                    Removes the plan row and any provider price-id
                    mappings attached to it. Historical (canceled)
                    subscriptions stay pointed at the gone plan and
                    will render with no plan name on admin pages.
                    Cannot be undone.
                </p>
                {error && (
                    <p
                        className="text-[12px] font-medium text-destructive"
                        data-testid={`admin-plan-${plan.key}-delete-error`}
                    >
                        {error}
                    </p>
                )}
            </Modal.Body>
            <Modal.Footer>
                <Button
                    type="button"
                    variant="ghost"
                    size="sm"
                    onClick={onCancel}
                    disabled={busy}
                    data-testid={`admin-plan-${plan.key}-delete-cancel`}
                >
                    Cancel
                </Button>
                <Button
                    type="button"
                    variant="danger"
                    size="sm"
                    onClick={onConfirm}
                    disabled={busy}
                    data-testid={`admin-plan-${plan.key}-delete-confirm`}
                >
                    {busy ? (
                        <Loader2
                            className="h-3.5 w-3.5 animate-spin"
                            strokeWidth={2}
                        />
                    ) : null}
                    Delete plan
                </Button>
            </Modal.Footer>
        </Modal>
    );
}

type CreatePlanFormState = {
    key: string;
    name: string;
    description: string;
    position: string;
    max_seats: string;
    is_active: boolean;
    can_export: boolean;
    can_use_premium_kits: boolean;
    project_limit: string;
    project_limit_unlimited: boolean;
    storage_limit_gb: string;
    storage_limit_unlimited: boolean;
};

function emptyCreateForm(): CreatePlanFormState {
    return {
        key: '',
        name: '',
        description: '',
        position: '99',
        max_seats: '1',
        is_active: true,
        can_export: true,
        can_use_premium_kits: true,
        project_limit: '10',
        project_limit_unlimited: false,
        storage_limit_gb: '',
        storage_limit_unlimited: false,
    };
}

function CreatePlanModal({
    onClose,
}: {
    onClose: () => void;
}) {
    const [form, setForm] = useState<CreatePlanFormState>(() => emptyCreateForm());
    const [saving, setSaving] = useState(false);
    const [errors, setErrors] = useState<Record<string, string>>({});
    const [globalError, setGlobalError] = useState<string | null>(null);

    const set = <K extends keyof CreatePlanFormState>(
        key: K,
        value: CreatePlanFormState[K],
    ) => {
        setForm((prev) => ({ ...prev, [key]: value }));
    };

    const errorFor = (field: string): string | undefined => errors[field];

    const onSubmit = async (e: ReactSubmitEvent<HTMLFormElement>) => {
        e.preventDefault();
        setSaving(true);
        setErrors({});
        setGlobalError(null);

        const localErrors: Record<string, string> = {};

        const projectLimit = form.project_limit_unlimited
            ? null
            : (() => {
                  const n = parseInt(form.project_limit, 10);
                  return Number.isFinite(n) && n >= 1 ? n : null;
              })();
        if (
            !form.project_limit_unlimited &&
            projectLimit === null
        ) {
            localErrors['features.project_limit'] = 'Enter at least 1.';
        }

        const storageBytes = form.storage_limit_unlimited
            ? null
            : (() => {
                  const gb = parseFloat(form.storage_limit_gb);
                  if (! Number.isFinite(gb) || gb < 0) return null;
                  return Math.round(gb * 1024 * 1024 * 1024);
              })();
        if (
            !form.storage_limit_unlimited &&
            storageBytes === null &&
            form.storage_limit_gb.trim() !== ''
        ) {
            localErrors['features.storage_bytes_limit'] =
                'Enter a non-negative number, or check Unlimited.';
        }

        if (Object.keys(localErrors).length > 0) {
            setErrors(localErrors);
            setSaving(false);
            return;
        }

        try {
            const res = await fetch('/admin/billing/plans', {
                method: 'POST',
                credentials: 'same-origin',
                headers: csrfHeaders(),
                body: JSON.stringify({
                    key: form.key.trim(),
                    name: form.name,
                    description: form.description || null,
                    is_active: form.is_active,
                    position: parseInt(form.position, 10) || 0,
                    max_seats:
                        form.max_seats.trim() === ''
                            ? null
                            : parseInt(form.max_seats, 10) || 1,
                    features: {
                        can_export: form.can_export,
                        can_use_premium_kits: form.can_use_premium_kits,
                        project_limit: projectLimit,
                        storage_bytes_limit: storageBytes,
                    },
                }),
            });

            if (!res.ok) {
                const body = await res.json().catch(() => null);
                const fieldErrors: Record<string, string> = {};
                if (body?.errors && typeof body.errors === 'object') {
                    for (const [field, msgs] of Object.entries(body.errors)) {
                        if (Array.isArray(msgs) && msgs.length > 0) {
                            fieldErrors[field] = String(msgs[0]);
                        }
                    }
                }
                setErrors(fieldErrors);
                setGlobalError(
                    typeof body?.message === 'string'
                        ? body.message
                        : `Create failed (${res.status}).`,
                );
                return;
            }

            router.reload({ only: ['plans', 'prices'] });
            onClose();
        } catch {
            setGlobalError('Network error. Try again.');
        } finally {
            setSaving(false);
        }
    };

    return (
        <Modal
            open
            onClose={onClose}
            size="md"
            onSubmit={onSubmit}
            closeOnBackdrop={false}
            closeOnEsc={!saving}
            data-testid="admin-plan-create-modal"
        >
                <Modal.Header
                    title="Create plan"
                    subtitle="Plans are seeded permanently on creation — keys are immutable, so pick carefully."
                    actions={
                        <button
                            type="button"
                            onClick={onClose}
                            className="rounded-md p-1 text-muted-foreground hover:bg-muted hover:text-foreground"
                            aria-label="Close"
                            data-testid="admin-plan-create-close"
                        >
                            <X className="h-4 w-4" strokeWidth={2} />
                        </button>
                    }
                />

                <Modal.Body
                    fade={false}
                    className="space-y-4"
                    data-testid="admin-plan-create-form"
                >
                        {globalError && (
                            <div
                                className="flex items-start gap-2 rounded-lg border border-destructive/30 bg-destructive/5 px-3 py-2 text-[11px] font-medium text-destructive"
                                role="alert"
                            >
                                <AlertCircle
                                    className="mt-0.5 h-3.5 w-3.5"
                                    strokeWidth={2.5}
                                />
                                <span>{globalError}</span>
                            </div>
                        )}

                        <div className="grid gap-4 sm:grid-cols-2">
                            <FieldRow label="Key (lowercase slug)" error={errorFor('key')}>
                                <Input
                                    value={form.key}
                                    onChange={(e) =>
                                        set('key', e.target.value.toLowerCase())
                                    }
                                    placeholder="enterprise"
                                    invalid={!!errorFor('key')}
                                    data-testid="admin-plan-create-key"
                                />
                            </FieldRow>
                            <FieldRow label="Name" error={errorFor('name')}>
                                <Input
                                    value={form.name}
                                    onChange={(e) => set('name', e.target.value)}
                                    invalid={!!errorFor('name')}
                                    data-testid="admin-plan-create-name"
                                />
                            </FieldRow>
                        </div>

                        <FieldRow label="Description" error={errorFor('description')}>
                            <Input
                                value={form.description}
                                onChange={(e) => set('description', e.target.value)}
                                invalid={!!errorFor('description')}
                                data-testid="admin-plan-create-description"
                            />
                        </FieldRow>

                        <div className="rounded-md border border-border/60 bg-muted/30 px-3 py-2 text-[11px] text-muted-foreground">
                            Pricing is configured separately under Provider
                            prices below — add at least one price row per
                            currency × gateway × period the plan should
                            support after the plan is created.
                        </div>

                        <div className="grid gap-4 sm:grid-cols-2">
                            <FieldRow label="Position" error={errorFor('position')}>
                                <Input
                                    type="number"
                                    min={0}
                                    value={form.position}
                                    onChange={(e) => set('position', e.target.value)}
                                    invalid={!!errorFor('position')}
                                    data-testid="admin-plan-create-position"
                                />
                            </FieldRow>
                            <FieldRow label="Max seats" error={errorFor('max_seats')}>
                                <Input
                                    type="number"
                                    min={1}
                                    value={form.max_seats}
                                    onChange={(e) => set('max_seats', e.target.value)}
                                    invalid={!!errorFor('max_seats')}
                                    placeholder="Empty = unlimited"
                                    data-testid="admin-plan-create-max-seats"
                                />
                                <p className="mt-1 text-[11px] text-muted-foreground">
                                    Caps owner + admins + editors; empty = unlimited.
                                    Viewers never count toward seats.
                                </p>
                            </FieldRow>
                        </div>

                        <FieldRow
                            label="Project limit"
                            error={errorFor('features.project_limit')}
                        >
                            <div className="flex items-center gap-3">
                                <Input
                                    type="number"
                                    min={1}
                                    value={form.project_limit}
                                    onChange={(e) =>
                                        set('project_limit', e.target.value)
                                    }
                                    disabled={form.project_limit_unlimited}
                                    invalid={!!errorFor('features.project_limit')}
                                    className="max-w-[120px]"
                                    data-testid="admin-plan-create-project-limit"
                                />
                                <label className="flex items-center gap-1.5 text-[12px] text-foreground">
                                    <Checkbox
                                        checked={form.project_limit_unlimited}
                                        onChange={(e) =>
                                            set(
                                                'project_limit_unlimited',
                                                e.target.checked,
                                            )
                                        }
                                        data-testid="admin-plan-create-project-limit-unlimited"
                                    />
                                    Unlimited
                                </label>
                            </div>
                        </FieldRow>

                        <FieldRow
                            label="Storage limit (GB)"
                            error={errorFor('features.storage_bytes_limit')}
                        >
                            <div className="flex items-center gap-3">
                                <Input
                                    type="number"
                                    min={0}
                                    step={0.1}
                                    value={form.storage_limit_gb}
                                    onChange={(e) =>
                                        set('storage_limit_gb', e.target.value)
                                    }
                                    disabled={form.storage_limit_unlimited}
                                    invalid={
                                        !!errorFor(
                                            'features.storage_bytes_limit',
                                        )
                                    }
                                    placeholder={
                                        form.storage_limit_unlimited
                                            ? 'Unlimited'
                                            : '5.0'
                                    }
                                    className="max-w-[120px]"
                                    data-testid="admin-plan-create-storage"
                                />
                                <label className="flex items-center gap-1.5 text-[12px] text-foreground">
                                    <Checkbox
                                        checked={form.storage_limit_unlimited}
                                        onChange={(e) => {
                                            const checked = e.target.checked;
                                            setForm((prev) => ({
                                                ...prev,
                                                storage_limit_unlimited: checked,
                                                storage_limit_gb: checked
                                                    ? ''
                                                    : prev.storage_limit_gb,
                                            }));
                                        }}
                                        data-testid="admin-plan-create-storage-unlimited"
                                    />
                                    Unlimited
                                </label>
                            </div>
                        </FieldRow>

                        <div className="flex flex-wrap items-center gap-4">
                            <label className="flex items-center gap-2 text-[12px] text-foreground">
                                <Checkbox
                                    checked={form.is_active}
                                    onChange={(e) =>
                                        set('is_active', e.target.checked)
                                    }
                                    data-testid="admin-plan-create-is-active"
                                />
                                Active (shown to end users)
                            </label>
                            <label className="flex items-center gap-2 text-[12px] text-foreground">
                                <Checkbox
                                    checked={form.can_export}
                                    onChange={(e) =>
                                        set('can_export', e.target.checked)
                                    }
                                    data-testid="admin-plan-create-can-export"
                                />
                                Allow PNG export
                            </label>
                            <label
                                className="flex items-center gap-2 text-[12px] text-foreground"
                                title="Allow users on this plan to use kits tagged as Premium on /admin/kits."
                            >
                                <Checkbox
                                    checked={form.can_use_premium_kits}
                                    onChange={(e) =>
                                        set('can_use_premium_kits', e.target.checked)
                                    }
                                    data-testid="admin-plan-create-can-use-premium-kits"
                                />
                                Allow premium kits
                            </label>
                        </div>
                </Modal.Body>

                <Modal.Footer>
                        <Button
                            type="button"
                            variant="ghost"
                            size="sm"
                            onClick={onClose}
                            disabled={saving}
                            data-testid="admin-plan-create-cancel"
                        >
                            Cancel
                        </Button>
                        <Button
                            type="submit"
                            variant="primary"
                            size="sm"
                            disabled={saving}
                            data-testid="admin-plan-create-submit"
                        >
                            {saving ? (
                                <Loader2
                                    className="h-3.5 w-3.5 animate-spin"
                                    strokeWidth={2}
                                />
                            ) : null}
                            Create plan
                        </Button>
                </Modal.Footer>
        </Modal>
    );
}
