// @mention autocomplete for textareas. Attaches a small popover that appears whenever the
// user types `@` followed by characters; arrow keys to navigate, Enter/Tab to insert,
// Escape to dismiss.
//
// The C# side passes a list of {name, token} pairs (token = FullName-with-dots) so we can
// filter client-side without a roundtrip.
window.tmMention = window.tmMention || (function () {
    const ATTACHED = new WeakMap();

    function attach(target, users) {
        // Accept either a DOM element or an element id (string). Lets Razor call this without
        // having to round-trip an IJSObjectReference for the textarea.
        const textarea = typeof target === 'string' ? document.getElementById(target) : target;
        if (!textarea) return;
        if (ATTACHED.has(textarea)) {
            // Re-attaching with a fresh user list — update the closure.
            ATTACHED.get(textarea).users = users || [];
            return;
        }
        // selected = -1 means "nothing highlighted yet, keyboard nav disabled". A 1-second
        // idle timer flips it to 0 (highlights the first row), at which point arrow keys
        // navigate and Enter inserts. Any further keystroke resets the highlight + timer.
        const state = { users: users || [], popup: null, selected: -1, anchor: -1, query: '', highlightTimer: null };
        ATTACHED.set(textarea, state);

        function close() {
            if (state.popup) { state.popup.remove(); state.popup = null; }
            if (state.highlightTimer) { clearTimeout(state.highlightTimer); state.highlightTimer = null; }
            state.anchor = -1;
            state.query = '';
            state.selected = -1;
        }

        // Reset the "auto-highlight first row after 1s of no typing" timer. Called from
        // input handler — each new keystroke restarts the wait.
        function scheduleHighlight() {
            if (state.highlightTimer) clearTimeout(state.highlightTimer);
            state.highlightTimer = setTimeout(function () {
                state.highlightTimer = null;
                if (state.anchor < 0) return;            // popup was closed
                if (state.selected >= 0) return;          // already highlighted
                const filtered = getMatches();
                if (filtered.length === 0) return;
                state.selected = 0;
                refresh();
            }, 1000);
        }

        function open(filtered) {
            close();
            if (!filtered.length) return;
            const rect = textarea.getBoundingClientRect();
            const popup = document.createElement('div');
            popup.className = 'tm-mention-popup';
            popup.style.position = 'fixed';
            popup.style.left = rect.left + 'px';
            popup.style.top = (rect.bottom + 4) + 'px';
            popup.style.minWidth = '180px';
            popup.style.maxHeight = '220px';
            popup.style.overflowY = 'auto';
            popup.style.background = 'var(--tm-panel,#fff)';
            popup.style.border = '1px solid var(--tm-panel-line,#e5e7eb)';
            popup.style.borderRadius = '8px';
            popup.style.boxShadow = '0 8px 24px rgba(15,23,42,0.15)';
            popup.style.zIndex = '9999';
            popup.style.fontSize = '13px';

            filtered.forEach((u, i) => {
                const row = document.createElement('div');
                row.textContent = u.name;
                row.dataset.token = u.token;
                row.style.padding = '8px 12px';
                row.style.cursor = 'pointer';
                // Highlight only when keyboard nav has activated (selected >= 0). While the
                // user is still typing fast, we leave the list plain so arrow keys are no-ops
                // and don't fight the typing.
                if (state.selected >= 0 && i === state.selected) {
                    row.style.background = 'var(--tm-hover-bg,rgba(99,102,241,0.08))';
                }
                row.addEventListener('mousedown', (e) => {
                    e.preventDefault();
                    insertToken(u.token);
                });
                popup.appendChild(row);
            });

            document.body.appendChild(popup);
            state.popup = popup;
        }

        function getMatches() {
            const q = state.query.toLowerCase();
            const filtered = state.users.filter(u =>
                u.token.toLowerCase().startsWith(q) ||
                u.name.toLowerCase().includes(q)
            );
            return filtered.slice(0, 6);
        }

        function refresh() {
            const filtered = getMatches();
            open(filtered);
        }

        function insertToken(token) {
            // User-supplied logic: from the current cursor position, walk backwards
            // until we hit the nearest '@' and replace everything between there and
            // the cursor with the mention. Reading the cursor + scanning live state
            // avoids the stored-anchor race that produced "@de@Deepika @dee".
            const value = textarea.value;
            const cursor = textarea.selectionStart || value.length;
            const atPos = value.lastIndexOf('@', cursor - 1);
            if (atPos < 0) { close(); return; }

            const before = value.slice(0, atPos);
            const after = value.slice(cursor);
            const insert = '@' + token + ' ';
            // Select the @-to-cursor range first, then use execCommand so Blazor's
            // @bind:event="oninput" pipeline treats it as a real native edit.
            textarea.focus();
            try { textarea.setSelectionRange(atPos, cursor); } catch (_) { }

            let ok = false;
            try {
                ok = !!document.execCommand && document.execCommand('insertText', false, insert);
            } catch (_) { ok = false; }

            if (!ok) {
                // Fallback: direct value mutation + dispatchEvent.
                textarea.value = before + insert + after;
                const newCursor = (before + insert).length;
                try { textarea.setSelectionRange(newCursor, newCursor); } catch (_) { }
            }

            // Fire input event so Blazor's @bind picks it up. Native InputEvent so
            // Blazor's reconciler treats it as a real edit.
            try {
                textarea.dispatchEvent(new InputEvent('input', {
                    bubbles: true, data: insert, inputType: 'insertText'
                }));
            } catch (_) {
                textarea.dispatchEvent(new Event('input', { bubbles: true }));
            }
            close();
        }

        textarea.addEventListener('input', () => {
            const pos = textarea.selectionStart || 0;
            const text = textarea.value.slice(0, pos);
            // Find the last @ that's preceded by whitespace or string start.
            const m = text.match(/(^|\s)@([A-Za-z0-9._\-]*)$/);
            if (!m) { close(); return; }
            state.anchor = m.index + (m[1] ? 1 : 0); // index of the @
            state.query = m[2];
            // Per UX spec: while the user is actively typing we DO NOT highlight anything.
            // After 1s of no typing, scheduleHighlight() auto-selects the first row and
            // arrow keys become navigable. Any new keystroke resets the highlight + timer.
            state.selected = -1;
            refresh();
            scheduleHighlight();
        });

        textarea.addEventListener('keydown', (e) => {
            if (state.anchor < 0) return;            // popup not active
            const filtered = getMatches();
            if (filtered.length === 0) return;

            // Arrow / Enter / Tab: if the user hasn't paused long enough for the auto-
            // highlight to kick in, the first arrow press promotes the first row to
            // highlighted (cancels the timer too). Subsequent arrows then navigate.
            if (e.key === 'ArrowDown') {
                e.preventDefault();
                if (state.highlightTimer) { clearTimeout(state.highlightTimer); state.highlightTimer = null; }
                if (state.selected < 0) state.selected = 0;
                else state.selected = Math.min(state.selected + 1, filtered.length - 1);
                refresh();
            } else if (e.key === 'ArrowUp') {
                e.preventDefault();
                if (state.highlightTimer) { clearTimeout(state.highlightTimer); state.highlightTimer = null; }
                if (state.selected < 0) state.selected = filtered.length - 1;
                else state.selected = Math.max(state.selected - 1, 0);
                refresh();
            } else if (e.key === 'Enter' || e.key === 'Tab') {
                // Enter only inserts when we already have a highlight. Before the auto-
                // highlight kicks in, Enter is a normal newline — don't interfere.
                if (state.selected >= 0 && filtered[state.selected]) {
                    e.preventDefault();
                    insertToken(filtered[state.selected].token);
                }
            } else if (e.key === 'Escape') {
                e.preventDefault();
                close();
            }
        });

        textarea.addEventListener('blur', () => setTimeout(close, 150));
    }

    return { attach };
})();
