import Image from '@tiptap/extension-image';
import Link from '@tiptap/extension-link';
import Placeholder from '@tiptap/extension-placeholder';
import TextAlign from '@tiptap/extension-text-align';
import Underline from '@tiptap/extension-underline';
import { EditorContent, useEditor } from '@tiptap/react';
import StarterKit from '@tiptap/starter-kit';
import {
    AlignCenter,
    AlignLeft,
    AlignRight,
    Bold,
    Heading2,
    Heading3,
    ImagePlus,
    Italic,
    Link as LinkIcon,
    List,
    ListOrdered,
    Loader2,
    Redo2,
    Underline as UnderlineIcon,
    Undo2,
    X,
} from 'lucide-react';
import { useCallback, useEffect, useRef, useState } from 'react';
import { toast } from 'sonner';
import { richTextContentClass } from '@/lib/rich-text-content';
import { cn } from '@/lib/utils';
import { store as uploadMedia } from '@/routes/media/uploads';

type MediaItem = {
    id: number;
    name: string;
    file_name: string;
    mime_type: string;
    size: number;
    url: string;
};

type TiptapEditorProps = {
    value?: string;
    onChange?: (value: string) => void;
    name?: string;
    placeholder?: string;
    collection?: string;
    error?: string;
};

function ToolbarButton({
    onClick,
    isActive = false,
    disabled = false,
    children,
    title,
}: {
    onClick: () => void;
    isActive?: boolean;
    disabled?: boolean;
    children: React.ReactNode;
    title?: string;
}) {
    return (
        <button
            type="button"
            onClick={onClick}
            disabled={disabled}
            title={title}
            className={cn(
                'inline-flex h-8 w-8 items-center justify-center rounded-md text-sm font-medium transition-colors',
                'hover:bg-muted hover:text-muted-foreground',
                'disabled:pointer-events-none disabled:opacity-50',
                isActive && 'bg-accent text-accent-foreground',
            )}
        >
            {children}
        </button>
    );
}

function ToolbarDivider() {
    return <div className="h-6 w-px bg-border" />;
}

export default function TiptapEditor({
    value = '',
    onChange,
    name = 'content',
    placeholder = 'Tulis konten di sini...',
    collection = 'content',
    error,
}: TiptapEditorProps) {
    const fileInputRef = useRef<HTMLInputElement>(null);
    const [uploading, setUploading] = useState(false);
    const [showLinkInput, setShowLinkInput] = useState(false);
    const [linkUrl, setLinkUrl] = useState('');

    const editor = useEditor({
        extensions: [
            StarterKit.configure({
                heading: {
                    levels: [2, 3],
                },
            }),
            Image.configure({
                inline: false,
                allowBase64: true,
            }),
            Placeholder.configure({
                placeholder,
            }),
            Underline,
            TextAlign.configure({
                types: ['heading', 'paragraph'],
            }),
            Link.configure({
                openOnClick: false,
                HTMLAttributes: {
                    class: 'text-primary underline cursor-pointer',
                },
            }),
        ],
        content: value,
        onUpdate: ({ editor: e }) => {
            onChange?.(e.getHTML());
        },
        editorProps: {
            attributes: {
                class: cn(
                    'prose prose-sm sm:prose-base max-w-none',
                    'min-h-[300px] w-full rounded-b-md px-3 py-2',
                    'focus:outline-none',
                    richTextContentClass,
                    'placeholder:text-muted-foreground',
                ),
            },
        },
    });

    useEffect(() => {
        if (editor && value !== editor.getHTML()) {
            editor.commands.setContent(value);
        }
    }, [value, editor]);

    const getCsrfToken = () =>
        document
            .querySelector('meta[name="csrf-token"]')
            ?.getAttribute('content') ?? '';

    const handleImageUpload = useCallback(
        async (file: File) => {
            if (!editor) {
                return;
            }

            setUploading(true);

            try {
                const formData = new FormData();
                formData.append('file', file);
                formData.append('collection', collection);

                const res = await fetch(uploadMedia.url(), {
                    method: 'POST',
                    headers: {
                        'X-CSRF-TOKEN': getCsrfToken(),
                        Accept: 'application/json',
                    },
                    body: formData,
                });

                if (!res.ok) {
                    const body = await res.json().catch(() => null);
                    const message =
                        body?.errors?.file?.[0] ??
                        body?.message ??
                        'Gagal mengunggah gambar.';

                    throw new Error(message);
                }

                const media: MediaItem = await res.json();

                editor
                    .chain()
                    .focus()
                    .setImage({ src: media.url, alt: file.name })
                    .run();
            } catch (err) {
                const message =
                    err instanceof Error
                        ? err.message
                        : 'Gagal mengunggah gambar.';

                toast.error(message);
                console.error('Image upload error:', err);
            } finally {
                setUploading(false);
            }
        },
        [editor, collection],
    );

    const handleFileSelect = useCallback(
        (e: React.ChangeEvent<HTMLInputElement>) => {
            const file = e.target.files?.[0];

            if (file) {
                handleImageUpload(file);
            }

            if (fileInputRef.current) {
                fileInputRef.current.value = '';
            }
        },
        [handleImageUpload],
    );

    const handleSetLink = useCallback(() => {
        if (!editor) {
            return;
        }

        if (linkUrl) {
            editor
                .chain()
                .focus()
                .extendMarkRange('link')
                .setLink({ href: linkUrl })
                .run();
        } else {
            editor.chain().focus().extendMarkRange('link').unsetLink().run();
        }

        setShowLinkInput(false);
        setLinkUrl('');
    }, [editor, linkUrl]);

    if (!editor) {
        return null;
    }

    return (
        <div className="grid gap-2">
            <div className="rounded-md border border-input">
                <div className="flex flex-wrap items-center gap-0.5 border-b border-border p-1">
                    <ToolbarButton
                        onClick={() =>
                            editor.chain().focus().toggleBold().run()
                        }
                        isActive={editor.isActive('bold')}
                        title="Bold"
                    >
                        <Bold className="h-4 w-4" />
                    </ToolbarButton>

                    <ToolbarButton
                        onClick={() =>
                            editor.chain().focus().toggleItalic().run()
                        }
                        isActive={editor.isActive('italic')}
                        title="Italic"
                    >
                        <Italic className="h-4 w-4" />
                    </ToolbarButton>

                    <ToolbarButton
                        onClick={() =>
                            editor.chain().focus().toggleUnderline().run()
                        }
                        isActive={editor.isActive('underline')}
                        title="Underline"
                    >
                        <UnderlineIcon className="h-4 w-4" />
                    </ToolbarButton>

                    <ToolbarDivider />

                    <ToolbarButton
                        onClick={() =>
                            editor
                                .chain()
                                .focus()
                                .toggleHeading({ level: 2 })
                                .run()
                        }
                        isActive={editor.isActive('heading', { level: 2 })}
                        title="Heading 2"
                    >
                        <Heading2 className="h-4 w-4" />
                    </ToolbarButton>

                    <ToolbarButton
                        onClick={() =>
                            editor
                                .chain()
                                .focus()
                                .toggleHeading({ level: 3 })
                                .run()
                        }
                        isActive={editor.isActive('heading', { level: 3 })}
                        title="Heading 3"
                    >
                        <Heading3 className="h-4 w-4" />
                    </ToolbarButton>

                    <ToolbarDivider />

                    <ToolbarButton
                        onClick={() =>
                            editor.chain().focus().toggleBulletList().run()
                        }
                        isActive={editor.isActive('bulletList')}
                        title="Bullet List"
                    >
                        <List className="h-4 w-4" />
                    </ToolbarButton>

                    <ToolbarButton
                        onClick={() =>
                            editor.chain().focus().toggleOrderedList().run()
                        }
                        isActive={editor.isActive('orderedList')}
                        title="Ordered List"
                    >
                        <ListOrdered className="h-4 w-4" />
                    </ToolbarButton>

                    <ToolbarDivider />

                    <ToolbarButton
                        onClick={() =>
                            editor.chain().focus().setTextAlign('left').run()
                        }
                        isActive={editor.isActive({ textAlign: 'left' })}
                        title="Align Left"
                    >
                        <AlignLeft className="h-4 w-4" />
                    </ToolbarButton>

                    <ToolbarButton
                        onClick={() =>
                            editor.chain().focus().setTextAlign('center').run()
                        }
                        isActive={editor.isActive({ textAlign: 'center' })}
                        title="Align Center"
                    >
                        <AlignCenter className="h-4 w-4" />
                    </ToolbarButton>

                    <ToolbarButton
                        onClick={() =>
                            editor.chain().focus().setTextAlign('right').run()
                        }
                        isActive={editor.isActive({ textAlign: 'right' })}
                        title="Align Right"
                    >
                        <AlignRight className="h-4 w-4" />
                    </ToolbarButton>

                    <ToolbarDivider />

                    <ToolbarButton
                        onClick={() => {
                            if (editor.isActive('link')) {
                                editor.chain().focus().unsetLink().run();
                            } else {
                                setShowLinkInput(true);
                            }
                        }}
                        isActive={editor.isActive('link')}
                        title="Insert Link"
                    >
                        <LinkIcon className="h-4 w-4" />
                    </ToolbarButton>

                    <ToolbarButton
                        onClick={() => fileInputRef.current?.click()}
                        disabled={uploading}
                        title="Insert Image"
                    >
                        {uploading ? (
                            <Loader2 className="h-4 w-4 animate-spin" />
                        ) : (
                            <ImagePlus className="h-4 w-4" />
                        )}
                    </ToolbarButton>

                    <div className="ml-auto flex items-center gap-0.5">
                        <ToolbarButton
                            onClick={() => editor.chain().focus().undo().run()}
                            disabled={!editor.can().undo()}
                            title="Undo"
                        >
                            <Undo2 className="h-4 w-4" />
                        </ToolbarButton>

                        <ToolbarButton
                            onClick={() => editor.chain().focus().redo().run()}
                            disabled={!editor.can().redo()}
                            title="Redo"
                        >
                            <Redo2 className="h-4 w-4" />
                        </ToolbarButton>
                    </div>
                </div>

                {showLinkInput && (
                    <div className="flex items-center gap-2 border-b border-border px-3 py-2">
                        <LinkIcon className="h-4 w-4 shrink-0 text-muted-foreground" />
                        <input
                            type="url"
                            value={linkUrl}
                            onChange={(e) => setLinkUrl(e.target.value)}
                            placeholder="https://example.com"
                            className="flex-1 rounded-md border border-input bg-transparent px-2 py-1 text-sm outline-none focus:border-ring focus:ring-1 focus:ring-ring"
                            onKeyDown={(e) => {
                                if (e.key === 'Enter') {
                                    e.preventDefault();
                                    handleSetLink();
                                }

                                if (e.key === 'Escape') {
                                    setShowLinkInput(false);
                                    setLinkUrl('');
                                }
                            }}
                            autoFocus
                        />
                        <button
                            type="button"
                            onClick={handleSetLink}
                            className="rounded-md bg-primary px-2 py-1 text-xs text-primary-foreground hover:bg-primary/90"
                        >
                            Set
                        </button>
                        <button
                            type="button"
                            onClick={() => {
                                setShowLinkInput(false);
                                setLinkUrl('');
                            }}
                            className="rounded-md px-2 py-1 text-xs text-muted-foreground hover:bg-muted"
                        >
                            <X className="h-3 w-3" />
                        </button>
                    </div>
                )}

                <EditorContent editor={editor} />
            </div>

            <input
                ref={fileInputRef}
                type="file"
                accept="image/*"
                onChange={handleFileSelect}
                className="hidden"
            />

            <input type="hidden" name={name} value={editor.getHTML()} />

            {error && <p className="text-sm text-destructive">{error}</p>}
        </div>
    );
}
