import * as React from "react";
import { Suspense, useCallback, useEffect, useRef, useState } from "react";
import { AutoFocusPlugin } from "@lexical/react/LexicalAutoFocusPlugin";

import { useLexicalComposerContext } from "@lexical/react/LexicalComposerContext";
import { LexicalErrorBoundary } from "@lexical/react/LexicalErrorBoundary";
import { HistoryPlugin } from "@lexical/react/LexicalHistoryPlugin";
import { LexicalNestedComposer } from "@lexical/react/LexicalNestedComposer";
import { RichTextPlugin } from "@lexical/react/LexicalRichTextPlugin";
import { OnChangePlugin } from "@lexical/react/LexicalOnChangePlugin";
import { useLexicalEditable } from "@lexical/react/useLexicalEditable";
import { useLexicalNodeSelection } from "@lexical/react/useLexicalNodeSelection";
import { mergeRegister } from "@lexical/utils";
import type {
    BaseSelection,
    LexicalCommand,
    LexicalEditor,
    NodeKey,
} from "lexical";
import {
    $getNodeByKey,
    $getSelection,
    $isNodeSelection,
    $isRangeSelection,
    $setSelection,
    CLICK_COMMAND,
    COMMAND_PRIORITY_LOW,
    createCommand,
    DRAGSTART_COMMAND,
    KEY_BACKSPACE_COMMAND,
    KEY_DELETE_COMMAND,
    KEY_ENTER_COMMAND,
    KEY_ESCAPE_COMMAND,
    ParagraphNode,
    RootNode,
    SELECTION_CHANGE_COMMAND,
    TextNode,
} from "lexical";

import { ContentEditable } from "@/components/editor/editor-ui/content-editable";
import { ImageResizer } from "@/components/editor/editor-ui/image-resizer";
import { $isImageNode } from "@/components/editor/nodes/image-node";

const imageCache = new Set<string>();

export const RIGHT_CLICK_IMAGE_COMMAND: LexicalCommand<MouseEvent> =
    createCommand("RIGHT_CLICK_IMAGE_COMMAND");

function useSuspenseImage(src: string) {
    if (!imageCache.has(src)) {
        throw new Promise((resolve) => {
            const img = new Image();
            img.src = src;
            img.onload = () => {
                imageCache.add(src);
                resolve(null);
            };
            img.onerror = () => {
                imageCache.add(src);
                resolve(null);
            };
        });
    }
}

function LazyImage({
    altText,
    className,
    imageRef,
    src,
    width,
    height,
    maxWidth,
    onError,
}: {
    altText: string;
    className: string | null;
    height: "inherit" | number;
    imageRef: { current: null | HTMLImageElement };
    maxWidth: number;
    src: string;
    width: "inherit" | number;
    onError: () => void;
}): React.ReactElement {
    useSuspenseImage(src);
    return (
        <img
            className={className || undefined}
            src={src}
            alt={altText}
            ref={imageRef}
            style={{
                height,
                maxWidth,
                width,
            }}
            onError={onError}
            draggable="false"
        />
    );
}

function BrokenImage(): React.ReactElement {
    return (
        <img
            src={""}
            style={{
                height: 200,
                opacity: 0.2,
                width: 200,
            }}
            draggable="false"
        />
    );
}


function readImageAlign(editor: LexicalEditor, nodeKey: NodeKey) {
    return editor.getEditorState().read(() => {
        const node: any = $getNodeByKey(nodeKey);
        if (!node) return null;

        // نسخه‌های مختلف ممکن است یکی از این‌ها را داشته باشند
        if (typeof node.getFormatType === "function")
            return node.getFormatType();
        if (typeof node.getFormat === "function") {
            const f = node.getFormat();
            if (typeof f === "string") return f;
        }
        if (typeof node.getAlignment === "function") return node.getAlignment();
        if (typeof node.getAlign === "function") return node.getAlign();

        return null;
    }) as "left" | "center" | "right" | "justify" | null;
}

export default function ImageComponent({
    src,
    altText,
    nodeKey,
    width,
    height,
    maxWidth,
    resizable,
    showCaption,
    caption,
    captionsEnabled,
}: {
    altText: string;
    caption: LexicalEditor;
    height: "inherit" | number;
    maxWidth: number;
    nodeKey: NodeKey;
    resizable: boolean;
    showCaption: boolean;
    src: string;
    width: "inherit" | number;
    captionsEnabled: boolean;
}): React.ReactElement {
    const imageRef = useRef<null | HTMLImageElement>(null);
    const buttonRef = useRef<HTMLButtonElement | null>(null);
    const [isSelected, setSelected, clearSelection] =
        useLexicalNodeSelection(nodeKey);
    const [isResizing, setIsResizing] = useState<boolean>(false);

    const [editor] = useLexicalComposerContext();
    const [selection, setSelection] = useState<BaseSelection | null>(null);
    const activeEditorRef = useRef<LexicalEditor | null>(null);
    const [isLoadError, setIsLoadError] = useState<boolean>(false);
    const isEditable = useLexicalEditable();

    // ✅ Align تصویر
    const [imgAlign, setImgAlign] = useState<
        "left" | "center" | "right" | "justify" | null
    >(null);

    const $onDelete = useCallback(
        (payload: KeyboardEvent) => {
            const deleteSelection = $getSelection();
            if (isSelected && $isNodeSelection(deleteSelection)) {
                const event: KeyboardEvent = payload;
                event.preventDefault();
                editor.update(() => {
                    deleteSelection.getNodes().forEach((node) => {
                        if ($isImageNode(node)) node.remove();
                    });
                });
            }
            return false;
        },
        [editor, isSelected]
    );

    const $onEnter = useCallback(
        (event: KeyboardEvent) => {
            const latestSelection = $getSelection();
            const buttonElem = buttonRef.current;
            if (
                isSelected &&
                $isNodeSelection(latestSelection) &&
                latestSelection.getNodes().length === 1
            ) {
                if (showCaption) {
                    $setSelection(null);
                    event.preventDefault();
                    caption.focus();
                    return true;
                } else if (
                    buttonElem !== null &&
                    buttonElem !== document.activeElement
                ) {
                    event.preventDefault();
                    buttonElem.focus();
                    return true;
                }
            }
            return false;
        },
        [caption, isSelected, showCaption]
    );

    const $onEscape = useCallback(
        (event: KeyboardEvent) => {
            if (
                activeEditorRef.current === caption ||
                buttonRef.current === event.target
            ) {
                $setSelection(null);
                editor.update(() => {
                    setSelected(true);
                    const parentRootElement = editor.getRootElement();
                    parentRootElement?.focus();
                });
                return true;
            }
            return false;
        },
        [caption, editor, setSelected]
    );

    const onClick = useCallback(
        (payload: MouseEvent) => {
            const event = payload;

            if (isResizing) return true;

            if (event.target === imageRef.current) {
                if (event.shiftKey) {
                    setSelected(!isSelected);
                } else {
                    clearSelection();
                    setSelected(true);
                }
                return true;
            }

            return false;
        },
        [isResizing, isSelected, setSelected, clearSelection]
    );

    const onRightClick = useCallback(
        (event: MouseEvent): void => {
            editor.getEditorState().read(() => {
                const latestSelection = $getSelection();
                const domElement = event.target as HTMLElement;
                if (
                    domElement.tagName === "IMG" &&
                    $isRangeSelection(latestSelection) &&
                    latestSelection.getNodes().length === 1
                ) {
                    editor.dispatchCommand(
                        RIGHT_CLICK_IMAGE_COMMAND,
                        event as MouseEvent
                    );
                }
            });
        },
        [editor]
    );

    useEffect(() => {
        let isMounted = true;
        const rootElement = editor.getRootElement();

        // مقدار اولیه align
        setImgAlign(readImageAlign(editor, nodeKey));

        const unregister = mergeRegister(
            editor.registerUpdateListener(({ editorState }) => {
                if (!isMounted) return;
                setSelection(editorState.read(() => $getSelection()));
                // sync align
                const a = editorState.read(() => {
                    const node: any = $getNodeByKey(nodeKey);
                    if (!node) return null;
                    if (typeof node.getFormatType === "function")
                        return node.getFormatType();
                    if (typeof node.getFormat === "function") {
                        const f = node.getFormat();
                        if (typeof f === "string") return f;
                    }
                    if (typeof node.getAlignment === "function")
                        return node.getAlignment();
                    if (typeof node.getAlign === "function")
                        return node.getAlign();
                    return null;
                }) as any;
                setImgAlign(a ?? null);
            }),
            editor.registerCommand(
                SELECTION_CHANGE_COMMAND,
                (_, activeEditor) => {
                    activeEditorRef.current = activeEditor;
                    return false;
                },
                COMMAND_PRIORITY_LOW
            ),
            editor.registerCommand<MouseEvent>(
                CLICK_COMMAND,
                onClick,
                COMMAND_PRIORITY_LOW
            ),
            editor.registerCommand<MouseEvent>(
                RIGHT_CLICK_IMAGE_COMMAND,
                onClick,
                COMMAND_PRIORITY_LOW
            ),
            editor.registerCommand(
                DRAGSTART_COMMAND,
                (event) => {
                    if (event.target === imageRef.current) {
                        event.preventDefault();
                        return true;
                    }
                    return false;
                },
                COMMAND_PRIORITY_LOW
            ),
            editor.registerCommand(
                KEY_DELETE_COMMAND,
                $onDelete,
                COMMAND_PRIORITY_LOW
            ),
            editor.registerCommand(
                KEY_BACKSPACE_COMMAND,
                $onDelete,
                COMMAND_PRIORITY_LOW
            ),
            editor.registerCommand(
                KEY_ENTER_COMMAND,
                $onEnter,
                COMMAND_PRIORITY_LOW
            ),
            editor.registerCommand(
                KEY_ESCAPE_COMMAND,
                $onEscape,
                COMMAND_PRIORITY_LOW
            )
        );

        rootElement?.addEventListener("contextmenu", onRightClick);

        return () => {
            isMounted = false;
            unregister();
            rootElement?.removeEventListener("contextmenu", onRightClick);
        };
    }, [
        clearSelection,
        editor,
        isResizing,
        isSelected,
        nodeKey,
        $onDelete,
        $onEnter,
        $onEscape,
        onClick,
        onRightClick,
        setSelected,
    ]);

    // ✅ TS-safe: setShowCaption
    const setShowCaption = (show: boolean) => {
        editor.update(() => {
            const node = $getNodeByKey(nodeKey);
            if (!$isImageNode(node)) return;

            const anyNode: any = node;
            if (typeof anyNode.setShowCaption === "function") {
                anyNode.setShowCaption(show);
                return;
            }

            const writable: any =
                typeof anyNode.getWritable === "function"
                    ? anyNode.getWritable()
                    : anyNode;
            // رایج‌ترین نام داخلی
            writable.__showCaption = show;
        });
    };

    const onResizeEnd = (
        nextWidth: "inherit" | number,
        nextHeight: "inherit" | number
    ) => {
        setTimeout(() => {
            setIsResizing(false);
        }, 200);

        editor.update(() => {
            const node = $getNodeByKey(nodeKey);
            if (!$isImageNode(node)) return;

            const anyNode: any = node;
            if (typeof anyNode.setWidthAndHeight === "function") {
                anyNode.setWidthAndHeight(nextWidth, nextHeight);
                return;
            }

            const writable: any =
                typeof anyNode.getWritable === "function"
                    ? anyNode.getWritable()
                    : anyNode;
            writable.__width = nextWidth;
            writable.__height = nextHeight;
        });
    };

    const onResizeStart = () => {
        setIsResizing(true);
    };

    const draggable = isSelected && $isNodeSelection(selection) && !isResizing;
    const isFocused = (isSelected || isResizing) && isEditable;

    const effectiveAlign = imgAlign ?? undefined;

    return (
        <Suspense fallback={null}>
            <>
                {/* ✅ wrapper full-width + textAlign */}
                <div
                    draggable={draggable}
                    className="w-full"
                    style={{ textAlign: effectiveAlign as any, direction: "inherit" }}
                >
                    {isLoadError ? (
                        <BrokenImage />
                    ) : (
                        <div
                            className="relative inline-block select-none"
                            style={{
                                width: width === "inherit" ? "auto" : width,
                                maxWidth: "100%",
                            }}
                        >
                            <LazyImage
                                className={`max-w-full cursor-default ${isFocused
                                    ? `${$isNodeSelection(selection)
                                        ? "draggable cursor-grab active:cursor-grabbing"
                                        : ""
                                    } focused ring-primary ring-2 ring-offset-2`
                                    : null
                                    }`}
                                src={src}
                                altText={altText}
                                imageRef={imageRef}
                                width={width}
                                height={height}
                                maxWidth={maxWidth}
                                onError={() => setIsLoadError(true)}
                            />

                            {showCaption && (
                                <div className="absolute right-0 top-full left-0 m-0 mt-1 min-w-[100px] overflow-hidden rounded-md border bg-background shadow-sm">
                                    <div className="p-1">
                                        <LexicalNestedComposer
                                            initialEditor={caption}
                                            initialNodes={[
                                                RootNode,
                                                TextNode,
                                                ParagraphNode,
                                            ]}
                                        >
                                            <AutoFocusPlugin />
                                            <HistoryPlugin />
                                            <RichTextPlugin
                                                contentEditable={
                                                    <ContentEditable
                                                        className="ImageNode__contentEditable relative block min-h-[20px] w-full resize-none p-1 text-sm outline-none"
                                                        placeholderClassName="ImageNode__placeholder text-sm text-muted-foreground absolute top-1 right-1 pointer-events-none"
                                                        placeholder="توضیح تصویر..."
                                                    />
                                                }
                                                ErrorBoundary={LexicalErrorBoundary}
                                            />
                                            {/* ✅ Sync changes to the image node immediately */}
                                            <OnChangePlugin
                                                onChange={(editorState) => {
                                                    const json = JSON.stringify(editorState.toJSON());
                                                    editor.update(() => {
                                                        const node = $getNodeByKey(nodeKey);
                                                        if ($isImageNode(node)) {
                                                            const anyNode = node as any;
                                                            if (typeof anyNode.setCaptionState === "function") {
                                                                anyNode.setCaptionState(json);
                                                            }
                                                        }
                                                    });
                                                }}
                                            />
                                        </LexicalNestedComposer>
                                    </div>
                                    <div className="flex items-center justify-between border-t bg-muted/20 p-1">
                                        <button
                                            type="button"
                                            className="ml-2 rounded-sm bg-primary px-2 py-0.5 text-xs text-primary-foreground hover:bg-primary/90"
                                            onClick={(e) => {
                                                e.preventDefault();
                                                // Just close/save visual state, data is already in node
                                                // Optionally could deselect to "finish"
                                                // setShowCaption(false); // If we want to hide it? No user wants to see it.
                                                // Maybe just blur?
                                                editor.blur();
                                            }}
                                        >
                                            ✓ تایید
                                        </button>
                                        <button
                                            type="button"
                                            className="text-xs text-destructive hover:underline"
                                            onClick={() => {
                                                setShowCaption(false);
                                            }}
                                        >
                                            حذف توضیحات
                                        </button>
                                    </div>
                                </div>
                            )}

                            {resizable &&
                                $isNodeSelection(selection) &&
                                isFocused && (
                                    <ImageResizer
                                        showCaption={showCaption}
                                        setShowCaption={setShowCaption}
                                        editor={editor}
                                        buttonRef={buttonRef}
                                        imageRef={imageRef}
                                        maxWidth={maxWidth}
                                        onResizeStart={onResizeStart}
                                        onResizeEnd={onResizeEnd}
                                        captionsEnabled={!isLoadError && captionsEnabled}
                                    />
                                )}
                        </div>
                    )}
                </div>
            </>
        </Suspense>
    );
}
