import {
    CircleCheck, Clapperboard, FileSignature, FileText, ImageIcon, LayoutPanelTop, ListOrdered, MessageSquareQuote, Plus, ScrollText,
    Trash2, Trophy, Upload, Users, type LucideIcon,
} from 'lucide-react';
import { useRef, useState } from 'react';
import { RichTextEditor } from '@/components/RichTextEditor';
import { Button } from '@/components/ui/button';
import { Input, Select, Textarea } from '@/components/ui/input';
import { Field, FieldError } from '@/components/ui/label';
import { xsrfToken } from '@/lib/http';
import { cn } from '@/lib/utils';

/** Structured blocks (schema v2) — see app/Support/Blocks.php for the stored shape. */
export type StructuredType = 'cover' | 'video' | 'testimonial' | 'team' | 'timeline' | 'case_study' | 'terms' | 'acceptance';

export type TeamMember = { name: string | null; role: string | null; bio: string | null; photo_path: string | null; photo_url?: string | null };
export type TimelineStep = { title: string | null; when: string | null; description: string | null };
export type BlockContent = {
    subtitle?: string | null; image_path?: string | null; image_url?: string | null; url?: string | null;
    quote?: string | null; name?: string | null; role?: string | null; client?: string | null; result?: string | null;
    members?: TeamMember[]; steps?: TimelineStep[]; source?: 'company' | 'custom' | 'reviews'; display?: 'across' | 'down';
    mode?: 'statement' | 'checkbox'; signature?: 'none' | 'typed' | 'drawn'; statement?: string | null;
};
export type BlockStyle = { spacing?: 'none' | 'sm' | 'md' | 'lg'; align?: 'left' | 'center' | 'right'; background?: string | null; color?: string | null };

export const BLOCK_META: Record<StructuredType, { label: string; icon: LucideIcon; hint: string }> = {
    cover: { label: 'Cover', icon: LayoutPanelTop, hint: 'Hero image and subtitle for the cover page' },
    video: { label: 'Video', icon: Clapperboard, hint: 'YouTube, Vimeo or Loom' },
    testimonial: { label: 'Testimonial', icon: MessageSquareQuote, hint: 'A quote from a happy client' },
    team: { label: 'Team bios', icon: Users, hint: 'People working on the project' },
    timeline: { label: 'Timeline', icon: ListOrdered, hint: 'Steps or milestones' },
    case_study: { label: 'Case study', icon: Trophy, hint: 'Past work with a result' },
    terms: { label: 'T&Cs', icon: ScrollText, hint: 'Company terms or custom terms' },
    acceptance: { label: 'Acceptance', icon: FileSignature, hint: 'How the client accepts and signs' },
};
export const STRUCTURED_TYPES = Object.keys(BLOCK_META) as StructuredType[];
export const isStructured = (t: string): t is StructuredType => (STRUCTURED_TYPES as string[]).includes(t);

export function defaultContent(t: StructuredType): BlockContent {
    switch (t) {
        case 'cover': return { subtitle: null, image_path: null, image_url: null };
        case 'video': return { url: '' };
        case 'testimonial': return { quote: '', name: '', role: '' };
        case 'team': return { members: [{ name: '', role: '', bio: '', photo_path: null }] };
        case 'timeline': return { steps: [{ title: '', when: '', description: '' }, { title: '', when: '', description: '' }] };
        case 'case_study': return { client: '', result: '', image_path: null, image_url: null };
        case 'terms': return { source: 'company' };
        case 'acceptance': return { mode: 'checkbox', signature: 'typed', statement: 'I accept this quote and the terms and conditions.' };
    }
}

/** YouTube / Vimeo / Loom → embeddable player URL (or null). */
export function embedUrl(url: string | null | undefined): string | null {
    if (!url) return null;
    let m = url.match(/(?:youtube\.com\/(?:watch\?v=|embed\/|shorts\/)|youtu\.be\/)([A-Za-z0-9_-]{6,})/);
    if (m) return `https://www.youtube-nocookie.com/embed/${m[1]}`;
    m = url.match(/vimeo\.com\/(?:video\/)?(\d+)/);
    if (m) return `https://player.vimeo.com/video/${m[1]}`;
    m = url.match(/loom\.com\/(?:share|embed)\/([A-Za-z0-9]+)/);
    if (m) return `https://www.loom.com/embed/${m[1]}`;
    return null;
}
export function videoThumb(url: string | null | undefined): string | null {
    const m = url?.match(/(?:youtube\.com\/(?:watch\?v=|embed\/|shorts\/)|youtu\.be\/)([A-Za-z0-9_-]{6,})/);
    return m ? `https://img.youtube.com/vi/${m[1]}/hqdefault.jpg` : null;
}

export async function uploadImage(file: File): Promise<{ path: string; url: string; width?: number; height?: number }> {
    const body = new FormData();
    body.append('image', file);
    const res = await fetch('/api/uploads/images', {
        method: 'POST', body, credentials: 'same-origin',
        headers: { Accept: 'application/json', 'X-Requested-With': 'XMLHttpRequest', 'X-XSRF-TOKEN': xsrfToken() },
    });
    const data = await res.json().catch(() => ({}));
    if (!res.ok) throw new Error(data?.errors?.image?.[0] ?? data?.message ?? 'Upload failed.');
    return data;
}

function ImagePick({ url, onChange, label, round = false }: { url: string | null | undefined; onChange: (p: { path: string | null; url: string | null }) => void; label: string; round?: boolean }) {
    const input = useRef<HTMLInputElement>(null);
    const [busy, setBusy] = useState(false);
    const [err, setErr] = useState<string | null>(null);
    const pick = async (f: File) => {
        setBusy(true); setErr(null);
        try { const d = await uploadImage(f); onChange({ path: d.path, url: d.url }); } catch (e) { setErr(e instanceof Error ? e.message : 'Upload failed.'); } finally { setBusy(false); if (input.current) input.current.value = ''; }
    };
    return (
        <div className="flex items-center gap-3">
            <input ref={input} type="file" accept="image/jpeg,image/png,image/webp,image/gif" className="sr-only" aria-label={label}
                onChange={(e) => { const f = e.target.files?.[0]; if (f) void pick(f); }} />
            <button type="button" onClick={() => input.current?.click()} disabled={busy} title={label}
                className={cn('flex shrink-0 items-center justify-center overflow-hidden border border-dashed border-line-strong bg-stone-50 text-ink-muted hover:border-accent hover:text-link',
                    round ? 'h-14 w-14 rounded-full' : 'h-16 w-24 rounded-md')}>
                {url ? <img src={url} alt="" className="h-full w-full object-cover" /> : busy ? <span className="text-[10px]">…</span> : <Upload className="h-4 w-4" />}
            </button>
            {url && <Button type="button" size="sm" variant="ghost" onClick={() => onChange({ path: null, url: null })}>Remove</Button>}
            {err && <FieldError message={err} />}
        </div>
    );
}

type EditorProps = {
    type: StructuredType; id: string; heading: string | null; description: string | null; content: BlockContent;
    onChange: (p: { heading?: string | null; description?: string | null; content?: BlockContent }) => void; errors?: string[];
};

/** Editing UI for one structured block. */
export function StructuredBlockEditor({ type, id, heading, description, content: c, onChange }: EditorProps) {
    const set = (patch: Partial<BlockContent>) => onChange({ content: { ...c, ...patch } });
    const headingField = (placeholder: string) => (
        <Field label="Heading" htmlFor={`bh-${id}`}>
            <Input id={`bh-${id}`} placeholder={placeholder} value={heading ?? ''} onChange={(e) => onChange({ heading: e.target.value })} />
        </Field>
    );

    switch (type) {
        case 'cover':
            return (
                <div className="grid gap-3 sm:grid-cols-2">
                    <Field label="Cover subtitle" htmlFor={`cs-${id}`} hint="Shown under the title on the cover page.">
                        <Input id={`cs-${id}`} value={c.subtitle ?? ''} onChange={(e) => set({ subtitle: e.target.value })} />
                    </Field>
                    <div>
                        <div className="mb-1 text-sm font-medium">Hero image</div>
                        <ImagePick label="Upload cover image" url={c.image_url} onChange={({ path, url }) => set({ image_path: path, image_url: url })} />
                        <p className="mt-1 text-xs text-ink-muted">Suggested 1600 × 800 px.</p>
                    </div>
                </div>
            );
        case 'video': {
            const ok = !c.url || !!embedUrl(c.url);
            return (
                <div className="grid gap-3">
                    {headingField('e.g. A quick walkthrough')}
                    <Field label="Video link" htmlFor={`vu-${id}`} hint="Paste a YouTube, Vimeo or Loom link. PDFs show a thumbnail with the link." error={ok ? undefined : 'Use a YouTube, Vimeo or Loom link.'}>
                        <Input id={`vu-${id}`} placeholder="https://www.youtube.com/watch?v=…" value={c.url ?? ''} onChange={(e) => set({ url: e.target.value })} />
                    </Field>
                    {embedUrl(c.url) && (
                        <div className="aspect-video w-full max-w-md overflow-hidden rounded-md border border-line bg-black">
                            <iframe src={embedUrl(c.url)!} title="Video preview" className="h-full w-full" allow="encrypted-media; picture-in-picture" allowFullScreen />
                        </div>
                    )}
                </div>
            );
        }
        case 'testimonial':
            return (
                <div className="grid gap-3 sm:grid-cols-2">
                    <Field label="Show" htmlFor={`ts-${id}`} className="sm:col-span-2" hint={c.source === 'reviews' ? 'Shows up to 3 reviews featured in Reviews (best first). Hidden if none are featured.' : undefined}>
                        <Select id={`ts-${id}`} value={c.source === 'reviews' ? 'reviews' : 'custom'} onChange={(e) => set({ source: e.target.value === 'reviews' ? 'reviews' : undefined })} data-testid="testimonial-source">
                            <option value="custom">A testimonial written here</option>
                            <option value="reviews">Featured client reviews</option>
                        </Select>
                    </Field>
                    {c.source !== 'reviews' && <>
                    <Field label="Quote" htmlFor={`tq-${id}`} className="sm:col-span-2">
                        <Textarea id={`tq-${id}`} rows={3} value={c.quote ?? ''} onChange={(e) => set({ quote: e.target.value })} />
                    </Field>
                    <Field label="Name" htmlFor={`tn-${id}`}><Input id={`tn-${id}`} value={c.name ?? ''} onChange={(e) => set({ name: e.target.value })} /></Field>
                    <Field label="Role / company" htmlFor={`tr-${id}`}><Input id={`tr-${id}`} value={c.role ?? ''} onChange={(e) => set({ role: e.target.value })} /></Field>
                    </>}
                </div>
            );
        case 'team': {
            const members = c.members ?? [];
            const setM = (i: number, p: Partial<TeamMember>) => set({ members: members.map((m, j) => (j === i ? { ...m, ...p } : m)) });
            return (
                <div className="space-y-3">
                    {headingField('e.g. Your project team')}
                    {members.map((m, i) => (
                        <div key={i} className="grid gap-3 rounded-md border border-line p-3 sm:grid-cols-[auto_1fr_1fr_auto]">
                            <ImagePick round label={`Photo for member ${i + 1}`} url={m.photo_url} onChange={({ path, url }) => setM(i, { photo_path: path, photo_url: url })} />
                            <Input aria-label="Name" placeholder="Name" value={m.name ?? ''} onChange={(e) => setM(i, { name: e.target.value })} />
                            <Input aria-label="Role" placeholder="Role" value={m.role ?? ''} onChange={(e) => setM(i, { role: e.target.value })} />
                            <button type="button" aria-label="Remove member" className="rounded p-1.5 text-ink-muted hover:bg-stone-100" onClick={() => set({ members: members.filter((_, j) => j !== i) })}><Trash2 className="h-4 w-4" /></button>
                            <Textarea aria-label="Short bio" placeholder="Short bio" rows={2} className="sm:col-span-4" value={m.bio ?? ''} onChange={(e) => setM(i, { bio: e.target.value })} />
                        </div>
                    ))}
                    <Button type="button" size="sm" variant="secondary" onClick={() => set({ members: [...members, { name: '', role: '', bio: '', photo_path: null }] })}><Plus className="h-4 w-4" /> Add person</Button>
                </div>
            );
        }
        case 'timeline': {
            const steps = c.steps ?? [];
            const setS = (i: number, p: Partial<TimelineStep>) => set({ steps: steps.map((st, j) => (j === i ? { ...st, ...p } : st)) });
            return (
                <div className="space-y-3">
                    {headingField('e.g. Project timeline')}
                    <Field label="Display" htmlFor={`tl-display-${id}`}>
                        <Select id={`tl-display-${id}`} value={c.display ?? 'across'} onChange={(e) => set({ display: e.target.value as 'across' | 'down' })} data-testid="timeline-display">
                            <option value="across">Boxes across the page (up to 3 per row)</option>
                            <option value="down">Rows down the page (one step per row)</option>
                        </Select>
                    </Field>
                    {steps.map((st, i) => (
                        <div key={i} className="grid gap-2 rounded-md border border-line p-3 sm:grid-cols-[2rem_1fr_10rem_auto]">
                            <span className="pt-2 text-sm font-semibold text-link">{String(i + 1).padStart(2, '0')}</span>
                            <Input aria-label="Step title" placeholder="Step title" value={st.title ?? ''} onChange={(e) => setS(i, { title: e.target.value })} />
                            <Input aria-label="When" placeholder="e.g. Week 1–2" value={st.when ?? ''} onChange={(e) => setS(i, { when: e.target.value })} />
                            <button type="button" aria-label="Remove step" className="rounded p-1.5 text-ink-muted hover:bg-stone-100" onClick={() => set({ steps: steps.filter((_, j) => j !== i) })}><Trash2 className="h-4 w-4" /></button>
                            <Textarea aria-label="Step description" placeholder="What happens" rows={2} className="sm:col-start-2 sm:col-span-3" value={st.description ?? ''} onChange={(e) => setS(i, { description: e.target.value })} />
                        </div>
                    ))}
                    <Button type="button" size="sm" variant="secondary" onClick={() => set({ steps: [...steps, { title: '', when: '', description: '' }] })}><Plus className="h-4 w-4" /> Add step</Button>
                </div>
            );
        }
        case 'case_study':
            return (
                <div className="grid gap-3 sm:grid-cols-2">
                    {headingField('e.g. How we helped Harbour Dental')}
                    <Field label="Client" htmlFor={`cc-${id}`}><Input id={`cc-${id}`} value={c.client ?? ''} onChange={(e) => set({ client: e.target.value })} /></Field>
                    <Field label="Result" htmlFor={`cr-${id}`} hint="A short headline figure, e.g. +120% enquiries"><Input id={`cr-${id}`} value={c.result ?? ''} onChange={(e) => set({ result: e.target.value })} /></Field>
                    <div>
                        <div className="mb-1 text-sm font-medium">Image</div>
                        <ImagePick label="Upload case study image" url={c.image_url} onChange={({ path, url }) => set({ image_path: path, image_url: url })} />
                    </div>
                    <div className="sm:col-span-2"><RichTextEditor label="Case study" value={description ?? ''} onChange={(h) => onChange({ description: h })} /></div>
                </div>
            );
        case 'terms':
            return (
                <div className="space-y-3">
                    <Field label="Terms" htmlFor={`ts-${id}`}>
                        <Select id={`ts-${id}`} value={c.source ?? 'company'} onChange={(e) => set({ source: e.target.value as 'company' | 'custom' })}>
                            <option value="company">Use the company's T&amp;Cs (Company settings)</option>
                            <option value="custom">Custom terms for this block</option>
                        </Select>
                    </Field>
                    {c.source === 'custom' && <RichTextEditor label="Custom terms" value={description ?? ''} onChange={(h) => onChange({ description: h })} />}
                </div>
            );
        case 'acceptance':
            return (
                <div className="grid gap-3 sm:grid-cols-2">
                    <Field label="How the client accepts" htmlFor={`am-${id}`}>
                        <Select id={`am-${id}`} value={c.mode ?? 'checkbox'} onChange={(e) => set({ mode: e.target.value as 'statement' | 'checkbox' })}>
                            <option value="statement">Accepting is agreeing to the statement</option>
                            <option value="checkbox">Client must tick the statement</option>
                        </Select>
                    </Field>
                    <Field label="Signature" htmlFor={`as-${id}`}>
                        <Select id={`as-${id}`} value={c.signature ?? 'none'} onChange={(e) => set({ signature: e.target.value as 'none' | 'typed' | 'drawn' })}>
                            <option value="none">No signature</option>
                            <option value="typed">Typed signature</option>
                            <option value="drawn">Drawn signature</option>
                        </Select>
                    </Field>
                    <Field label="Statement" htmlFor={`at-${id}`} className="sm:col-span-2">
                        <Textarea id={`at-${id}`} rows={2} value={c.statement ?? ''} onChange={(e) => set({ statement: e.target.value })} />
                    </Field>
                    <p className="text-xs text-ink-muted sm:col-span-2">The client's Accept flow (Phase 3) uses these settings. The PDF shows a signature panel.</p>
                </div>
            );
    }
}

/** Per-block spacing, alignment, background and text colour overrides. */
export function StylePanel({ id, style, onChange }: { id: string; style: BlockStyle | null | undefined; onChange: (s: BlockStyle | null) => void }) {
    const s = style ?? {};
    const set = (p: Partial<BlockStyle>) => {
        const next = { ...s, ...p };
        const clean = Object.fromEntries(Object.entries(next).filter(([, v]) => v)) as BlockStyle;
        onChange(Object.keys(clean).length ? clean : null);
    };
    return (
        <div className="grid gap-3 rounded-md border border-line bg-stone-50/60 p-3 sm:grid-cols-4" data-testid={`style-${id}`}>
            <Field label="Spacing" htmlFor={`sp-${id}`}>
                <Select id={`sp-${id}`} value={s.spacing ?? ''} onChange={(e) => set({ spacing: (e.target.value || undefined) as BlockStyle['spacing'] })}>
                    <option value="">Default</option><option value="none">None</option><option value="sm">Small</option><option value="md">Medium</option><option value="lg">Large</option>
                </Select>
            </Field>
            <Field label="Alignment" htmlFor={`al-${id}`}>
                <Select id={`al-${id}`} value={s.align ?? ''} onChange={(e) => set({ align: (e.target.value || undefined) as BlockStyle['align'] })}>
                    <option value="">Default</option><option value="left">Left</option><option value="center">Centre</option><option value="right">Right</option>
                </Select>
            </Field>
            <ColourField id={`bg-${id}`} label="Background" value={s.background ?? null} onChange={(v) => set({ background: v })} />
            <ColourField id={`fg-${id}`} label="Text colour" value={s.color ?? null} onChange={(v) => set({ color: v })} />
        </div>
    );
}

function ColourField({ id, label, value, onChange }: { id: string; label: string; value: string | null; onChange: (v: string | null) => void }) {
    // Typing stays local until it's a full hex value, so autosave never sends half a colour.
    const [draft, setDraft] = useState(value ?? '');
    const [prev, setPrev] = useState(value);
    if (prev !== value) { setPrev(value); setDraft(value ?? ''); }
    return (
        <Field label={label} htmlFor={id}>
            <div className="flex items-center gap-2">
                <input type="color" aria-label={`${label} picker`} value={value ?? '#ffffff'} onChange={(e) => onChange(e.target.value.toUpperCase())} className="h-[38px] w-10 shrink-0 cursor-pointer rounded border border-line bg-surface p-1" />
                <Input id={id} placeholder="Theme" value={draft} onChange={(e) => {
                    const v = e.target.value.trim();
                    setDraft(e.target.value);
                    if (v === '') onChange(null);
                    else if (/^#[0-9A-Fa-f]{6}$/.test(v)) onChange(v.toUpperCase());
                }} />
                {value && <button type="button" className="text-xs text-link underline" onClick={() => onChange(null)}>Clear</button>}
            </div>
        </Field>
    );
}

export const BlockIcons = { FileText, ImageIcon, CircleCheck };
