"use client";

/**
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 *
 */
import {
    createContext,
    JSX,
    useContext,
    useEffect,
    useMemo,
    useState,
} from "react";
import * as React from "react";
import { useLexicalComposerContext } from "@lexical/react/LexicalComposerContext";
import {
    $createTableNodeWithDimensions,
    INSERT_TABLE_COMMAND,
    TableNode,
} from "@lexical/table";
import {
    $insertNodes,
    COMMAND_PRIORITY_EDITOR,
    createCommand,
    EditorThemeClasses,
    Klass,
    LexicalCommand,
    LexicalEditor,
    LexicalNode,
} from "lexical";

import { invariant } from "@/components/editor/shared/invariant";
import { Button } from "@/components/ui/button";
import { DialogFooter } from "@/components/ui/dialog";
import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label";

export type InsertTableCommandPayload = Readonly<{
    columns: string;
    rows: string;
    includeHeaders?: boolean;
}>;

export type CellContextShape = {
    cellEditorConfig: null | CellEditorConfig;
    cellEditorPlugins: null | JSX.Element | Array<JSX.Element>;
    set: (
        cellEditorConfig: null | CellEditorConfig,
        cellEditorPlugins: null | JSX.Element | Array<JSX.Element>
    ) => void;
};

export type CellEditorConfig = Readonly<{
    namespace: string;
    nodes?: ReadonlyArray<Klass<LexicalNode>>;
    onError: (error: Error, editor: LexicalEditor) => void;
    readOnly?: boolean;
    theme?: EditorThemeClasses;
}>;

export const INSERT_NEW_TABLE_COMMAND: LexicalCommand<InsertTableCommandPayload> =
    createCommand("INSERT_NEW_TABLE_COMMAND");

export const CellContext = createContext<CellContextShape>({
    cellEditorConfig: null,
    cellEditorPlugins: null,
    set: () => {
        // Empty
    },
});

export function TableContext({ children }: { children: JSX.Element }) {
    const [contextValue, setContextValue] = useState<{
        cellEditorConfig: null | CellEditorConfig;
        cellEditorPlugins: null | JSX.Element | Array<JSX.Element>;
    }>({
        cellEditorConfig: null,
        cellEditorPlugins: null,
    });
    return (
        <CellContext.Provider
            value={useMemo(
                () => ({
                    cellEditorConfig: contextValue.cellEditorConfig,
                    cellEditorPlugins: contextValue.cellEditorPlugins,
                    set: (cellEditorConfig, cellEditorPlugins) => {
                        setContextValue({
                            cellEditorConfig,
                            cellEditorPlugins,
                        });
                    },
                }),
                [contextValue.cellEditorConfig, contextValue.cellEditorPlugins]
            )}
        >
            {children}
        </CellContext.Provider>
    );
}

export function InsertTableDialog({
    activeEditor,
    onClose,
}: {
    activeEditor: LexicalEditor;
    onClose: () => void;
}): JSX.Element {
    const [rows, setRows] = useState("5");
    const [columns, setColumns] = useState("5");
    const [isDisabled, setIsDisabled] = useState(true);

    useEffect(() => {
        const row = Number(rows);
        const column = Number(columns);
        if (
            row &&
            row > 0 &&
            row <= 500 &&
            column &&
            column > 0 &&
            column <= 50
        ) {
            setIsDisabled(false);
        } else {
            setIsDisabled(true);
        }
    }, [rows, columns]);

    const onClick = () => {
        activeEditor.dispatchCommand(INSERT_TABLE_COMMAND, {
            columns,
            rows,
        });

        onClose();
    };

    return (
        <>
            <div className="grid gap-4">
                <div className="grid gap-2">
                    <Label htmlFor="rows">تعداد سطر ها</Label>
                    <Input
                        id="rows"
                        placeholder={"# of rows (1-500)"}
                        onChange={(e) => setRows(e.target.value)}
                        value={rows}
                        data-test-id="table-modal-rows"
                        type="number"
                    />
                </div>
                <div className="grid gap-2">
                    <Label htmlFor="columns">تعداد ستون ها</Label>
                    <Input
                        id="columns"
                        placeholder={"# of columns (1-50)"}
                        onChange={(e) => setColumns(e.target.value)}
                        value={columns}
                        data-test-id="table-modal-columns"
                        type="number"
                    />
                </div>
            </div>
            <DialogFooter data-test-id="table-model-confirm-insert">
                <Button disabled={isDisabled} onClick={onClick}>
                    تایید
                </Button>
            </DialogFooter>
        </>
    );
}

export function TablePlugin({
    cellEditorConfig,
    children,
}: {
    cellEditorConfig: CellEditorConfig;
    children: JSX.Element | Array<JSX.Element>;
}): JSX.Element | null {
    const [editor] = useLexicalComposerContext();
    const cellContext = useContext(CellContext);

    useEffect(() => {
        if (!editor.hasNodes([TableNode])) {
            invariant(
                false,
                "TablePlugin: TableNode is not registered on editor"
            );
        }

        cellContext.set(cellEditorConfig, children);

        return editor.registerCommand<InsertTableCommandPayload>(
            INSERT_NEW_TABLE_COMMAND,
            ({ columns, rows, includeHeaders }) => {
                const tableNode = $createTableNodeWithDimensions(
                    Number(rows),
                    Number(columns),
                    includeHeaders
                );
                $insertNodes([tableNode]);
                return true;
            },
            COMMAND_PRIORITY_EDITOR
        );
    }, [cellContext, cellEditorConfig, children, editor]);

    return null;
}
