import { cn } from '@/lib/cn';
import type { ThemeKnobs } from '@/lib/themePalette';
import type { SharedProps } from '@/types/inertia';
import { usePage } from '@inertiajs/react';
import { Monitor, Moon, Sun } from 'lucide-react';
import { useEffect, useRef, useState } from 'react';

// Preview is scoped to marketing surfaces (/, /pricing). Authenticated app
// pages (/projects, /admin*, /editor) were dropped — they either need real
// data (projects, project ids) or live behind admin chrome that we already
// see while editing the theme. Marketing surfaces are also where buyer-side
// brand impressions land, so they're the only ones that matter here.
type Route = '/' | '/pricing';
type Mode = 'light' | 'dark' | 'system';
type Width = 375 | 768 | 1024 | 1440 | 'fluid';

type Props = {
    knobs: ThemeKnobs;
    previewVersion: number;
};

type RouteOption = { value: Route; label: string; saasOnly?: boolean };

const ROUTES: RouteOption[] = [
    { value: '/', label: '/' },
    { value: '/pricing', label: '/pricing', saasOnly: true },
];

const MODES: { value: Mode; label: string; Icon: typeof Sun }[] = [
    { value: 'light', label: 'Light', Icon: Sun },
    { value: 'dark', label: 'Dark', Icon: Moon },
    { value: 'system', label: 'System', Icon: Monitor },
];

const WIDTHS: { value: Width; label: string }[] = [
    { value: 'fluid', label: 'Fluid' },
    { value: 375, label: '375' },
    { value: 768, label: '768' },
    { value: 1024, label: '1024' },
    { value: 1440, label: '1440' },
];

const MIN_WIDTH = 320;
const MAX_WIDTH = 1920;

function buildIframeSrc(route: Route, mode: Mode, version: number): string {
    const params = new URLSearchParams({
        theme_preview: '1',
        theme_mode: mode,
        v: String(version),
    });
    return `${route}?${params.toString()}`;
}

// `knobs` is consumed by the parent's debounced draft POST — the iframe doesn't
// read knobs directly, it pulls the session draft from the server. We accept
// it here so the prop contract stays explicit (and to silence future churn if
// we want to flash the route bar when knobs change).
// eslint-disable-next-line @typescript-eslint/no-unused-vars
export default function PreviewPane({ knobs: _knobs, previewVersion }: Props) {
    const { appMode } = usePage<SharedProps>().props;
    // /pricing is saas-only — drop it in self_hosted (would 404).
    const routes = ROUTES.filter((r) => !(r.saasOnly && appMode !== 'saas'));
    const iframeRef = useRef<HTMLIFrameElement>(null);
    const [route, setRoute] = useState<Route>('/');
    const [mode, setMode] = useState<Mode>('light');
    const [width, setWidth] = useState<Width>('fluid');
    const [customWidth, setCustomWidth] = useState<number | null>(null);

    // Imperative src updates avoid React re-mounting the iframe (which would
    // blank out before each reload). Debounced so a flurry of `previewVersion`
    // bumps coalesces into one reload — the parent already debounces draft
    // POSTs but rapid knob changes can still chain bumps if a save lands while
    // the user is dragging a slider.
    useEffect(() => {
        const iframe = iframeRef.current;
        if (!iframe) return;
        const next = buildIframeSrc(route, mode, previewVersion);
        const id = window.setTimeout(() => {
            if (iframe.src.endsWith(next)) return;
            iframe.src = next;
        }, 60);
        return () => window.clearTimeout(id);
    }, [route, mode, previewVersion]);

    const effectiveWidth = customWidth ?? (width === 'fluid' ? null : width);

    return (
        <div
            className="flex h-full min-h-0 flex-col overflow-hidden rounded-xl border border-border bg-card shadow-sm"
            data-testid="appearance-preview-pane"
        >
            <div className="flex h-10 shrink-0 items-center gap-3 overflow-x-auto border-b border-border bg-card px-3">
                <div
                    className="flex items-center gap-1 rounded-md border border-border bg-muted/40 p-0.5"
                    role="group"
                    aria-label="Preview route"
                >
                    {routes.map((r) => (
                        <button
                            key={r.value}
                            type="button"
                            onClick={() => setRoute(r.value)}
                            data-testid={`appearance-preview-route-${r.label.replace(/\W/g, '') || 'home'}`}
                            className={cn(
                                'h-7 rounded-[5px] px-2.5 text-[11px] font-medium transition-colors',
                                route === r.value
                                    ? 'bg-card text-foreground shadow-sm'
                                    : 'text-muted-foreground hover:text-foreground',
                            )}
                        >
                            {r.label}
                        </button>
                    ))}
                </div>

                <div
                    className="flex items-center gap-1 rounded-md border border-border bg-muted/40 p-0.5"
                    role="group"
                    aria-label="Preview color mode"
                >
                    {MODES.map(({ value, label, Icon }) => (
                        <button
                            key={value}
                            type="button"
                            onClick={() => setMode(value)}
                            aria-label={label}
                            title={label}
                            data-testid={`appearance-preview-mode-${value}`}
                            className={cn(
                                'inline-flex h-7 w-7 items-center justify-center rounded-[5px] transition-colors',
                                mode === value
                                    ? 'bg-card text-foreground shadow-sm'
                                    : 'text-muted-foreground hover:text-foreground',
                            )}
                        >
                            <Icon className="h-3.5 w-3.5" strokeWidth={2} />
                        </button>
                    ))}
                </div>

                <div className="ml-auto flex items-center gap-2">
                    <span className="text-[11px] font-medium text-muted-foreground">
                        Width
                    </span>
                    <div
                        className="flex items-center gap-1 rounded-md border border-border bg-muted/40 p-0.5"
                        role="group"
                        aria-label="Preview viewport width"
                    >
                        {WIDTHS.map((w) => {
                            const active =
                                customWidth === null && width === w.value;
                            return (
                                <button
                                    key={String(w.value)}
                                    type="button"
                                    onClick={() => {
                                        setWidth(w.value);
                                        setCustomWidth(null);
                                    }}
                                    data-testid={`appearance-preview-width-${w.value}`}
                                    className={cn(
                                        'h-7 rounded-[5px] px-2.5 text-[11px] font-medium transition-colors',
                                        active
                                            ? 'bg-card text-foreground shadow-sm'
                                            : 'text-muted-foreground hover:text-foreground',
                                    )}
                                >
                                    {w.label}
                                </button>
                            );
                        })}
                    </div>
                    {customWidth !== null && (
                        <span
                            className="text-[11px] font-medium tabular-nums text-foreground"
                            data-testid="appearance-preview-custom-width"
                        >
                            {customWidth}px
                        </span>
                    )}
                </div>
            </div>

            <div className="relative flex flex-1 min-h-0 items-stretch justify-center overflow-auto bg-muted/30 p-4">
                <ResizableFrame
                    width={effectiveWidth}
                    onCustomWidth={(w) => setCustomWidth(w)}
                >
                    {/*
                        pointer-events:none — preview is for VIEWING, not
                        interacting. Without this, clicking a Subscribe CTA
                        inside a /pricing preview would actually start a real
                        Razorpay checkout and load checkout.js + prepare.js
                        into the iframe; prepare.js's setInterval then injects
                        inline fingerprint scripts (blocked by our nonced CSP)
                        on every tick afterward, polluting the console even
                        when the preview navigates elsewhere.
                    */}
                    <iframe
                        ref={iframeRef}
                        title="Appearance live preview"
                        loading="eager"
                        data-testid="appearance-preview-iframe"
                        className="pointer-events-none block h-full w-full rounded-lg border border-border bg-card"
                    />
                </ResizableFrame>
            </div>
        </div>
    );
}

type ResizableFrameProps = {
    width: number | null;
    onCustomWidth: (next: number) => void;
    children: React.ReactNode;
};

function ResizableFrame({ width, onCustomWidth, children }: ResizableFrameProps) {
    const [dragging, setDragging] = useState(false);
    const startRef = useRef<{ x: number; w: number } | null>(null);
    const containerRef = useRef<HTMLDivElement>(null);

    useEffect(() => {
        if (!dragging) return;
        const onMove = (e: MouseEvent) => {
            const start = startRef.current;
            if (!start) return;
            const next = Math.max(
                MIN_WIDTH,
                Math.min(MAX_WIDTH, start.w + (e.clientX - start.x) * 2),
            );
            onCustomWidth(Math.round(next));
        };
        const onUp = () => setDragging(false);
        document.addEventListener('mousemove', onMove);
        document.addEventListener('mouseup', onUp);
        return () => {
            document.removeEventListener('mousemove', onMove);
            document.removeEventListener('mouseup', onUp);
        };
    }, [dragging, onCustomWidth]);

    const startDrag = (e: React.MouseEvent<HTMLDivElement>) => {
        const current = containerRef.current?.offsetWidth ?? width ?? 1024;
        startRef.current = { x: e.clientX, w: current };
        setDragging(true);
    };

    const style = width === null ? { width: '100%' } : { width: `${width}px` };

    return (
        <div
            ref={containerRef}
            className="relative flex h-full max-w-full"
            style={style}
        >
            {children}
            <div
                role="separator"
                aria-orientation="vertical"
                aria-label="Resize preview viewport"
                onMouseDown={startDrag}
                data-testid="appearance-preview-resize-handle"
                className={cn(
                    'absolute right-[-6px] top-0 bottom-0 w-3 cursor-ew-resize',
                    'flex items-center justify-center',
                )}
            >
                <span
                    className={cn(
                        'block h-10 w-1 rounded-full bg-border transition-colors',
                        dragging
                            ? 'bg-accent'
                            : 'group-hover:bg-muted-foreground',
                    )}
                />
            </div>
        </div>
    );
}
