<script lang="ts">
/**
 * element_editor_codemirror.svelte
 * Consolidated CodeMirror 6 Editor for Aether Platform.
 * Combines technical power with a user-friendly Markdown toolbar.
 * Uses strictly snake_case and Svelte 5 Runes.
 */
import { onMount, onDestroy, untrack } from 'svelte';
import { browser } from '$app/environment';
import { ensure_CodeMirror_modules } from './codemirror_modules';
// import type { key_val } from '$lib/stores/ae_stores';

// Icons (Standardized to Lucide where possible, or FontAwesome placeholders)
import { Bold, Code, Hash, Italic, List } from '@lucide/svelte';
interface Props {
    content?: string | null;
    new_content?: string;
    placeholder?: string;
    theme_mode?: 'light' | 'dark';
    language?: 'markdown' | 'json' | 'html' | 'javascript';
    readonly?: boolean;
    show_line_numbers?: boolean;
    wrap_lines?: boolean;
    show_toolbar?: boolean;
    editor_view?: any; // $bindable for external control
    class_li?: string;
}

let {
    content = $bindable(''),
    new_content = $bindable(),
    placeholder = 'Start typing...',
    theme_mode = 'light',
    language = 'markdown',
    readonly = false,
    show_line_numbers = false,
    wrap_lines = true,
    show_toolbar = true,
    editor_view = $bindable(),
    class_li = ''
}: Props = $props();

let editor_container: HTMLDivElement | undefined = $state();
let cm: any = $state(); // CodeMirror modules cache
let show_line_nums = $state(untrack(() => show_line_numbers));
let ln_compartment: any = null;

async function create_editor() {
    if (!browser) return;

    cm = await ensure_CodeMirror_modules();
    if (!cm) return;

    // Cleanup existing instance if HMR or remount occurs
    if (editor_view) {
        editor_view.destroy();
        editor_view = null;
    }

    ln_compartment = new cm.Compartment();

    const extensions = [
        cm.highlightSpecialChars(),
        cm.history(),
        cm.drawSelection(),
        cm.dropCursor(),
        cm.EditorState_allowMultipleSelections.of(true),
        cm.indentOnInput(),
        cm.bracketMatching(),
        cm.closeBrackets(),
        cm.autocompletion(),
        cm.rectangularSelection(),
        cm.crosshairCursor(),
        cm.highlightActiveLine(),
        cm.highlightActiveLineGutter(),

        // Keymaps — indentWithTab must come before defaultKeymap
        cm.keymap.of([
            cm.indentWithTab,
            ...cm.defaultKeymap,
            ...cm.searchKeymap,
            ...cm.historyKeymap,
            ...cm.foldKeymap,
            ...cm.completionKeymap,
            ...cm.lintKeymap
        ]),

        // 4-space indentation unit
        cm.indentUnit.of('    '),

        // Language Support
        language === 'markdown'
            ? cm.markdown({ base: cm.markdownLanguage })
            : null,
        language === 'json'
            ? cm.languages.find((l: any) => l.name === 'json')?.load()
            : null,

        // Theme & Behavior
        theme_mode === 'dark' ? cm.oneDark : cm.EditorView.baseTheme(),
        cm.EditorView.contentAttributes.of({ spellcheck: 'true' }),
        readonly
            ? cm.EditorState.readOnly.of(true)
            : cm.EditorView.editable.of(true),
        ln_compartment.of(show_line_nums ? cm.lineNumbers() : []),
        wrap_lines ? cm.EditorView_lineWrapping : null,
        placeholder ? cm.placeholderExt(placeholder) : null,

        // Sync back to Svelte
        cm.EditorView.updateListener.of((update: any) => {
            if (update.docChanged) {
                const doc_str = update.state.doc.toString();
                content = doc_str;
                new_content = doc_str;
            }
        })
    ].filter(Boolean);

    if (!editor_container) return;

    editor_view = new cm.EditorView({
        state: cm.EditorState.create({
            doc: content ?? '',
            extensions
        }),
        parent: editor_container as HTMLElement
    });
}

// Initialize on mount
onMount(async () => {
    await create_editor();
});

// Cleanup on destroy
onDestroy(() => {
    if (editor_view) editor_view.destroy();
});

// Reactive update if content is changed from outside (e.g. API load)
$effect(() => {
    if (editor_view && content !== editor_view.state.doc.toString()) {
        untrack(() => {
            editor_view.dispatch({
                changes: {
                    from: 0,
                    to: editor_view.state.doc.length,
                    insert: content || ''
                }
            });
        });
    }
});

// Toggle line numbers without rebuilding the editor
$effect(() => {
    if (!editor_view || !ln_compartment || !cm) return;
    editor_view.dispatch({
        effects: ln_compartment.reconfigure(show_line_nums ? cm.lineNumbers() : [])
    });
});

// *** Toolbar Helpers
const wrap_selection = (before: string, after: string = before) => {
    if (!editor_view) return;
    const state = editor_view.state;
    const changes = state.changeByRange((range: any) => {
        const is_wrapped =
            state.sliceDoc(range.from - before.length, range.from) === before &&
            state.sliceDoc(range.to, range.to + after.length) === after;

        if (is_wrapped) {
            return {
                changes: [
                    {
                        from: range.from - before.length,
                        to: range.from,
                        insert: ''
                    },
                    { from: range.to, to: range.to + after.length, insert: '' }
                ],
                range: cm.EditorSelection.range(
                    range.from - before.length,
                    range.to - before.length
                )
            };
        }

        return {
            changes: [
                { from: range.from, insert: before },
                { from: range.to, insert: after }
            ],
            range: cm.EditorSelection.range(
                range.from + before.length,
                range.to + before.length
            )
        };
    });
    editor_view.dispatch(changes);
    editor_view.focus();
};

const toggle_list = () => {
    if (!editor_view) return;
    const state = editor_view.state;
    const changes = state.changeByRange((range: any) => {
        const line = state.doc.lineAt(range.from);
        const has_list = line.text.startsWith('- ');

        if (has_list) {
            return {
                changes: [{ from: line.from, to: line.from + 2, insert: '' }],
                range: cm.EditorSelection.range(range.from - 2, range.to - 2)
            };
        }

        return {
            changes: [{ from: line.from, insert: '- ' }],
            range: cm.EditorSelection.range(range.from + 2, range.to + 2)
        };
    });
    editor_view.dispatch(changes);
    editor_view.focus();
};
</script>

<div
    class="ae__comp__codemirror_editor border-surface-500/20 rounded-container flex h-full flex-col overflow-hidden border {class_li}">
    {#if show_toolbar && !readonly}
        <div
            class="toolbar bg-surface-50 dark:bg-surface-900 border-surface-500/20 flex flex-wrap gap-1 border-b p-1">
            <button
                type="button"
                class="btn btn-sm variant-soft hover:variant-filled-primary"
                onclick={() => wrap_selection('**')}
                title="Bold">
                <Bold size="14" />
            </button>
            <button
                type="button"
                class="btn btn-sm variant-soft hover:variant-filled-primary"
                onclick={() => wrap_selection('*')}
                title="Italic">
                <Italic size="14" />
            </button>
            <button
                type="button"
                class="btn btn-sm variant-soft hover:variant-filled-primary"
                onclick={toggle_list}
                title="Bullet List">
                <List size="14" />
            </button>
            <button
                type="button"
                class="btn btn-sm variant-soft hover:variant-filled-primary"
                onclick={() => wrap_selection('`')}
                title="Inline Code">
                <Code size="14" />
            </button>

            <div class="ml-auto flex gap-1">
                <button
                    type="button"
                    class="btn btn-sm {show_line_nums
                        ? 'variant-filled-primary'
                        : 'variant-soft'} hover:variant-filled-primary"
                    onclick={() => (show_line_nums = !show_line_nums)}
                    title="Toggle Line Numbers">
                    <Hash size="14" />
                </button>
                <span
                    class="mr-2 self-center text-[10px] font-bold uppercase opacity-50"
                    >{language}</span>
            </div>
        </div>
    {/if}

    <div
        bind:this={editor_container}
        class="grow overflow-auto bg-white dark:bg-black/20"
        style="min-height: 100px;">
    </div>
</div>

<style lang="postcss">
.ae__comp__codemirror_editor :global(.cm-editor) {
    height: 100%;
    outline: none !important;
}
.ae__comp__codemirror_editor :global(.cm-scroller) {
    font-family: theme('fontFamily.mono');
    font-size: 0.875rem;
}
/* Hide the focus outline from CM6 default theme */
.ae__comp__codemirror_editor :global(.cm-focused) {
    outline: none !important;
}
</style>
