(function() {
    'use strict';

    const STANDARD_SUGGESTIONS = [
        'and','as','assert','async','await','break','class','continue','def','del','elif','else','except',
        'False','finally','for','from','global','if','import','in','is','lambda','None','nonlocal','not',
        'or','pass','raise','return','True','try','while','with','yield',
        'abs','all','any','bin','bool','chr','dict','dir','enumerate','filter','float','format','help',
        'hex','input','int','len','list','map','max','min','oct','open','ord','pow','print','range',
        'repr','reversed','round','set','sorted','sum','tuple','type','zip'
    ];

    let autocompleteEnabled = false;
    let editorElement = null;
    let menuElement = null;
    let currentSuggestions = [];
    let selectedIndex = 0;
    let currentStartOffset = 0;
    let currentEndOffset = 0;
    let userIdentifiers = [];
    let parseTimer = null;
    let confirmKey = 'Enter';
    let listenersAttached = false;
    let bodyObserver = null;
    let intervalTimer = null;

    let inputListener = null;
    let keyDownListener = null;
    let clickOutsideListener = null;

    function findEditor() {
        return document.querySelector('.cm-content[contenteditable="true"]');
    }

    function createMenu() {
        if (menuElement) return;
        menuElement = document.createElement('div');
        menuElement.id = 'python-autocomplete-menu';
        menuElement.style.cssText = 'position:absolute;z-index:1000000;background:white;border:1px solid #ccc;border-radius:4px;box-shadow:0 2px 10px rgba(0,0,0,0.2);font-family:monospace;font-size:14px;max-height:200px;overflow-y:auto;display:none;';
        document.body.appendChild(menuElement);
    }

    function removeMenu() { if (menuElement) { menuElement.remove(); menuElement = null; } }

    function parseCodeForIdentifiers(code) {
        const keywords = new Set(STANDARD_SUGGESTIONS);
        const identifiers = new Set();
        let i = 0, n = code.length;
        const isIdentStart = ch => /[a-zA-Z_]/.test(ch);
        const isIdentPart = ch => /[a-zA-Z0-9_]/.test(ch);
        while (i < n) {
            const ch = code[i];
            if (/\s/.test(ch)) { i++; continue; }
            if (ch === '#') { while (i < n && code[i] !== '\n') i++; i++; continue; }
            if (ch === '"' || ch === "'") {
                const quote = ch;
                if (i+2 < n && code[i+1]===quote && code[i+2]===quote) {
                    i += 3;
                    while (i < n && !(code[i]===quote && code[i+1]===quote && code[i+2]===quote)) i++;
                    i += 3;
                } else {
                    i++;
                    while (i < n && code[i] !== quote) { if (code[i] === '\\') i += 2; else i++; }
                    if (i < n) i++;
                }
                continue;
            }
            if (isIdentStart(ch)) {
                let start = i;
                while (i < n && isIdentPart(code[i])) i++;
                const word = code.substring(start, i);
                if (!keywords.has(word) && word.length > 0) identifiers.add(word);
                continue;
            }
            i++;
        }
        return Array.from(identifiers);
    }

    function refreshUserIdentifiers() {
        if (!editorElement) return;
        const newIdentifiers = parseCodeForIdentifiers(editorElement.textContent);
        if (JSON.stringify(newIdentifiers) !== JSON.stringify(userIdentifiers)) userIdentifiers = newIdentifiers;
    }

    function scheduleParse() {
        if (parseTimer) clearTimeout(parseTimer);
        parseTimer = setTimeout(() => { refreshUserIdentifiers(); parseTimer = null; }, 300);
    }

    function updateMenu(suggestions, x, y) {
        if (!menuElement) createMenu();
        menuElement.innerHTML = '';
        if (suggestions.length === 0) { menuElement.style.display = 'none'; return; }
        suggestions.forEach((item, index) => {
            const div = document.createElement('div');
            div.textContent = item.text;
            div.style.cssText = `padding:5px 10px;cursor:pointer;background:${index===selectedIndex?'#e0e0e0':'transparent'};color:${item.type==='user'?'#2e7d32':'#1565c0'};font-weight:${item.type==='user'?'bold':'normal'};`;
            div.addEventListener('mouseenter', () => { selectedIndex = index; updateMenu(suggestions, x, y); });
            div.addEventListener('click', () => applySuggestion(item.text));
            menuElement.appendChild(div);
        });
        menuElement.style.display = 'block';
        menuElement.style.left = x + 'px';
        menuElement.style.top = y + 'px';
    }

    function getWordBeforeCursor() {
        const sel = window.getSelection();
        if (!sel.rangeCount) return { word: '', start: 0, end: 0 };

        const range = sel.getRangeAt(0);
        const node = range.startContainer;
        const offset = range.startOffset;

        const lineElement = node.nodeType === Node.TEXT_NODE
            ? node.parentElement?.closest('.cm-line')
            : node.closest('.cm-line');

        if (!lineElement) return { word: '', start: 0, end: 0 };

        const lineText = lineElement.textContent || '';

        let posInLine = 0;
        if (node.nodeType === Node.TEXT_NODE) {
            const walker = document.createTreeWalker(lineElement, NodeFilter.SHOW_TEXT);
            while (walker.nextNode()) {
                const currentNode = walker.currentNode;
                if (currentNode === node) {
                    posInLine += offset;
                    break;
                } else {
                    posInLine += currentNode.textContent.length;
                }
            }
        } else {
            posInLine = offset;
        }

        let start = posInLine;
        while (start > 0 && /[a-zA-Z0-9_]/.test(lineText[start - 1])) {
            start--;
        }
        let end = posInLine;
        while (end < lineText.length && /[a-zA-Z0-9_]/.test(lineText[end])) {
            end++;
        }

        const word = lineText.slice(start, end);
        return { word, start, end };
    }

    function getCursorPosition() {
        const sel = window.getSelection();
        if (!sel.rangeCount) return { x:0, y:0 };
        const range = sel.getRangeAt(0);
        const rect = range.getBoundingClientRect();
        return { x: rect.right + window.scrollX, y: rect.bottom + window.scrollY };
    }

    function applySuggestion(suggestion) {
        if (!editorElement) return;
        const sel = window.getSelection();
        if (!sel.rangeCount) return;

        const range = sel.getRangeAt(0);
        const node = range.startContainer;
        const lineElement = node.nodeType === Node.TEXT_NODE
            ? node.parentElement?.closest('.cm-line')
            : node.closest('.cm-line');
        if (!lineElement) return;

        const lineText = lineElement.textContent || '';
        const before = lineText.slice(0, currentStartOffset);
        const after = lineText.slice(currentEndOffset);
        const newText = before + suggestion + after;

        lineElement.innerText = newText;

        const newCursorPos = currentStartOffset + suggestion.length;
        const newRange = document.createRange();
        const textNode = lineElement.firstChild;
        if (textNode && textNode.nodeType === Node.TEXT_NODE) {
            newRange.setStart(textNode, newCursorPos);
            newRange.collapse(true);
            sel.removeAllRanges();
            sel.addRange(newRange);
        }

        if (menuElement) menuElement.style.display = 'none';
        editorElement.focus();
    }

    function onInput(e) {
        if (!editorElement) return;
        const { word, start, end } = getWordBeforeCursor();
        if (word.length === 0) {
            if (menuElement) menuElement.style.display = 'none';
            return;
        }
        currentStartOffset = start;
        currentEndOffset = end;

        const standardMatches = STANDARD_SUGGESTIONS.filter(s => s.startsWith(word) && s !== word).map(s => ({ text:s, type:'standard' }));
        const userMatches = userIdentifiers.filter(id => id.startsWith(word) && id !== word && !STANDARD_SUGGESTIONS.includes(id)).map(id => ({ text:id, type:'user' }));
        currentSuggestions = [...standardMatches, ...userMatches].slice(0,30);
        selectedIndex = 0;
        if (currentSuggestions.length > 0) {
            const pos = getCursorPosition();
            updateMenu(currentSuggestions, pos.x, pos.y);
        } else if (menuElement) menuElement.style.display = 'none';
    }

    function onKeyDown(e) {
        if (!menuElement || menuElement.style.display !== 'block') return;
        if (e.key === 'ArrowDown') {
            e.preventDefault(); e.stopPropagation(); e.stopImmediatePropagation();
            selectedIndex = (selectedIndex + 1) % currentSuggestions.length;
            const pos = getCursorPosition();
            updateMenu(currentSuggestions, pos.x, pos.y);
            return false;
        } else if (e.key === 'ArrowUp') {
            e.preventDefault(); e.stopPropagation(); e.stopImmediatePropagation();
            selectedIndex = (selectedIndex - 1 + currentSuggestions.length) % currentSuggestions.length;
            const pos = getCursorPosition();
            updateMenu(currentSuggestions, pos.x, pos.y);
            return false;
        } else if (e.key === 'Enter' || e.key === 'Tab') {
            if (e.key === confirmKey) {
                e.preventDefault(); e.stopPropagation(); e.stopImmediatePropagation();
                if (currentSuggestions[selectedIndex]) applySuggestion(currentSuggestions[selectedIndex].text);
                return false;
            } else {
                return true;
            }
        } else if (e.key === 'Escape') {
            e.preventDefault(); e.stopPropagation(); e.stopImmediatePropagation();
            menuElement.style.display = 'none';
            editorElement.focus();
            return false;
        }
    }

    function onClickOutside(e) {
        if (menuElement && !menuElement.contains(e.target) && e.target !== editorElement) {
            menuElement.style.display = 'none';
        }
    }

    function attachListeners() {
        if (!editorElement || listenersAttached) return;
        editorElement.addEventListener('input', inputListener);
        editorElement.addEventListener('input', scheduleParse);
        editorElement.addEventListener('keydown', keyDownListener, { capture: true });
        document.addEventListener('click', clickOutsideListener);
        listenersAttached = true;
    }

    function detachListeners() {
        if (!editorElement || !listenersAttached) return;
        editorElement.removeEventListener('input', inputListener);
        editorElement.removeEventListener('input', scheduleParse);
        editorElement.removeEventListener('keydown', keyDownListener, { capture: true });
        document.removeEventListener('click', clickOutsideListener);
        listenersAttached = false;
    }

    function saveConfirmKey(key) { localStorage.setItem('autocompleteConfirmKey', key); confirmKey = key; }
    function loadConfirmKey() {
        const saved = localStorage.getItem('autocompleteConfirmKey');
        confirmKey = (saved === 'Enter' || saved === 'Tab') ? saved : 'Enter';
        return confirmKey;
    }

    function enableAutocomplete() {
        if (!autocompleteEnabled) {
            autocompleteEnabled = true;
            loadConfirmKey();
        }

        const newEditor = findEditor();
        if (newEditor !== editorElement) {
            if (editorElement) detachListeners();
            editorElement = newEditor;
        }

        if (!editorElement) return;

        createMenu();
        refreshUserIdentifiers();

        if (!inputListener) {
            inputListener = onInput;
            keyDownListener = onKeyDown;
            clickOutsideListener = onClickOutside;
        }

        attachListeners();

        localStorage.setItem('autocompleteEnabled', 'true');
    }

    function disableAutocomplete() {
        if (!autocompleteEnabled) return;
        autocompleteEnabled = false;
        detachListeners();
        removeMenu();
        if (parseTimer) { clearTimeout(parseTimer); parseTimer = null; }
        localStorage.setItem('autocompleteEnabled', 'false');
    }

    function loadAutocompleteState() { return localStorage.getItem('autocompleteEnabled') === 'true'; }

    function initAutocomplete() {
        if (loadAutocompleteState()) {
            enableAutocomplete();
        }

        if (!bodyObserver) {
            bodyObserver = new MutationObserver(() => {
                if (!autocompleteEnabled) return;
                enableAutocomplete();
            });
            bodyObserver.observe(document.body, { childList: true, subtree: true });
        }

        if (!intervalTimer) {
            intervalTimer = setInterval(() => {
                if (loadAutocompleteState()) {
                    if (!autocompleteEnabled || !editorElement || !listenersAttached) {
                        enableAutocomplete();
                    }
                } else {
                    if (autocompleteEnabled) disableAutocomplete();
                }
            }, 2000);
        }
    }

    window.enableAutocomplete = enableAutocomplete;
    window.disableAutocomplete = disableAutocomplete;
    window.loadAutocompleteState = loadAutocompleteState;
    window.initAutocomplete = initAutocomplete;
    window.saveConfirmKey = saveConfirmKey;
    window.loadConfirmKey = loadConfirmKey;
})();