import React, { MutableRefObject } from "react";
import { twMerge } from "tailwind-merge";
import AceEditorModes from "./ace-editor-modes";
import { AceEditorOptions } from "@moduletrace/datasquirel/dist/package-shared/types";

export type AceEditorComponentType = {
    editorRef?: MutableRefObject<AceAjax.Editor | undefined>;
    readOnly?: boolean;
    /** Function to call when Ctrl+Enter is pressed */
    ctrlEnterFn?: (editor: AceAjax.Editor) => void;
    content?: string;
    placeholder?: string;
    title?: string;
    mode?: (typeof AceEditorModes)[number];
    fontSize?: string;
    previewMode?: boolean;
    onChange?: (value: string) => void;
    delay?: number;
    wrapperProps?: React.DetailedHTMLProps<
        React.HTMLAttributes<HTMLDivElement>,
        HTMLDivElement
    >;
    refreshDepArr?: any[];
    editorOptions?: AceEditorOptions;
    showLabel?: boolean;
};

let timeout: any;

/**
 * # Powerful Ace Editor
 * @note **NOTE** head scripts required
 * @script `https://cdnjs.cloudflare.com/ajax/libs/ace/1.22.0/ace.min.js`
 * @script `https://cdnjs.cloudflare.com/ajax/libs/ace/1.22.0/ext-language_tools.min.js`
 */
export default function AceEditor({
    editorRef,
    readOnly,
    ctrlEnterFn,
    content = "",
    placeholder,
    mode,
    fontSize,
    previewMode,
    onChange,
    delay = 500,
    refreshDepArr,
    wrapperProps,
    editorOptions,
    showLabel,
    title,
}: AceEditorComponentType) {
    try {
        const editorElementRef = React.useRef<HTMLDivElement>();
        const editorRefInstance = React.useRef<AceAjax.Editor>();

        const [refresh, setRefresh] = React.useState(0);
        const [darkMode, setDarkMode] = React.useState(false);
        const [ready, setReady] = React.useState(false);

        React.useEffect(() => {
            if (!ready) return;

            if (!ace?.edit || !editorElementRef.current) {
                setTimeout(() => {
                    setRefresh((prev) => prev + 1);
                }, 1000);
                return;
            }

            const editor = ace.edit(editorElementRef.current);

            editor.setOptions({
                mode: `ace/mode/${mode ? mode : "javascript"}`,
                theme: darkMode
                    ? "ace/theme/tomorrow_night_eighties"
                    : "ace/theme/ace_light",
                value: (() => {
                    try {
                        return JSON.stringify(JSON.parse(content), null, 4);
                    } catch (error) {
                        return content;
                    }
                })(),
                placeholder: placeholder ? placeholder : "",
                enableBasicAutocompletion: true,
                enableLiveAutocompletion: true,
                readOnly: readOnly ? true : false,
                fontSize: fontSize ? fontSize : null,
                showLineNumbers: previewMode ? false : true,
                wrap: true,
                wrapMethod: "code",
                ...editorOptions,
            });

            editor.commands.addCommand({
                name: "myCommand",
                bindKey: { win: "Ctrl-Enter", mac: "Command-Enter" },
                exec: function (editor) {
                    if (ctrlEnterFn) ctrlEnterFn(editor);
                },
                readOnly: true,
            });

            editor.getSession().on("change", function (e) {
                if (onChange) {
                    clearTimeout(timeout);

                    setTimeout(() => {
                        try {
                            onChange(editor.getValue());
                        } catch (error) {}
                    }, delay);
                }
            });

            editorRefInstance.current = editor;
            if (editorRef) editorRef.current = editor;

            return function () {
                editor.destroy();
            };
        }, [refresh, darkMode, ready, mode, ...(refreshDepArr || [])]);

        React.useEffect(() => {
            const htmlClassName = document.documentElement.className;
            if (htmlClassName.match(/dark/i)) setDarkMode(true);
            setTimeout(() => {
                setReady(true);
            }, 200);
        }, []);

        return (
            <React.Fragment>
                <div
                    {...wrapperProps}
                    className={twMerge(
                        "w-full h-[400px] block rounded-default",
                        "border border-slate-200 border-solid relative",
                        "dark:border-white/20",
                        showLabel && title ? "pt-4" : "",
                        wrapperProps?.className,
                    )}
                >
                    {showLabel && title ? (
                        <label
                            className={twMerge(
                                "bg-background-light dark:bg-background-dark text-xs",
                                "-top-3 left-2 px-2 py-1 absolute z-10",
                            )}
                        >
                            {title}
                        </label>
                    ) : null}
                    <div
                        ref={editorElementRef as any}
                        className="w-full h-full"
                    ></div>
                </div>
            </React.Fragment>
        );
    } catch (error: any) {
        return (
            <React.Fragment>
                <span className="m-0">
                    Editor Error:{" "}
                    <b className="text-red-600">{error.message}</b>
                </span>
            </React.Fragment>
        );
    }
}
