import { Button } from '@/components/ui/button';
import { Input } from '@/components/ui/input';
import {
    AlignCenter,
    AlignLeft,
    AlignRight,
    Bold,
    Loader2,
    Minus,
    PenLine,
    Plus,
    Square,
    Stamp,
    Trash2,
    Type,
} from 'lucide-react';
import * as pdfjsLib from 'pdfjs-dist';
import workerUrl from 'pdfjs-dist/build/pdf.worker.min.mjs?url';
import * as React from 'react';

pdfjsLib.GlobalWorkerOptions.workerSrc = workerUrl;

export type PdfOverlay = {
    id: string;
    page: number; // 1-based
    type: 'text' | 'whiteout' | 'signature' | 'stamp';
    x: number; // normalized 0..1, top-left origin
    y: number;
    w: number;
    h: number;
    text?: string;
    fontSize?: number; // fraction of page height
    align?: 'L' | 'C' | 'R';
    color?: string;
    bold?: boolean;
};

interface Props {
    pdfUrl: string;
    value: PdfOverlay[];
    onChange: (overlays: PdfOverlay[]) => void;
    editable: boolean;
    senderSignatureUrl?: string | null;
    senderStampUrl?: string | null;
}

const CONTAINER_WIDTH = 760;

const DEFAULTS: Record<PdfOverlay['type'], Partial<PdfOverlay>> = {
    text: { w: 0.32, h: 0.04, text: 'Texte', fontSize: 0.018, align: 'L', color: '#111111' },
    whiteout: { w: 0.22, h: 0.04 },
    signature: { w: 0.22, h: 0.1 },
    stamp: { w: 0.18, h: 0.12 },
};

const uid = () => 'ov_' + Math.random().toString(36).slice(2, 10);
const clamp = (v: number, lo: number, hi: number) => Math.max(lo, Math.min(hi, v));

type PageSize = { width: number; height: number };

export function PdfOverlayEditor({ pdfUrl, value, onChange, editable, senderSignatureUrl, senderStampUrl }: Props) {
    const [pages, setPages] = React.useState<PageSize[]>([]);
    const [loading, setLoading] = React.useState(true);
    const [error, setError] = React.useState<string | null>(null);
    const [selectedId, setSelectedId] = React.useState<string | null>(null);

    const pdfDocRef = React.useRef<pdfjsLib.PDFDocumentProxy | null>(null);
    const canvasRefs = React.useRef<(HTMLCanvasElement | null)[]>([]);

    // Keep latest props reachable from the window drag listeners (no stale closure).
    const valueRef = React.useRef(value);
    valueRef.current = value;
    const pagesRef = React.useRef(pages);
    pagesRef.current = pages;
    const onChangeRef = React.useRef(onChange);
    onChangeRef.current = onChange;

    // Load the document + compute display page sizes.
    React.useEffect(() => {
        let cancelled = false;
        setLoading(true);
        setError(null);
        setPages([]);
        pdfjsLib
            .getDocument(pdfUrl)
            .promise.then(async (pdf) => {
                if (cancelled) return;
                pdfDocRef.current = pdf;
                const sizes: PageSize[] = [];
                for (let n = 1; n <= pdf.numPages; n++) {
                    const vp = (await pdf.getPage(n)).getViewport({ scale: 1 });
                    sizes.push({ width: CONTAINER_WIDTH, height: (CONTAINER_WIDTH * vp.height) / vp.width });
                }
                if (cancelled) return;
                setPages(sizes);
                setLoading(false);
            })
            .catch(() => {
                if (!cancelled) {
                    setError('Impossible de charger le PDF.');
                    setLoading(false);
                }
            });
        return () => {
            cancelled = true;
            // Release the worker-backed document (avoids a worker/memory leak
            // on URL change or unmount).
            pdfDocRef.current?.destroy();
            pdfDocRef.current = null;
        };
    }, [pdfUrl]);

    // Paint each page onto its canvas once the canvases are mounted.
    React.useEffect(() => {
        const pdf = pdfDocRef.current;
        if (!pdf || pages.length === 0) return;
        let cancelled = false;
        (async () => {
            try {
                for (let n = 1; n <= pages.length; n++) {
                    if (cancelled) return;
                    const canvas = canvasRefs.current[n - 1];
                    if (!canvas) continue;
                    const page = await pdf.getPage(n);
                    const scale = CONTAINER_WIDTH / page.getViewport({ scale: 1 }).width;
                    const viewport = page.getViewport({ scale });
                    const ctx = canvas.getContext('2d');
                    if (!ctx) continue;
                    canvas.width = viewport.width;
                    canvas.height = viewport.height;
                    await page.render({ canvasContext: ctx, viewport, canvas }).promise;
                }
            } catch {
                // Document destroyed / render cancelled mid-flight — ignore.
            }
        })();
        return () => {
            cancelled = true;
        };
    }, [pages]);

    const patch = (id: string, partial: Partial<PdfOverlay>) =>
        onChange(value.map((o) => (o.id === id ? { ...o, ...partial } : o)));

    const remove = (id: string) => {
        onChange(value.filter((o) => o.id !== id));
        setSelectedId(null);
    };

    const add = (type: PdfOverlay['type'], page: number) => {
        const id = uid();
        onChange([...value, { id, page, type, x: 0.32, y: 0.12, w: 0.3, h: 0.04, ...DEFAULTS[type] } as PdfOverlay]);
        setSelectedId(id);
    };

    // ── Drag / resize via window listeners (anchored on the original element) ──
    const drag = React.useRef<null | { id: string; pageIdx: number; mode: 'move' | 'resize'; sx: number; sy: number; orig: PdfOverlay }>(null);

    const onMove = React.useCallback((e: PointerEvent) => {
        const d = drag.current;
        if (!d) return;
        const size = pagesRef.current[d.pageIdx];
        if (!size) return;
        const dx = (e.clientX - d.sx) / size.width;
        const dy = (e.clientY - d.sy) / size.height;
        onChangeRef.current(
            valueRef.current.map((o) => {
                if (o.id !== d.id) return o;
                if (d.mode === 'move') {
                    return { ...o, x: clamp(d.orig.x + dx, 0, 1 - o.w), y: clamp(d.orig.y + dy, 0, 1 - o.h) };
                }
                return { ...o, w: clamp(d.orig.w + dx, 0.02, 1 - o.x), h: clamp(d.orig.h + dy, 0.01, 1 - o.y) };
            }),
        );
    }, []);

    const onUp = React.useCallback(() => {
        drag.current = null;
        window.removeEventListener('pointermove', onMove);
        window.removeEventListener('pointerup', onUp);
    }, [onMove]);

    // Clean up any dangling drag listeners if we unmount mid-drag.
    React.useEffect(() => {
        return () => {
            window.removeEventListener('pointermove', onMove);
            window.removeEventListener('pointerup', onUp);
        };
    }, [onMove, onUp]);

    const startDrag = (e: React.PointerEvent, o: PdfOverlay, pageIdx: number, mode: 'move' | 'resize') => {
        if (!editable) return;
        e.preventDefault();
        e.stopPropagation();
        setSelectedId(o.id);
        drag.current = { id: o.id, pageIdx, mode, sx: e.clientX, sy: e.clientY, orig: { ...o } };
        window.addEventListener('pointermove', onMove);
        window.addEventListener('pointerup', onUp);
    };

    const selected = value.find((o) => o.id === selectedId) ?? null;

    if (error) {
        return <div className="rounded-md border border-rose-200 bg-rose-50 p-4 text-sm text-rose-700">{error}</div>;
    }

    return (
        <div className="space-y-3">
            {/* Selected-element toolbar */}
            {editable && (
                <div className="sticky top-0 z-10 flex min-h-[44px] flex-wrap items-center gap-2 rounded-md border bg-background/95 p-2 text-sm backdrop-blur">
                    {!selected && <span className="text-muted-foreground">Ajoutez un élément sur une page, puis glissez-le à sa place.</span>}
                    {selected && selected.type === 'text' && (
                        <>
                            <Input
                                value={selected.text ?? ''}
                                onChange={(e) => patch(selected.id, { text: e.target.value })}
                                placeholder="Texte…"
                                className="h-8 w-56"
                            />
                            <Button type="button" size="icon" variant="outline" className="h-8 w-8" title="Réduire" onClick={() => patch(selected.id, { fontSize: clamp((selected.fontSize ?? 0.018) - 0.003, 0.006, 0.12) })}>
                                <Minus className="h-4 w-4" />
                            </Button>
                            <Button type="button" size="icon" variant="outline" className="h-8 w-8" title="Agrandir" onClick={() => patch(selected.id, { fontSize: clamp((selected.fontSize ?? 0.018) + 0.003, 0.006, 0.12) })}>
                                <Plus className="h-4 w-4" />
                            </Button>
                            <Button type="button" size="icon" variant={selected.bold ? 'default' : 'outline'} className="h-8 w-8" title="Gras" onClick={() => patch(selected.id, { bold: !selected.bold })}>
                                <Bold className="h-4 w-4" />
                            </Button>
                            {(['L', 'C', 'R'] as const).map((a) => (
                                <Button key={a} type="button" size="icon" variant={(selected.align ?? 'L') === a ? 'default' : 'outline'} className="h-8 w-8" onClick={() => patch(selected.id, { align: a })}>
                                    {a === 'L' ? <AlignLeft className="h-4 w-4" /> : a === 'C' ? <AlignCenter className="h-4 w-4" /> : <AlignRight className="h-4 w-4" />}
                                </Button>
                            ))}
                            <input type="color" value={selected.color ?? '#111111'} onChange={(e) => patch(selected.id, { color: e.target.value })} className="h-8 w-8 cursor-pointer rounded border" title="Couleur" />
                        </>
                    )}
                    {selected && (
                        <Button type="button" size="sm" variant="outline" className="ml-auto text-rose-600" onClick={() => remove(selected.id)}>
                            <Trash2 className="mr-1 h-4 w-4" /> Supprimer
                        </Button>
                    )}
                </div>
            )}

            {loading && (
                <div className="flex h-40 items-center justify-center text-sm text-muted-foreground">
                    <Loader2 className="mr-2 h-4 w-4 animate-spin" /> Chargement du PDF…
                </div>
            )}

            <div className="space-y-6 overflow-x-auto">
                {pages.map((size, idx) => {
                    const pageNum = idx + 1;
                    return (
                        <div key={pageNum} className="mx-auto" style={{ width: size.width }}>
                            <div className="mb-1 flex items-center justify-between">
                                <span className="text-xs text-muted-foreground">Page {pageNum}</span>
                                {editable && (
                                    <div className="flex items-center gap-1">
                                        <ToolBtn icon={<Type className="h-3.5 w-3.5" />} label="Texte" onClick={() => add('text', pageNum)} />
                                        <ToolBtn icon={<Square className="h-3.5 w-3.5" />} label="Masquer" onClick={() => add('whiteout', pageNum)} />
                                        <ToolBtn icon={<PenLine className="h-3.5 w-3.5" />} label="Signature" onClick={() => add('signature', pageNum)} />
                                        <ToolBtn icon={<Stamp className="h-3.5 w-3.5" />} label="Cachet" onClick={() => add('stamp', pageNum)} />
                                    </div>
                                )}
                            </div>
                            <div
                                className="relative border bg-white shadow-sm"
                                style={{ width: size.width, height: size.height }}
                                onPointerDown={() => setSelectedId(null)}
                            >
                                <canvas
                                    ref={(el) => {
                                        canvasRefs.current[idx] = el;
                                    }}
                                    className="block h-full w-full"
                                />
                                {value
                                    .filter((o) => o.page === pageNum)
                                    .map((o) => (
                                        <OverlayBox
                                            key={o.id}
                                            overlay={o}
                                            size={size}
                                            editable={editable}
                                            selected={selectedId === o.id}
                                            senderSignatureUrl={senderSignatureUrl}
                                            senderStampUrl={senderStampUrl}
                                            onPointerDownBody={(e) => startDrag(e, o, idx, 'move')}
                                            onPointerDownResize={(e) => startDrag(e, o, idx, 'resize')}
                                        />
                                    ))}
                            </div>
                        </div>
                    );
                })}
            </div>
        </div>
    );
}

function ToolBtn({ icon, label, onClick }: { icon: React.ReactNode; label: string; onClick: () => void }) {
    return (
        <Button type="button" size="sm" variant="outline" className="h-7 gap-1 px-2 text-xs" onClick={onClick}>
            {icon}
            {label}
        </Button>
    );
}

function OverlayBox({
    overlay: o,
    size,
    editable,
    selected,
    senderSignatureUrl,
    senderStampUrl,
    onPointerDownBody,
    onPointerDownResize,
}: {
    overlay: PdfOverlay;
    size: PageSize;
    editable: boolean;
    selected: boolean;
    senderSignatureUrl?: string | null;
    senderStampUrl?: string | null;
    onPointerDownBody: (e: React.PointerEvent) => void;
    onPointerDownResize: (e: React.PointerEvent) => void;
}) {
    const style: React.CSSProperties = {
        position: 'absolute',
        left: o.x * size.width,
        top: o.y * size.height,
        width: o.w * size.width,
        // Text grows downward with its content (matches the bake's MultiCell);
        // the stored height is only a minimum for the selection frame.
        height: o.type === 'text' ? 'auto' : o.h * size.height,
        minHeight: o.type === 'text' ? o.h * size.height : undefined,
    };

    let body: React.ReactNode = null;
    if (o.type === 'whiteout') {
        body = <div className="h-full w-full bg-white" />;
    } else if (o.type === 'text') {
        body = (
            <div
                className="w-full whitespace-pre-wrap leading-tight"
                style={{
                    fontSize: (o.fontSize ?? 0.018) * size.height,
                    color: o.color ?? '#111111',
                    textAlign: o.align === 'C' ? 'center' : o.align === 'R' ? 'right' : 'left',
                    fontWeight: o.bold ? 700 : 400,
                }}
            >
                {o.text || (editable ? 'Texte' : '')}
            </div>
        );
    } else {
        const url = o.type === 'signature' ? senderSignatureUrl : senderStampUrl || '/admin-sign/sign-admin.png';
        body = url ? (
            <img src={url} alt={o.type} className="h-full w-full object-contain" draggable={false} />
        ) : (
            <div className="flex h-full w-full items-center justify-center rounded border border-dashed border-slate-400 text-[11px] text-slate-400">
                {o.type === 'signature' ? 'Signature' : 'Cachet'}
            </div>
        );
    }

    return (
        <div
            style={style}
            onPointerDown={editable ? onPointerDownBody : undefined}
            className={`${editable ? 'cursor-move' : ''} ${selected ? 'outline outline-2 outline-primary' : o.type === 'text' ? 'hover:outline hover:outline-1 hover:outline-slate-300' : ''}`}
        >
            {body}
            {editable && selected && (
                <span
                    onPointerDown={onPointerDownResize}
                    className="absolute -bottom-1.5 -right-1.5 h-3 w-3 cursor-se-resize rounded-full border-2 border-white bg-primary"
                />
            )}
        </div>
    );
}
