"use client";

import { useEffect, useMemo, useState } from "react";
import { useRouter } from "next/navigation";
import Link from "next/link";
import { useMutation, useQuery } from "@tanstack/react-query";
import { ApiError, api, lineItemsFor, type CatalogueRow, type CheckoutOptions, type Deployment, type Plan, type Term, type Tier } from "@/lib/api";
import { initPaddle, previewPrices, type PricePreview } from "@/lib/paddle";
import { featureDesc, featureLabel } from "@/lib/features";

/* Tiers in the order a customer reads them, cheapest first. */
const TIER_ORDER: Tier[] = ["free", "professional", "enterprise"];

const UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;

/* Feature wording lives in lib/features.ts, shared with the staff
 * configurator. It was duplicated here and there, and the two copies had
 * already drifted. */

interface Choice {
    tier: Tier;
    term: Term;
    servers: number;
    features: string[];
}

/* What a plan offers a given feature: included in the base, a paid add-on, or
 * absent. Drives both the tier cards and the configurator toggles. */
type FeatureState = "included" | "addon" | "absent";

function featureStateFor(plan: Plan | undefined, rows: CatalogueRow[], env: string, term: Term, key: string): FeatureState {
    if (plan?.base_features.includes(key)) return "included";
    const row = rows.find((r) => r.kind === "feature" && r.feature_key === key);
    const priced = Boolean(row?.price_ids?.[env]?.[term]);
    return priced ? "addon" : "absent";
}

export function PurchaseForm() {
    const router = useRouter();
    const account = useQuery({ queryKey: ["account"], queryFn: api.account });
    const optionsQ = useQuery({ queryKey: ["checkout-options"], queryFn: api.checkoutOptions });

    const [dep, setDep] = useState<Deployment>("cloud");
    const [choice, setChoice] = useState<Choice>({
        tier: "professional",
        term: "annual",
        servers: 3,
        features: [],
    });
    const [name, setName] = useState("");
    const [error, setError] = useState<string | null>(null);

    // Follow-up phase after a checkout has been started.
    const [pending, setPending] = useState<null | {
        instanceId: string;
        deployment: Deployment;
    }>(null);
    const [uuid, setUuid] = useState("");

    const options = optionsQ.data;
    const accountId = account.data?.account.account_id ?? "";

    // Distinct feature keys offered on this deployment, in a stable order.
    const featureKeys = useMemo(() => {
        if (!options) return [] as string[];
        const keys = new Set<string>();
        for (const r of options.catalogue) {
            if (r.deployment === dep && r.kind === "feature" && r.feature_key) {
                keys.add(r.feature_key);
            }
        }
        return [...keys];
    }, [options, dep]);

    const activePlans = useMemo(() => (options?.plans ?? []).filter((p) => p.deployment === dep && p.active).sort((a, b) => TIER_ORDER.indexOf(a.tier) - TIER_ORDER.indexOf(b.tier)), [options, dep]);
    const plan = activePlans.find((p) => p.tier === choice.tier);
    const baseServers = plan?.base_limits.max_servers ?? 0;
    const unlimited = baseServers === -1;

    const rows = useMemo(() => (options?.catalogue ?? []).filter((r) => r.deployment === dep && r.tier === choice.tier), [options, dep, choice.tier]);

    // Real line items for the current configuration the same builder the
    // checkout uses, so the summary can never disagree with the overlay.
    const items = useMemo(() => (options ? lineItemsFor(options, choice, dep) : []), [options, choice, dep]);

    // Real, localised prices from Paddle for those items.
    const [receiptPrice, setReceiptPrice] = useState<PricePreview | null>(null);
    useEffect(() => {
        let live = true;
        previewPrices(items).then((p) => {
            if (live) setReceiptPrice(p);
        });
        return () => {
            live = false;
        };
    }, [items]);

    // A headline "base" price per tier, all previewed in one call.
    const [basePrices, setBasePrices] = useState<Record<string, string>>({});
    useEffect(() => {
        if (!options) return;
        const baseItems: { priceId: string; quantity: number; tier: Tier }[] = [];
        for (const p of activePlans) {
            const row = options.catalogue.find((r) => r.deployment === dep && r.tier === p.tier && r.kind === "base");
            const id = row?.price_ids?.[options.env]?.[choice.term];
            if (id) baseItems.push({ priceId: id, quantity: 1, tier: p.tier });
        }
        let live = true;
        previewPrices(baseItems.map(({ priceId, quantity }) => ({ priceId, quantity }))).then((p) => {
            if (!live) return;
            const next: Record<string, string> = {};
            if (p) {
                for (const bi of baseItems) {
                    const line = p.lines[bi.priceId];
                    if (line) next[bi.tier] = line.total;
                }
            }
            setBasePrices(next);
        });
        return () => {
            live = false;
        };
    }, [options, dep, choice.term, activePlans]);

    // --- actions -----------------------------------------------------------

    const createFree = useMutation({
        mutationFn: () => api.createInstance(name.trim()),
        onSuccess: () => router.push("/"),
        onError: (e) => setError(e instanceof ApiError ? e.message : "Could not create the instance."),
    });

    // Self-hosted Free binds to the install's own UUID: register the instance,
    // then issue its Free licence in one action.
    const createSelfHostedFree = useMutation({
        mutationFn: async () => {
            const inst = await api.link(uuid.trim(), name.trim());
            await api.claimFree(inst.instance_id);
            return inst.instance_id;
        },
        onSuccess: (id) => router.push(`/instances/${id}`),
        onError: (e) => setError(e instanceof ApiError ? e.message : "Could not create the licence."),
    });

    const startCheckout = useMutation({
        mutationFn: async () => {
            const trimmed = name.trim();
            const r = dep === "cloud" ? await api.createCloudCheckout(trimmed) : await api.createSelfHosted(trimmed);
            return r.instance_id;
        },
        onSuccess: async (instanceId) => {
            setPending({ instanceId, deployment: dep });
            const paddle = await initPaddle();
            paddle?.Checkout.open({
                items: items.map((i) => ({ priceId: i.priceId, quantity: i.quantity })),
                customData: { account_id: accountId, instance_id: instanceId },
            });
        },
        onError: (e) => setError(e instanceof ApiError ? e.message : "Could not start checkout."),
    });

    const claim = useMutation({
        mutationFn: () => api.claimLink(pending!.instanceId, uuid.trim()),
        onSuccess: () => router.push("/"),
        onError: (e) => setError(e instanceof ApiError ? e.message : "Could not link the install."),
    });

    if (optionsQ.isLoading || account.isLoading) {
        return <p className="text-ink-3">Loading plans…</p>;
    }
    if (!options) {
        return <p className="text-ink-2">Plans are unavailable right now. Try again shortly.</p>;
    }

    const selfHostedFree = dep === "self_hosted" && choice.tier === "free";
    const cloudFree = dep === "cloud" && choice.tier === "free";
    const paid = choice.tier !== "free";

    return (
        <div className="grid items-start gap-6 lg:grid-cols-[minmax(0,1fr)_340px]">
            {/* ---- main column ---- */}
            <div className="grid min-w-0 gap-6">
                <Block n={1} label="Deployment">
                    <Seg
                        value={dep}
                        onChange={(v) => {
                            const next = v as Deployment;
                            setDep(next);
                            // Self-hosted sells annual only; clamp the term.
                            setChoice((c) => ({
                                ...c,
                                term: next === "self_hosted" ? "annual" : c.term,
                            }));
                        }}
                        options={[
                            {
                                value: "cloud",
                                icon: cloudIcon,
                                title: "Cloud",
                                sub: "We host and manage it · monthly or annual",
                            },
                            {
                                value: "self_hosted",
                                icon: serverIcon,
                                title: "Self-hosted",
                                sub: "Runs on your own servers · annual only",
                            },
                        ]}
                    />
                </Block>

                {dep === "cloud" && (
                    <Block n={2} label="Billing">
                        <Seg
                            value={choice.term}
                            onChange={(v) => setChoice((c) => ({ ...c, term: v as Term }))}
                            options={[
                                {
                                    value: "monthly",
                                    icon: calendarIcon,
                                    title: "Monthly",
                                    sub: "Pay as you go · cancel anytime",
                                },
                                {
                                    value: "annual",
                                    icon: annualIcon,
                                    title: "Annual",
                                    sub: "2 months free vs monthly",
                                },
                            ]}
                        />
                    </Block>
                )}

                <Block n={dep === "cloud" ? 3 : 2} label="Plan">
                    <div className="grid gap-3 sm:grid-cols-3">
                        {activePlans.map((p) => (
                            <TierCard
                                key={p.tier}
                                plan={p}
                                selected={p.tier === choice.tier}
                                headline={p.tier === "free" ? "£0" : basePrices[p.tier]}
                                cycleLabel={cycleShort(dep, choice.term)}
                                featureKeys={featureKeys}
                                catalogue={options.catalogue.filter((r) => r.deployment === dep && r.tier === p.tier)}
                                env={options.env}
                                term={choice.term}
                                onSelect={() =>
                                    setChoice((c) => ({
                                        ...c,
                                        tier: p.tier,
                                        // Moving tier moves the floor; clamp up.
                                        servers: Math.max(c.servers, p.base_limits.max_servers === -1 ? c.servers : p.base_limits.max_servers),
                                        // Drop add-ons the new tier does not sell.
                                        features: c.features.filter((k) => {
                                            const st = featureStateFor(
                                                p,
                                                options.catalogue.filter((r) => r.deployment === dep && r.tier === p.tier),
                                                options.env,
                                                c.term,
                                                k,
                                            );
                                            return st === "addon";
                                        }),
                                    }))
                                }
                            />
                        ))}
                    </div>
                </Block>

                {paid && (
                    <Block n={dep === "cloud" ? 4 : 3} label="Configure">
                        <div className="rounded border border-rule bg-panel p-4">
                            {/* servers */}
                            <Row title="Managed servers" desc={unlimited ? "Unlimited servers included in this plan" : `${baseServers} included`}>
                                {unlimited ? (
                                    <span className="text-[0.72rem] font-semibold uppercase tracking-[0.06em] text-valid">Unlimited</span>
                                ) : (
                                    <Stepper value={choice.servers} min={baseServers} max={500} onChange={(servers) => setChoice((c) => ({ ...c, servers }))} />
                                )}
                            </Row>

                            {/* features */}
                            {featureKeys.map((key) => {
                                const st = featureStateFor(plan, rows, options.env, choice.term, key);
                                return (
                                    <Row key={key} title={featureLabel(key)} desc={featureDesc(key)} dim={st === "absent"}>
                                        {st === "included" ? (
                                            <span className="text-[0.72rem] font-semibold uppercase tracking-[0.06em] text-valid">Included</span>
                                        ) : st === "absent" ? (
                                            <span className="font-mono text-[0.76rem] text-ink-3">Not in this plan</span>
                                        ) : (
                                            <Toggle
                                                checked={choice.features.includes(key)}
                                                onChange={(on) =>
                                                    setChoice((c) => ({
                                                        ...c,
                                                        features: on ? [...c.features, key] : c.features.filter((f) => f !== key),
                                                    }))
                                                }
                                            />
                                        )}
                                    </Row>
                                );
                            })}
                        </div>
                    </Block>
                )}

                {selfHostedFree && (
                    <Block n={3} label="Your install">
                        <div className="grid gap-3 rounded border border-rule bg-panel p-4">
                            <p className="text-[0.86rem] text-ink-2">
                                Install Vantage on your own server first, then paste the instance ID it reports. We register it and issue your Free licence nothing to pay.
                            </p>
                            <label className="grid gap-1">
                                <span className="text-[0.72rem] font-semibold uppercase tracking-[0.08em] text-ink-3">Instance ID</span>
                                <input
                                    value={uuid}
                                    onChange={(e) => setUuid(e.target.value)}
                                    placeholder="00000000-0000-0000-0000-000000000000"
                                    className="rounded border border-rule bg-panel px-2.5 py-2 font-mono text-[0.82rem] text-ink placeholder:text-ink-3"
                                />
                                <span className="text-[0.72rem] text-ink-3">Find this on your install&rsquo;s Settings → Licence page, or the setup screen just after first sign-in.</span>
                            </label>
                        </div>
                    </Block>
                )}
            </div>

            {/* ---- receipt rail ---- */}
            <aside className="lg:sticky lg:top-5">
                <div className="overflow-hidden rounded-[14px] border border-rule bg-panel shadow-[var(--shadow)]">
                    <div className="flex items-center justify-between border-b border-rule-soft px-4 py-3.5">
                        <h3 className="text-[0.95rem] font-semibold">Order summary</h3>
                        <span className="rounded border border-rule px-1.5 py-0.5 font-mono text-[0.62rem] uppercase tracking-[0.07em] text-ink-3">{dep === "cloud" ? "Cloud" : "Self-hosted"}</span>
                    </div>

                    <Receipt options={options} dep={dep} choice={choice} plan={plan} items={items} price={receiptPrice} />

                    {/* name + CTA */}
                    <div className="grid gap-3 border-t border-rule px-4 py-4">
                        {!pending && (
                            <label className="grid gap-1">
                                <span className="text-[0.72rem] font-semibold uppercase tracking-[0.08em] text-ink-3">Instance name</span>
                                <input
                                    value={name}
                                    onChange={(e) => setName(e.target.value)}
                                    placeholder="Northgate Systems"
                                    className="rounded border border-rule bg-panel px-2.5 py-2 text-[0.9rem] text-ink placeholder:text-ink-3"
                                />
                            </label>
                        )}

                        {error && <p className="text-[0.82rem] text-expired">{error}</p>}

                        {/* Phase A: choose an action for the configuration. */}
                        {!pending &&
                            (selfHostedFree ? (
                                <Cta
                                    label={createSelfHostedFree.isPending ? "Creating…" : "Create licence"}
                                    variant="line"
                                    disabled={!UUID_RE.test(uuid.trim()) || createSelfHostedFree.isPending}
                                    onClick={() => {
                                        setError(null);
                                        createSelfHostedFree.mutate();
                                    }}
                                />
                            ) : cloudFree ? (
                                <Cta
                                    label={createFree.isPending ? "Creating…" : "Create free instance"}
                                    variant="line"
                                    disabled={!name.trim() || createFree.isPending}
                                    onClick={() => {
                                        setError(null);
                                        createFree.mutate();
                                    }}
                                />
                            ) : (
                                <Cta
                                    label={startCheckout.isPending ? "Starting…" : "Continue to payment"}
                                    disabled={!name.trim() || items.length === 0 || !accountId || startCheckout.isPending}
                                    onClick={() => {
                                        setError(null);
                                        startCheckout.mutate();
                                    }}
                                />
                            ))}

                        {/* Phase B: after the checkout has been opened. */}
                        {pending?.deployment === "self_hosted" && (
                            <div className="grid gap-2 border-t border-rule-soft pt-3">
                                <p className="text-[0.8rem] text-ink-2">Once payment clears, paste the instance ID your install reports (Settings → Licence) to receive your licence.</p>
                                <input
                                    value={uuid}
                                    onChange={(e) => setUuid(e.target.value)}
                                    placeholder="00000000-0000-0000-0000-000000000000"
                                    className="rounded border border-rule bg-panel px-2.5 py-2 font-mono text-[0.82rem] text-ink placeholder:text-ink-3"
                                />
                                <Cta
                                    label={claim.isPending ? "Linking…" : "Link and issue licence"}
                                    disabled={!uuid.trim() || claim.isPending}
                                    onClick={() => {
                                        setError(null);
                                        claim.mutate();
                                    }}
                                />
                            </div>
                        )}
                        {pending?.deployment === "cloud" && (
                            <div className="grid gap-2 border-t border-rule-soft pt-3">
                                <p className="text-[0.8rem] text-ink-2">Your instance is being set up. Its licence appears the moment payment clears no further steps.</p>
                                <Link href={`/instances/${pending.instanceId}`} className="font-semibold text-accent underline">
                                    Go to your instance
                                </Link>
                            </div>
                        )}
                    </div>

                    <div className="flex items-start gap-2 border-t border-rule-soft px-4 py-3 text-[0.72rem] text-ink-3">
                        <LockIcon />
                        <span>{paid ? "Secure checkout by Paddle, our reseller of record. VAT is added at checkout where applicable." : "No payment details required for the Free plan."}</span>
                    </div>
                </div>
            </aside>
        </div>
    );
}

// ---------------------------------------------------------------------------
// Presentational pieces
// ---------------------------------------------------------------------------

function cycleShort(dep: Deployment, term: Term) {
    return dep === "cloud" ? (term === "annual" ? "/yr" : "/mo") : "/yr";
}

function Block({ n, label, children }: { n: number; label: string; children: React.ReactNode }) {
    return (
        <section className="grid gap-2.5">
            <h2 className="flex items-center gap-2 text-[0.72rem] font-bold uppercase tracking-[0.1em] text-ink-3">
                <span className="font-mono text-accent">{n}</span>
                {label}
            </h2>
            {children}
        </section>
    );
}

interface SegOption {
    value: string;
    icon: React.ReactNode;
    title: string;
    sub: string;
}

function Seg({ value, onChange, options }: { value: string; onChange: (v: string) => void; options: SegOption[] }) {
    return (
        <div className="flex gap-1 rounded-[9px] border border-rule bg-panel-2 p-1">
            {options.map((o) => {
                const on = o.value === value;
                return (
                    <button
                        key={o.value}
                        type="button"
                        aria-pressed={on}
                        onClick={() => onChange(o.value)}
                        className={`flex flex-1 items-center gap-3 rounded-[7px] px-4 py-3 text-left transition-colors ${on ? "bg-panel text-ink shadow-[var(--shadow)]" : "text-ink-2"}`}
                    >
                        <span
                            className={`grid h-[34px] w-[34px] flex-none place-items-center rounded-lg border ${
                                on ? "border-accent/40 bg-accent-wash text-accent" : "border-rule bg-panel text-ink-3"
                            }`}
                        >
                            {o.icon}
                        </span>
                        <span className="flex flex-col leading-tight">
                            <span className="text-[0.92rem] font-bold">{o.title}</span>
                            <span className={`text-[0.72rem] font-medium ${on ? "text-accent" : "text-ink-3"}`}>{o.sub}</span>
                        </span>
                        <span className={`relative ml-auto h-[18px] w-[18px] flex-none rounded-full border-2 ${on ? "border-accent bg-accent" : "border-rule"}`}>
                            {on && <span className="absolute inset-[3px] rounded-full bg-accent-ink" />}
                        </span>
                    </button>
                );
            })}
        </div>
    );
}

function TierCard({
    plan,
    selected,
    headline,
    cycleLabel,
    featureKeys,
    catalogue,
    env,
    term,
    onSelect,
}: {
    plan: Plan;
    selected: boolean;
    headline?: string;
    cycleLabel: string;
    featureKeys: string[];
    catalogue: CatalogueRow[];
    env: string;
    term: Term;
    onSelect: () => void;
}) {
    const base = plan.base_limits.max_servers;
    const servers = base === -1 ? "Unlimited servers" : `${base} server${base === 1 ? "" : "s"} included`;
    return (
        <button
            type="button"
            aria-pressed={selected}
            onClick={onSelect}
            className={`relative flex flex-col gap-3 rounded-xl border bg-panel p-4 text-left transition-[border-color,box-shadow] ${
                selected ? "border-accent shadow-[0_0_0_1px_var(--accent)]" : "border-rule hover:border-accent/50"
            }`}
        >
            {plan.tier === "professional" && (
                <span className="absolute -top-2 right-3 rounded-full bg-accent px-2 py-0.5 text-[0.6rem] font-bold uppercase tracking-[0.08em] text-accent-ink">Most popular</span>
            )}
            <span className="flex items-center justify-between gap-2">
                <span className="text-[1.05rem] font-extrabold tracking-[-0.02em]">{plan.name}</span>
                <span className={`relative h-4 w-4 flex-none rounded-full border-2 ${selected ? "border-accent bg-accent" : "border-rule"}`}>
                    {selected && <span className="absolute inset-[3px] rounded-full bg-accent-ink" />}
                </span>
            </span>
            <span className="flex items-baseline gap-1">
                <span className="text-[1.5rem] font-extrabold tracking-[-0.03em] tabular-nums">{headline ?? "—"}</span>
                <span className="text-[0.72rem] text-ink-3">{plan.tier === "free" ? "forever" : cycleLabel}</span>
            </span>
            <ul className="grid gap-1.5 text-[0.8rem] text-ink-2">
                <FeatureLine on>{servers}</FeatureLine>
                {featureKeys.map((key) => {
                    const st = featureStateFor(plan, catalogue, env, term, key);
                    return (
                        <FeatureLine key={key} on={st !== "absent"}>
                            {featureLabel(key)}
                            {st === "included" ? " included" : st === "addon" ? " add-on" : " not available"}
                        </FeatureLine>
                    );
                })}
                <FeatureLine on>{supportLabel(plan.support_level)} support</FeatureLine>
            </ul>
        </button>
    );
}

function supportLabel(level: string) {
    switch (level) {
        case "community":
            return "Community";
        case "email_24_5":
            return "Email, 24/5";
        case "email_call_24_7":
            return "Email + call, 24/7";
        default:
            return level;
    }
}

function FeatureLine({ on, children }: { on: boolean; children: React.ReactNode }) {
    return (
        <li className={`flex items-start gap-2 ${on ? "" : "text-ink-3"}`}>
            <span className={`mt-0.5 flex-none ${on ? "text-valid" : "text-ink-3"}`} aria-hidden>
                {on ? (
                    <svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="3" strokeLinecap="round" strokeLinejoin="round">
                        <path d="M20 6 9 17l-5-5" />
                    </svg>
                ) : (
                    <svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="3" strokeLinecap="round">
                        <path d="M5 12h14" />
                    </svg>
                )}
            </span>
            <span>{children}</span>
        </li>
    );
}

function Row({ title, desc, dim, children }: { title: string; desc: string; dim?: boolean; children: React.ReactNode }) {
    return (
        <div className={`flex items-center justify-between gap-4 border-b border-rule-soft py-3.5 first:pt-0 last:border-0 last:pb-0 ${dim ? "opacity-55" : ""}`}>
            <div className="min-w-0">
                <h4 className="text-[0.9rem] font-semibold">{title}</h4>
                {desc && <p className="text-[0.78rem] text-ink-3">{desc}</p>}
            </div>
            <div className="flex-none">{children}</div>
        </div>
    );
}

function Stepper({ value, min, max, onChange }: { value: number; min: number; max: number; onChange: (v: number) => void }) {
    const clamp = (v: number) => Math.min(max, Math.max(min, v));
    return (
        <div className="inline-flex items-center overflow-hidden rounded-lg border border-rule">
            <button
                type="button"
                aria-label="Fewer servers"
                disabled={value <= min}
                onClick={() => onChange(clamp(value - 1))}
                className="h-9 w-9 bg-panel-2 text-lg leading-none text-ink hover:bg-accent-wash hover:text-accent disabled:opacity-35"
            >
                −
            </button>
            <input
                value={value}
                inputMode="numeric"
                aria-label="Server count"
                onChange={(e) => onChange(clamp(parseInt(e.target.value) || min))}
                className="h-9 w-14 border-x border-rule bg-panel text-center text-[0.9rem] font-bold tabular-nums text-ink"
            />
            <button
                type="button"
                aria-label="More servers"
                disabled={value >= max}
                onClick={() => onChange(clamp(value + 1))}
                className="h-9 w-9 bg-panel-2 text-lg leading-none text-ink hover:bg-accent-wash hover:text-accent disabled:opacity-35"
            >
                +
            </button>
        </div>
    );
}

function Toggle({ checked, onChange }: { checked: boolean; onChange: (v: boolean) => void }) {
    return (
        <button
            type="button"
            role="switch"
            aria-checked={checked}
            onClick={() => onChange(!checked)}
            className={`relative h-6 w-[42px] flex-none rounded-full transition-colors ${checked ? "bg-accent" : "bg-rule"}`}
        >
            <span className={`absolute top-[3px] h-[18px] w-[18px] rounded-full bg-white shadow transition-[left] ${checked ? "left-[21px]" : "left-[3px]"}`} />
        </button>
    );
}

function Receipt({
    options,
    dep,
    choice,
    plan,
    items,
    price,
}: {
    options: CheckoutOptions;
    dep: Deployment;
    choice: Choice;
    plan: Plan | undefined;
    items: { priceId: string; quantity: number }[];
    price: PricePreview | null;
}) {
    if (choice.tier === "free") {
        return (
            <div className="px-4">
                <div className="flex items-center justify-between gap-3 py-3 text-[0.85rem]">
                    <span className="text-ink-2">
                        {plan?.name ?? "Free"} plan
                        <small className="block text-[0.72rem] text-ink-3">{plan?.base_limits.max_servers ?? 1} server · community support</small>
                    </span>
                    <span className="font-mono font-semibold tabular-nums text-valid">£0</span>
                </div>
            </div>
        );
    }

    // Label each real line item from the catalogue, and price it from Paddle.
    const base = plan?.base_limits.max_servers ?? 0;
    const extra = base === -1 ? 0 : Math.max(0, choice.servers - base);
    const rows = options.catalogue.filter((r) => r.deployment === dep && r.tier === choice.tier);
    const idFor = (predicate: (r: CatalogueRow) => boolean) => {
        const row = rows.find(predicate);
        return row?.price_ids?.[options.env]?.[choice.term] ?? "";
    };
    const amount = (priceId: string) => price?.lines[priceId]?.total ?? null;

    const lines: { label: string; sub?: string; value: string | null }[] = [];
    const baseId = idFor((r) => r.kind === "base");
    lines.push({
        label: `${plan?.name ?? ""} base`,
        sub: base === -1 ? "unlimited servers" : `${base} servers included`,
        value: amount(baseId),
    });
    if (extra > 0) {
        lines.push({
            label: "Extra servers",
            sub: `${extra} × per server`,
            value: amount(idFor((r) => r.kind === "limit" && r.limit_key === "max_servers")),
        });
    }
    for (const key of choice.features) {
        const id = idFor((r) => r.kind === "feature" && r.feature_key === key);
        if (id) lines.push({ label: featureLabel(key), sub: "add-on", value: amount(id) });
    }

    const priced = price !== null;
    return (
        <div className="px-4">
            <div className="grid">
                {lines.map((l, i) => (
                    <div key={i} className="flex justify-between gap-3 border-b border-dashed border-rule-soft py-2.5 text-[0.85rem] last:border-0">
                        <span className="text-ink-2">
                            {l.label}
                            {l.sub && <small className="block text-[0.72rem] text-ink-3">{l.sub}</small>}
                        </span>
                        <span className="font-mono font-semibold tabular-nums">{l.value ?? "—"}</span>
                    </div>
                ))}
            </div>
            <div className="mt-2 flex items-baseline justify-between border-t border-rule pt-3">
                <span className="text-[0.85rem]">Total</span>
                <span className="text-[1.4rem] font-extrabold tabular-nums">{priced && price?.total ? price.total : "—"}</span>
            </div>
            <p className="pb-3 pt-0.5 text-[0.72rem] text-ink-3">
                {priced
                    ? dep === "cloud"
                        ? choice.term === "annual"
                            ? "per year, billed annually"
                            : "per month, billed monthly"
                        : "per year, billed annually"
                    : items.length > 0
                      ? "Final price shown at checkout."
                      : ""}
            </p>
        </div>
    );
}

function Cta({ label, onClick, disabled, variant = "solid" }: { label: string; onClick: () => void; disabled?: boolean; variant?: "solid" | "line" }) {
    return (
        <button
            type="button"
            disabled={disabled}
            onClick={onClick}
            className={`rounded-[9px] px-3 py-3 text-[0.9rem] font-bold transition-[filter] hover:brightness-[1.06] disabled:opacity-40 disabled:hover:brightness-100 ${
                variant === "solid" ? "bg-accent text-accent-ink" : "border border-accent bg-panel text-accent"
            }`}
        >
            {label}
        </button>
    );
}

// ---------------------------------------------------------------------------
// Icons
// ---------------------------------------------------------------------------

const cloudIcon = (
    <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
        <path d="M17.5 19a4.5 4.5 0 0 0 .5-9 6 6 0 0 0-11.6-1.5A4 4 0 0 0 6 19z" />
    </svg>
);
const serverIcon = (
    <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
        <rect x="2" y="3" width="20" height="6" rx="1" />
        <rect x="2" y="9" width="20" height="6" rx="1" />
        <path d="M6 6h.01M6 12h.01" />
    </svg>
);
const calendarIcon = (
    <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
        <rect x="3" y="4" width="18" height="18" rx="2" />
        <path d="M3 10h18M8 2v4M16 2v4" />
    </svg>
);
const annualIcon = (
    <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
        <path d="M12 2v20M17 5H9.5a3.5 3.5 0 0 0 0 7h5a3.5 3.5 0 0 1 0 7H6" />
    </svg>
);
function LockIcon() {
    return (
        <svg className="mt-px flex-none" width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2">
            <rect x="3" y="11" width="18" height="11" rx="2" />
            <path d="M7 11V7a5 5 0 0 1 10 0v4" />
        </svg>
    );
}
