// ==========================================
// Chat Switchboard — CM6 Code Editor Factory
// ==========================================
// Full-featured code editor: line numbers, bracket
// matching, search/replace, auto-indent, syntax
// highlighting. Used for extension editing and
// future code surfaces.
//
// Usage:
//   const editor = CM.codeEditor(container, {
//       language: 'javascript',
//       value: 'const x = 1;',
//       lineNumbers: true,
//       darkMode: true,
//       readOnly: false,
//       keymap: 'standard',  // 'standard' | 'vim' | 'emacs'
//       onChange: (value) => {},
//   });
//
//   editor.getValue()
//   editor.setValue(str)
//   editor.focus()
//   editor.destroy()
// ==========================================

import { EditorView, keymap, lineNumbers, highlightActiveLine,
         highlightActiveLineGutter, drawSelection, dropCursor,
         rectangularSelection, crosshairCursor,
         highlightSpecialChars, placeholder as placeholderExt } from '@codemirror/view';
import { EditorState, Compartment } from '@codemirror/state';
import { defaultKeymap, indentWithTab, history, historyKeymap,
         undo, redo } from '@codemirror/commands';
import { bracketMatching, indentOnInput, foldGutter, foldKeymap,
         syntaxHighlighting, defaultHighlightStyle,
         HighlightStyle } from '@codemirror/language';
import { highlightSelectionMatches, searchKeymap } from '@codemirror/search';
import { autocompletion, completionKeymap, closeBrackets,
         closeBracketsKeymap } from '@codemirror/autocomplete';
import { oneDark } from '@codemirror/theme-one-dark';

import { javascript } from '@codemirror/lang-javascript';
import { json } from '@codemirror/lang-json';
import { markdown, markdownLanguage } from '@codemirror/lang-markdown';
import { sql } from '@codemirror/lang-sql';
import { html } from '@codemirror/lang-html';
import { css } from '@codemirror/lang-css';
import { yaml } from '@codemirror/lang-yaml';
import { go } from '@codemirror/lang-go';
import { python } from '@codemirror/lang-python';
import { rust } from '@codemirror/lang-rust';

import { vim } from '@replit/codemirror-vim';
import { emacs } from '@replit/codemirror-emacs';

import { switchboardTheme } from './theme.mjs';

// ── Language Registry ────────────────────────

const LANGUAGES = {
    javascript, js: javascript,
    json,
    markdown, md: markdown,
    sql,
    html,
    css,
    yaml, yml: yaml,
    go,
    python, py: python,
    rust, rs: rust,
};

/**
 * Resolve a language name to a CM6 LanguageSupport instance.
 * Returns null if the language isn't bundled.
 */
function resolveLanguage(name) {
    if (!name) return null;
    const factory = LANGUAGES[name.toLowerCase()];
    return factory ? factory() : null;
}

// ── Code Editor Factory ─────────────────────

/**
 * Create a full-featured code editor.
 *
 * @param {HTMLElement} target - Container element (editor replaces its content)
 * @param {Object} opts
 * @param {string}   [opts.language]     - Language mode name
 * @param {string}   [opts.value='']     - Initial content
 * @param {boolean}  [opts.lineNumbers=true]
 * @param {boolean}  [opts.darkMode]     - Auto-detected from body if omitted
 * @param {boolean}  [opts.readOnly=false]
 * @param {string}   [opts.keymap='standard'] - 'standard' | 'vim' | 'emacs'
 * @param {string}   [opts.placeholder]  - Placeholder text
 * @param {Function} [opts.onChange]      - Called with new value on every edit
 * @returns {{ getValue, setValue, focus, getView, destroy }}
 */
export function codeEditor(target, opts = {}) {
    const {
        language = null,
        value = '',
        lineNumbers: showLineNumbers = true,
        darkMode = document.documentElement.getAttribute('data-theme') !== 'light',
        readOnly = false,
        keymap: keymapMode = 'standard',
        placeholder: placeholderText = '',
        onChange = null,
    } = opts;

    // Compartments for reconfigurable extensions
    const langCompartment = new Compartment();
    const keymapCompartment = new Compartment();
    const readOnlyCompartment = new Compartment();
    const themeCompartment = new Compartment();

    // Build extensions list
    const extensions = [
        // Base editing
        highlightSpecialChars(),
        history(),
        drawSelection(),
        dropCursor(),
        EditorState.allowMultipleSelections.of(true),
        indentOnInput(),
        bracketMatching(),
        closeBrackets(),
        autocompletion(),
        rectangularSelection(),
        crosshairCursor(),
        highlightActiveLine(),
        highlightSelectionMatches(),
        syntaxHighlighting(defaultHighlightStyle, { fallback: true }),

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

        // Theme
        switchboardTheme,
        themeCompartment.of(darkMode ? [oneDark] : []),

        // Configurable compartments
        langCompartment.of(resolveLanguage(language) || []),
        keymapCompartment.of(_resolveKeymapExt(keymapMode)),
        readOnlyCompartment.of(EditorState.readOnly.of(readOnly)),

        // Optional features
        ...(showLineNumbers ? [lineNumbers(), highlightActiveLineGutter(), foldGutter()] : []),
        ...(placeholderText ? [placeholderExt(placeholderText)] : []),
    ];

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

    // Create the editor
    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 for advanced use */
        getView() {
            return view;
        },

        /** Switch language mode at runtime */
        setLanguage(name) {
            const lang = resolveLanguage(name);
            view.dispatch({
                effects: langCompartment.reconfigure(lang || []),
            });
        },

        /** Switch keymap mode at runtime */
        setKeymap(mode) {
            view.dispatch({
                effects: keymapCompartment.reconfigure(_resolveKeymapExt(mode)),
            });
        },

        /** Toggle read-only state */
        setReadOnly(ro) {
            view.dispatch({
                effects: readOnlyCompartment.reconfigure(
                    EditorState.readOnly.of(ro)
                ),
            });
        },

        /** Toggle dark/light mode (controls oneDark syntax theme) */
        setDarkMode(dark) {
            view.dispatch({
                effects: themeCompartment.reconfigure(dark ? [oneDark] : []),
            });
        },

        /** Clean up — removes the editor from the DOM */
        destroy() {
            view.destroy();
        },
    };
}

// ── Keybinding Resolution ───────────────────

function _resolveKeymapExt(mode) {
    if (mode === 'vim') return vim();
    if (mode === 'emacs') return emacs();
    return [];
}
