import {Controller} from '@hotwired/stimulus';
import {
    $createParagraphNode,
    $createTextNode,
    $getRoot,
    $getSelection,
    $isRangeSelection,
    FORMAT_TEXT_COMMAND,
    SELECTION_CHANGE_COMMAND
} from 'lexical';
import {CheckListExtension, INSERT_ORDERED_LIST_COMMAND, INSERT_UNORDERED_LIST_COMMAND} from '@lexical/list';
import {$createHeadingNode, $createQuoteNode, RichTextExtension} from '@lexical/rich-text';
import {AutoLinkExtension, LinkExtension} from '@lexical/link';
import {CodeExtension} from '@lexical/code';
import {mergeRegister} from '@lexical/utils';
import {buildEditorFromExtensions, configExtension} from '@lexical/extension';
import {$convertFromMarkdownString, $convertToMarkdownString, TRANSFORMERS} from '@lexical/markdown';
import {$setBlocksType} from '@lexical/selection';

const URL_MATCHER = /((https?:\/\/(www\.)?)|(www\.))[-a-zA-Z0-9@:%._+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b([-a-zA-Z0-9()@:%_+.~#?&//=]*)/;

export default class extends Controller {
    static targets = ["editor", "toolbar", "input"];

    connect() {
        if (!this.hasInputTarget || this.inputTarget.disabled) {
            return;
        }

        const initialContent = this.inputTarget.value;

        this.editor = buildEditorFromExtensions(
            RichTextExtension,
            CheckListExtension,
            configExtension(LinkExtension, {
                validateUrl: (url) => {
                    try {
                        const parsed = new URL(url);
                        return ['http:', 'https:', 'mailto:', 'tel:'].includes(parsed.protocol);
                    } catch (e) {
                        return url.startsWith('/') || url.startsWith('#');
                    }
                }
            }),
            configExtension(AutoLinkExtension, {
                matchers: [
                    (text) => {
                        const match = URL_MATCHER.exec(text);
                        if (match) {
                            return {
                                index: match.index,
                                length: match[0].length,
                                text: match[0],
                                url: match[0],
                            };
                        }
                        return null;
                    }
                ]
            }),
            CodeExtension,
            {
                name: 'Config',
                onError: (error) => {
                    console.error('Lexical error:', error);
                }
            }
        );

        this.editor.setRootElement(this.editorTarget);

        // Custom registration of features
        this.unregisters = mergeRegister(
            this.editor.registerUpdateListener(({editorState}) => {
                editorState.read(() => {
                    const markdown = $convertToMarkdownString(TRANSFORMERS);
                    if (this.inputTarget.value !== markdown) {
                        this.inputTarget.value = markdown;
                        this.inputTarget.dispatchEvent(new Event('input', {bubbles: true}));
                        this.inputTarget.dispatchEvent(new Event('change', {bubbles: true}));
                        this.dispatch('dirty');
                    }
                });
            }),
            this.editor.registerCommand(
                SELECTION_CHANGE_COMMAND,
                () => {
                    this.updateToolbar();
                    return false;
                },
                1
            )
        );

        if (initialContent) {
            this.editor.update(() => {
                try {
                    $convertFromMarkdownString(initialContent, TRANSFORMERS);
                } catch (e) {
                    console.error('Lexical: Failed to parse initial markdown', {
                        error: e,
                        content: initialContent
                    });
                    const root = $getRoot();
                    root.clear();
                    const p = $createParagraphNode();
                    p.append($createTextNode(initialContent));
                    root.append(p);
                }
            });
        }
    }

    disconnect() {
        if (this.unregisters) {
            this.unregisters();
        }
    }

    dispatchAction(event) {
        event.preventDefault();
        const action = event.params.action;

        if (['bold', 'italic'].includes(action)) {
            this.editor.dispatchCommand(FORMAT_TEXT_COMMAND, action);
        } else if (action === 'ul') {
            this.editor.dispatchCommand(INSERT_UNORDERED_LIST_COMMAND, undefined);
        } else if (action === 'ol') {
            this.editor.dispatchCommand(INSERT_ORDERED_LIST_COMMAND, undefined);
        } else {
            this.handleAction(action);
        }
    }

    handleAction(action) {
        this.editor.update(() => {
            const selection = $getSelection();
            if ($isRangeSelection(selection)) {
                if (action === 'h2' || action === 'h3') {
                    $setBlocksType(selection, () => $createHeadingNode(action));
                }
                if (action === 'quote') {
                    $setBlocksType(selection, () => $createQuoteNode());
                }
            }
        });
    }

    updateToolbar() {
        // Here we could update active states of buttons
    }
}
