// ==========================================
// Chat Switchboard — CM6 Note Editor Factory
// ==========================================
// Rich markdown editor for the notes panel.
// Decorates headings, bold, italic, code blocks inline.
// Integrates wikilink autocomplete and chip rendering.
//
// Usage:
//   const editor = CM.noteEditor(container, {
//       value: '# My Note\n\nSome content',
//       darkMode: true,
//       onChange: (text) => { ... },
//       onLink: (title) => { ... },
//       linkCompleter: async (query) => [{ label, id }],
//   });
//
//   editor.getValue()
//   editor.setValue(str)
//   editor.focus()
//   editor.destroy()
// ==========================================

import { EditorView, keymap, drawSelection, dropCursor,
         highlightSpecialChars, ViewPlugin, Decoration } from '@codemirror/view';
import { EditorState, RangeSetBuilder } from '@codemirror/state';
import { defaultKeymap, history, historyKeymap, indentWithTab } from '@codemirror/commands';
import { closeBrackets, closeBracketsKeymap } from '@codemirror/autocomplete';
import { markdown, markdownLanguage } from '@codemirror/lang-markdown';
import { syntaxHighlighting, defaultHighlightStyle, syntaxTree } from '@codemirror/language';
import { searchKeymap, highlightSelectionMatches } from '@codemirror/search';

import { noteEditorTheme } from './theme.mjs';
import { wikilinkExtension } from './wikilink.mjs';

// ── Markdown Live Preview Decorations ───────

/**
 * ViewPlugin that styles markdown headings, code blocks, etc.
 * at their rendered sizes inside the editor (live preview).
 */
const mdPreviewPlugin = ViewPlugin.fromClass(class {
    decorations;

    constructor(view) {
        this.decorations = this.build(view);
    }

    update(update) {
        if (update.docChanged || update.viewportChanged ||
            syntaxTree(update.startState) !== syntaxTree(update.state)) {
            this.decorations = this.build(update.view);
        }
    }

    build(view) {
        const builder = new RangeSetBuilder();
        const tree = syntaxTree(view.state);
        const doc = view.state.doc;

        tree.iterate({
            enter(node) {
                // Heading decorations
                if (node.name.startsWith('ATXHeading')) {
                    const level = parseInt(node.name.replace('ATXHeading', ''), 10) || 1;
                    const line = doc.lineAt(node.from);
                    builder.add(line.from, line.from,
                        Decoration.line({ class: `cm-md-heading cm-md-h${level}` }));
                }

                // Fenced code block decorations
                if (node.name === 'FencedCode') {
                    const startLine = doc.lineAt(node.from);
                    const endLine = doc.lineAt(node.to);
                    for (let ln = startLine.number; ln <= endLine.number; ln++) {
                        const line = doc.line(ln);
                        let cls = 'cm-codeblock cm-codeblock-mid';
                        if (ln === startLine.number) cls = 'cm-codeblock cm-codeblock-open';
                        if (ln === endLine.number) cls = 'cm-codeblock cm-codeblock-close';
                        if (startLine.number === endLine.number) cls = 'cm-codeblock cm-codeblock-open cm-codeblock-close';
                        builder.add(line.from, line.from, Decoration.line({ class: cls }));
                    }
                }

                // Blockquote decorations
                if (node.name === 'Blockquote') {
                    const startLine = doc.lineAt(node.from);
                    const endLine = doc.lineAt(node.to);
                    for (let ln = startLine.number; ln <= endLine.number; ln++) {
                        const line = doc.line(ln);
                        builder.add(line.from, line.from,
                            Decoration.line({ class: 'cm-md-blockquote' }));
                    }
                }
            },
        });

        return builder.finish();
    }
}, {
    decorations: (v) => v.decorations,
});

// ── Note Editor Factory ─────────────────────

/**
 * Create a rich markdown note editor with wikilink support.
 *
 * @param {HTMLElement} target - Container element
 * @param {Object} opts
 * @param {string}   [opts.value='']
 * @param {boolean}  [opts.darkMode]
 * @param {Function} [opts.onChange]        - Called with text on every edit
 * @param {Function} [opts.onLink]         - Called with (title) when a [[link]] is clicked
 * @param {Function} [opts.linkCompleter]  - async (query) => [{ label, id }]
 * @returns {{ getValue, setValue, focus, getView, destroy }}
 */
export function noteEditor(target, opts = {}) {
    const {
        value = '',
        darkMode = document.documentElement.getAttribute('data-theme') !== 'light',
        onChange = null,
        onLink = null,
        linkCompleter = null,
    } = opts;

    const extensions = [
        // Markdown highlighting with live preview
        markdown({ base: markdownLanguage }),
        syntaxHighlighting(defaultHighlightStyle, { fallback: true }),
        mdPreviewPlugin,

        // Wikilink support
        ...wikilinkExtension({ onLink, linkCompleter }),

        // Base editing
        highlightSpecialChars(),
        history(),
        drawSelection(),
        dropCursor(),
        closeBrackets(),
        highlightSelectionMatches(),

        // Theme — note-specific (no line numbers, full-height)
        noteEditorTheme,

        // Keymaps
        keymap.of([
            indentWithTab,
            ...closeBracketsKeymap,
            ...defaultKeymap,
            ...historyKeymap,
            ...searchKeymap,
        ]),

        // Spell check
        EditorView.contentAttributes.of({
            spellcheck: 'true',
            autocapitalize: 'sentences',
            autocorrect: 'on',
        }),

        // Line wrapping
        EditorView.lineWrapping,
    ];

    // Change listener
    if (onChange) {
        extensions.push(EditorView.updateListener.of((update) => {
            if (update.docChanged) {
                onChange(update.state.doc.toString());
            }
        }));
    }

    const view = new EditorView({
        state: EditorState.create({ doc: value, extensions }),
        parent: target,
    });

    // ── Public API ──────────────────────────

    return {
        getValue() {
            return view.state.doc.toString();
        },

        setValue(text) {
            view.dispatch({
                changes: { from: 0, to: view.state.doc.length, insert: text || '' },
            });
        },

        focus() {
            view.focus();
        },

        /** Access the underlying EditorView */
        getView() {
            return view;
        },

        /** Clean up */
        destroy() {
            view.destroy();
        },
    };
}
