import { Color, TextStyle } from '@tiptap/extension-text-style';
import Image from '@tiptap/extension-image';
import Link from '@tiptap/extension-link';
import TextAlign from '@tiptap/extension-text-align';
import { EditorContent, useEditor, type Editor } from '@tiptap/react';
import StarterKit from '@tiptap/starter-kit';
import {
    AlignCenter, AlignLeft, AlignRight, Bold, Braces, ImagePlus, Italic, Link2, List, ListOrdered, Loader2, Minus, Palette, Quote, Redo2, Strikethrough, TicketCheck, Underline, Undo2,
} from 'lucide-react';
import { useEffect, useRef, useState } from 'react';
import { uploadImage } from '@/components/quote/BlockEditors';
import { cn } from '@/lib/utils';

const TEXT_COLOURS = ['#111827', '#383838', '#6b7280', '#b91c1c', '#c2410c', '#15803d', '#1d4ed8', '#7e22ce'];
const IMG_SIZES = [{ label: 'Small', w: 176 }, { label: 'Half', w: 264 }, { label: 'Full', w: null }] as const;

/**
 * Rich-text email message: headings, bold / italic / underline / strike, colour, alignment, lists,
 * quotes, dividers, links, images (uploaded, then shown inside the email), placeholders and the quote card.
 * HTML is sanitised and styled for email clients server-side (App\Support\EmailHtml).
 */
export function EmailEditor({ value, onChange, id, label, placeholders, brandColours = [], disabled = false, minHeight = 'min-h-56' }: {
    value: string; onChange: (html: string) => void; id: string; label: string; placeholders?: Record<string, string>;
    brandColours?: string[]; disabled?: boolean; minHeight?: string;
}) {
    const [busy, setBusy] = useState(false);
    const [err, setErr] = useState<string | null>(null);
    const [menu, setMenu] = useState<'colour' | 'tags' | null>(null);
    const file = useRef<HTMLInputElement>(null);
    const last = useRef(value);

    const editor = useEditor({
        extensions: [
            StarterKit.configure({ link: false, heading: { levels: [2, 3] }, code: false, codeBlock: false }),
            Link.configure({ openOnClick: false, autolink: true }),
            Image.configure({
                inline: false, allowBase64: false,
                // Drag a corner to resize; the image keeps its proportions.
                resize: { enabled: true, directions: ['top-left', 'top-right', 'bottom-left', 'bottom-right'], minWidth: 60, minHeight: 30, alwaysPreserveAspectRatio: true },
            }),
            TextStyle, Color,
            TextAlign.configure({ types: ['heading', 'paragraph'] }),
        ],
        content: value || '',
        editable: !disabled,
        immediatelyRender: false,
        editorProps: { attributes: { class: cn('prose-app email-prose px-3 py-2 text-sm', minHeight), 'aria-label': label, id, 'data-testid': `${id}-editor` } },
        onUpdate: ({ editor }) => { const html = editor.isEmpty ? '' : editor.getHTML(); last.current = html; onChange(html); },
    });

    // Replace the content when it changes from outside (template picked, reset to default).
    useEffect(() => {
        if (editor && value !== last.current) { last.current = value; editor.commands.setContent(value || '', { emitUpdate: false }); }
    }, [editor, value]);
    useEffect(() => { editor?.setEditable(!disabled); }, [editor, disabled]);

    const insertImage = async (f: File) => {
        if (!editor) return;
        setBusy(true); setErr(null);
        try {
            const d = await uploadImage(f);
            editor.chain().focus().setImage({ src: d.url, alt: '' }).run();
        } catch (e) {
            setErr(e instanceof Error ? e.message : 'Upload failed.');
        } finally {
            setBusy(false);
            if (file.current) file.current.value = '';
        }
    };

    if (!editor) return <div className={cn('rounded-md border border-line-strong bg-surface', minHeight)} />;

    const b = (active: boolean, onClick: () => void, icon: React.ReactNode, title: string, testId?: string) => (
        <button type="button" title={title} aria-label={title} aria-pressed={active} disabled={disabled} onClick={onClick} data-testid={testId}
            className={cn('rounded p-1.5 text-ink hover:bg-stone-100 disabled:opacity-40', active && 'bg-stone-200')}>{icon}</button>
    );
    const sep = <span className="mx-0.5 h-5 w-px self-center bg-line" aria-hidden />;
    const block = editor.isActive('heading', { level: 2 }) ? 'h2' : editor.isActive('heading', { level: 3 }) ? 'h3' : 'p';
    const imgActive = editor.isActive('image');

    return (
        <div className={cn('rounded-md border border-line-strong bg-surface focus-within:border-accent', disabled && 'bg-stone-50')}>
            {!disabled && (
                <div className="sticky top-0 z-10 flex flex-wrap items-center gap-0.5 rounded-t-md border-b border-line bg-surface px-1 py-1" role="toolbar" aria-label={`${label} formatting`}>
                    {b(false, () => editor.chain().focus().undo().run(), <Undo2 className="h-4 w-4" />, 'Undo')}
                    {b(false, () => editor.chain().focus().redo().run(), <Redo2 className="h-4 w-4" />, 'Redo')}
                    {sep}
                    <select aria-label="Text style" value={block} disabled={disabled} className="h-7 rounded border-0 bg-transparent px-1 text-xs hover:bg-stone-100"
                        onChange={(e) => { const v = e.target.value; const c = editor.chain().focus(); (v === 'p' ? c.setParagraph() : c.setHeading({ level: v === 'h2' ? 2 : 3 })).run(); }}>
                        <option value="p">Paragraph</option><option value="h2">Heading</option><option value="h3">Subheading</option>
                    </select>
                    {b(editor.isActive('bold'), () => editor.chain().focus().toggleBold().run(), <Bold className="h-4 w-4" />, 'Bold')}
                    {b(editor.isActive('italic'), () => editor.chain().focus().toggleItalic().run(), <Italic className="h-4 w-4" />, 'Italic')}
                    {b(editor.isActive('underline'), () => editor.chain().focus().toggleUnderline().run(), <Underline className="h-4 w-4" />, 'Underline')}
                    {b(editor.isActive('strike'), () => editor.chain().focus().toggleStrike().run(), <Strikethrough className="h-4 w-4" />, 'Strikethrough')}
                    <span className="relative">
                        {b(menu === 'colour', () => setMenu(menu === 'colour' ? null : 'colour'), <Palette className="h-4 w-4" style={{ color: editor.getAttributes('textStyle').color }} />, 'Text colour')}
                        {menu === 'colour' && (
                            <div className="motion-pop-up absolute left-0 top-9 z-20 w-48 rounded-md border border-line bg-surface p-2 shadow-pop" onMouseLeave={() => setMenu(null)}>
                                <div className="grid grid-cols-6 gap-1.5">
                                    {[...new Set([...brandColours, ...TEXT_COLOURS])].map((c) => (
                                        <button key={c} type="button" title={c} aria-label={`Colour ${c}`} className="h-6 w-6 rounded-full border border-black/10" style={{ background: c }}
                                            onClick={() => { editor.chain().focus().setColor(c).run(); setMenu(null); }} />
                                    ))}
                                </div>
                                <button type="button" className="mt-2 w-full rounded px-2 py-1 text-left text-xs hover:bg-stone-100" onClick={() => { editor.chain().focus().unsetColor().run(); setMenu(null); }}>Default colour</button>
                            </div>
                        )}
                    </span>
                    {sep}
                    {b(editor.isActive({ textAlign: 'left' }), () => editor.chain().focus().setTextAlign('left').run(), <AlignLeft className="h-4 w-4" />, 'Align left')}
                    {b(editor.isActive({ textAlign: 'center' }), () => editor.chain().focus().setTextAlign('center').run(), <AlignCenter className="h-4 w-4" />, 'Centre')}
                    {b(editor.isActive({ textAlign: 'right' }), () => editor.chain().focus().setTextAlign('right').run(), <AlignRight className="h-4 w-4" />, 'Align right')}
                    {sep}
                    {b(editor.isActive('bulletList'), () => editor.chain().focus().toggleBulletList().run(), <List className="h-4 w-4" />, 'Bullet list')}
                    {b(editor.isActive('orderedList'), () => editor.chain().focus().toggleOrderedList().run(), <ListOrdered className="h-4 w-4" />, 'Numbered list')}
                    {b(editor.isActive('blockquote'), () => editor.chain().focus().toggleBlockquote().run(), <Quote className="h-4 w-4" />, 'Quote')}
                    {b(false, () => editor.chain().focus().setHorizontalRule().run(), <Minus className="h-4 w-4" />, 'Divider')}
                    {b(editor.isActive('link'), () => setLink(editor), <Link2 className="h-4 w-4" />, 'Link')}
                    {sep}
                    <input ref={file} type="file" accept="image/jpeg,image/png,image/gif,image/webp" className="sr-only" aria-label="Image to insert"
                        data-testid={`${id}-image-input`} onChange={(e) => { const f = e.target.files?.[0]; if (f) void insertImage(f); }} />
                    <button type="button" onClick={() => file.current?.click()} disabled={busy || disabled} title="Insert image" data-testid={`${id}-insert-image`}
                        className="inline-flex items-center gap-1 rounded px-1.5 py-1 text-xs hover:bg-stone-100">
                        {busy ? <Loader2 className="h-4 w-4 animate-spin" /> : <ImagePlus className="h-4 w-4" />} Image
                    </button>
                    <button type="button" title="Insert the quote card with its “View quote” button" disabled={disabled}
                        onClick={() => editor.chain().focus().insertContent('<p>{quote_link}</p>').run()}
                        className="inline-flex items-center gap-1 rounded px-1.5 py-1 text-xs hover:bg-stone-100" data-testid={`${id}-insert-quote-link`}>
                        <TicketCheck className="h-4 w-4" /> Quote button
                    </button>
                    {placeholders && Object.keys(placeholders).length > 0 && (
                        <span className="relative">
                            <button type="button" onClick={() => setMenu(menu === 'tags' ? null : 'tags')} aria-expanded={menu === 'tags'} className="inline-flex items-center gap-1 rounded px-1.5 py-1 text-xs hover:bg-stone-100">
                                <Braces className="h-4 w-4" /> Placeholder
                            </button>
                            {menu === 'tags' && (
                                <div className="motion-pop-up absolute right-0 top-9 z-20 max-h-72 w-72 overflow-y-auto rounded-md border border-line bg-surface py-1 shadow-pop" onMouseLeave={() => setMenu(null)}>
                                    {Object.entries(placeholders).filter(([k]) => k !== '{quote_link}').map(([k, v]) => (
                                        <button key={k} type="button" className="block w-full px-3 py-1.5 text-left text-xs hover:bg-stone-100"
                                            onClick={() => { editor.chain().focus().insertContent(k).run(); setMenu(null); }}>
                                            <code className="text-link">{k}</code> <span className="text-ink-muted">{v}</span>
                                        </button>
                                    ))}
                                </div>
                            )}
                        </span>
                    )}
                </div>
            )}
            {imgActive && !disabled && (
                <div className="flex items-center gap-1 border-b border-line bg-stone-50 px-2 py-1 text-xs" data-testid={`${id}-image-size`}>
                    <span className="mr-1 text-ink-muted">Image size <span className="hidden sm:inline">(or drag a corner)</span></span>
                    {IMG_SIZES.map((s) => (
                        <button key={s.label} type="button" className={cn('rounded px-2 py-0.5 hover:bg-stone-200', String(editor.getAttributes('image').width ?? '') === String(s.w ?? '') && 'bg-stone-200 font-medium')}
                            onClick={() => editor.chain().focus().updateAttributes('image', { width: s.w, height: null }).run()}>{s.label}</button>
                    ))}
                    <button type="button" className="ml-auto rounded px-2 py-0.5 text-red-700 hover:bg-red-50" onClick={() => editor.chain().focus().deleteSelection().run()}>Remove</button>
                </div>
            )}
            <EditorContent editor={editor} />
            {err && <p role="alert" className="border-t border-line px-3 py-1.5 text-xs text-red-700">{err}</p>}
        </div>
    );
}

function setLink(editor: Editor) {
    const url = window.prompt('Link address (https://…, mailto: or tel:)', editor.getAttributes('link').href ?? 'https://');
    if (url === null) return;
    if (url.trim() === '') editor.chain().focus().extendMarkRange('link').unsetLink().run();
    else editor.chain().focus().extendMarkRange('link').setLink({ href: url.trim() }).run();
}
