// ==========================================
// Chat Switchboard — CM6 @Mention Extension
// ==========================================
// Provides:
//   1. Autocomplete triggered by @ for personas/users
//   2. ViewPlugin that decorates @handle as styled marks
//   3. Theme for mention styling in the editor
//
// Modeled on wikilink.mjs — same pattern adapted for
// @mention tokens instead of [[wikilink]] syntax.
//
// Usage:
//   import { mentionExtension } from './mention.mjs';
//   const ext = mentionExtension({
//       completer: async (query) => [{ label, handle, type }],
//   });
// ==========================================

import {
    ViewPlugin, Decoration, EditorView,
} from '@codemirror/view';
import { RangeSetBuilder } from '@codemirror/state';
import { autocompletion } from '@codemirror/autocomplete';

// ── Mention Regex ───────────────────────────

// Matches @handle tokens: @ preceded by start-of-string or whitespace,
// followed by one or more word chars or hyphens.
const MENTION_RE = /(?:^|(?<=\s))@([\w-]+)/g;

// ── ViewPlugin: Decorate @mentions ──────────

function mentionDecoPlugin() {
    return ViewPlugin.fromClass(class {
        decorations;

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

        update(update) {
            if (update.docChanged || update.viewportChanged || update.selectionSet) {
                this.decorations = this.build(update.view);
            }
        }

        build(view) {
            const builder = new RangeSetBuilder();
            const doc = view.state.doc;
            const text = doc.toString();
            const sel = view.state.selection.main;

            let match;
            MENTION_RE.lastIndex = 0;
            while ((match = MENTION_RE.exec(text)) !== null) {
                // match[0] includes any leading whitespace captured by lookbehind,
                // but lookbehind is zero-width so match.index points to @
                const atIdx = text.indexOf('@', match.index);
                const from = atIdx;
                const to = from + 1 + match[1].length; // @handle

                // Don't decorate if cursor is inside the mention (let user edit)
                if (sel.from >= from && sel.from <= to) continue;

                builder.add(from, to, Decoration.mark({ class: 'cm-mention' }));
            }

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

// ── Autocomplete: @ trigger ─────────────────

function mentionAutocomplete(completer) {
    return autocompletion({
        override: [
            async (ctx) => {
                // Look for @ before the cursor (at line start or after whitespace)
                const line = ctx.state.doc.lineAt(ctx.pos);
                const textBefore = ctx.state.doc.sliceString(line.from, ctx.pos);
                const triggerMatch = textBefore.match(/(?:^|\s)@([\w-]*)$/);

                if (!triggerMatch) return null;

                const query = triggerMatch[1];
                // from = position of the first char after @
                const from = ctx.pos - query.length;

                if (!completer) return null;

                try {
                    const results = await completer(query);
                    if (!results || results.length === 0) return null;

                    return {
                        from,
                        options: results.map(r => ({
                            label: r.handle,
                            displayLabel: r.label,
                            detail: r.type === 'all' ? 'all users' : r.type,
                            apply: (view, completion, from, to) => {
                                // Insert handle + trailing space
                                const insert = completion.label + ' ';
                                view.dispatch({
                                    changes: { from, to, insert },
                                    selection: { anchor: from + insert.length },
                                });
                            },
                        })),
                    };
                } catch (e) {
                    console.warn('[mention] autocomplete error:', e);
                    return null;
                }
            },
        ],
        activateOnTyping: true,
    });
}

// ── Theme: Mention Styles ───────────────────

const mentionTheme = EditorView.baseTheme({
    '.cm-mention': {
        color: 'var(--accent, #6366f1)',
        backgroundColor: 'rgba(var(--accent-rgb, 99, 102, 241), 0.1)',
        borderRadius: '3px',
        padding: '0 2px',
        fontWeight: '500',
    },
});

// ── Public: Combined Extension ──────────────

/**
 * Create the @mention CM6 extension bundle.
 *
 * @param {Object} opts
 * @param {Function} opts.completer - async (query) => [{ label, handle, type }]
 * @returns {Extension[]} Array of CM6 extensions
 */
export function mentionExtension(opts = {}) {
    const extensions = [
        mentionDecoPlugin(),
        mentionTheme,
    ];

    if (opts.completer) {
        extensions.push(mentionAutocomplete(opts.completer));
    }

    return extensions;
}
