import { BaseModule } from './base-module.js';

class UIInputHandlerModule extends BaseModule {
    constructor() {
        super('ui-input-handler', 'UI Input Handler');
        
        // Explicitly declare ui-display-handler as a dependency
        this.dependencies = ['ui-display-handler', 'markup-parser', 'playback-coordinator'];
        
        // Input elements
        this.inputArea = null;
        this.playerInput = null;
        this.cursor = null;
        this.commandHistoryElement = null;
        
        // Input state
        this.inputEnabled = true;
        this.historyIndex = -1;
        this.commandHistory = [];
        this.inputBuffer = '';
        this.inputMode = 'text';
        
        // Bind methods using the parent class bindMethods utility
        this.bindMethods([
            'setupInputElements',
            'handlePlayerInput',
            'handleInputKeyDown',
            'positionCursor',
            'handleKeyboardInput',
            'submitCommand',
            'addToHistory',
            'bindHistoryToTurn',
            'highlightHistoryTurn',
            'formatCommandHistory',
            'resetCursorPosition',
            'focusInput',
            'setProcessState',
            'setInputAvailability',
            'setMode',
            'clearHistory'
        ]);
        
        console.log('UIInputHandler: Constructor initialized');
    }
    
    async initialize() {
        try {
            this.reportProgress(0, 'Initializing UI Input Handler');
            
            // Get display handler reference through the parent's getModule method
            this.displayHandler = this.getModule('ui-display-handler');
            if (!this.displayHandler) {
                console.error('UIInputHandler: Display handler module not found');
                return false;
            }
            
            this.reportProgress(30, 'Setting up keyboard listeners');
            
            // Use the parent's addEventListener for automatic cleanup
            this.addEventListener(document, 'keydown', this.handleKeyboardInput);
            
            this.reportProgress(60, 'Setting up input elements');
            
            this.setupInputElements();
            this.addEventListener(document, 'story:process-state', (event) => {
                this.setProcessState(event.detail?.state || 'ready', event.detail || {});
            });
            this.addEventListener(document, 'story:input-mode', (event) => {
                this.setMode(event.detail || 'text');
            });
            this.addEventListener(document, 'story:turn-start', (event) => {
                this.bindHistoryToTurn(event.detail?.turnId);
            });
            this.addEventListener(document, 'story:visible-turn', (event) => {
                this.highlightHistoryTurn(event.detail?.turnId);
            });
            
            this.reportProgress(100, 'UI Input Handler ready');
            return true;
        } catch (error) {
            console.error('Error initializing UI Input Handler:', error);
            return false;
        }
    }

    /**
     * Handle keyboard shortcuts and input globally
     * @param {KeyboardEvent} event - The keyboard event
     */
    handleKeyboardInput(event) {
        if (!this.playerInput) return;

        if (event.key === 'Escape') {
            this.playerInput.blur();
            return;
        }

        const optionsModal = document.getElementById('options-modal');
        if (optionsModal && optionsModal.style.display !== 'none') {
            return;
        }

        if (event.key === ' ' && (this.isPlaybackActive() || this.isSkippablePauseActive())) {
            document.dispatchEvent(new CustomEvent('ui:command', {
                detail: { type: 'continue', source: 'spacebar' }
            }));
        }

        if (event.key === 'Enter' && !event.shiftKey) {
            event.preventDefault();
            if (document.body.dataset.gameRunning !== 'true' || this.inputMode !== 'text') {
                return;
            }
            this.submitCommand();
            return;
        }

        if (event.ctrlKey || event.metaKey || event.altKey) {
            return;
        }

        if (event.key.length === 1 && document.activeElement !== this.playerInput) {
            if (document.body.dataset.gameRunning !== 'true') {
                return;
            }
            if (this.inputMode !== 'text') {
                return;
            }
            event.preventDefault();
            this.focusInput();
            const start = this.playerInput.selectionStart ?? this.playerInput.value.length;
            const end = this.playerInput.selectionEnd ?? start;
            this.playerInput.setRangeText(event.key, start, end, 'end');
            this.playerInput.dispatchEvent(new Event('input', { bubbles: true }));
        }
    }

    setupInputElements() {
        console.log("UIInputHandler: Setting up input elements in document flow");
        
        // Find the left page - this is created by the display handler
        const pageLeft = document.getElementById('page_left');
        if (!pageLeft) {
            console.error('UIInputHandler: Left page not found, cannot create input elements');
            return;
        }
        
        // Only create choices container if it doesn't already exist
        let choicesContainer = document.getElementById('choices');
        if (!choicesContainer) {
            choicesContainer = document.createElement('div');
            choicesContainer.id = 'choices';
            choicesContainer.className = 'container';
            
            // Use natural document flow, not absolute positioning
            // Do NOT add a separator here, as it already exists in the CSS
            
            pageLeft.appendChild(choicesContainer);
        }
        
        // Create command history container if needed
        let commandHistory = document.getElementById('command_history');
        if (!commandHistory) {
            commandHistory = document.createElement('div');
            commandHistory.id = 'command_history';
            choicesContainer.appendChild(commandHistory);
            this.commandHistoryElement = commandHistory;
        } else {
            this.commandHistoryElement = commandHistory;
        }
        
        // Create input container if needed
        let commandInput = document.getElementById('command_input');
        if (!commandInput) {
            commandInput = document.createElement('div');
            commandInput.id = 'command_input';
            choicesContainer.appendChild(commandInput);
        }
        
        // Create input wrapper if needed
        let inputWrapper = commandInput.querySelector('.input-wrapper');
        if (!inputWrapper) {
            inputWrapper = document.createElement('div');
            inputWrapper.className = 'input-wrapper';
            commandInput.appendChild(inputWrapper);
        }
        
        // Create the textarea if needed
        let playerInput = document.getElementById('player_input');
        if (!playerInput) {
            playerInput = document.createElement('textarea');
            playerInput.id = 'player_input';
            playerInput.rows = 1;
            playerInput.placeholder = 'What will you do?';
            
            // Fix horizontal scrolling by ensuring the textbox wraps text
            playerInput.style.overflowX = 'hidden';  
            playerInput.style.wordWrap = 'break-word';
            playerInput.style.whiteSpace = 'pre-wrap';
            
            inputWrapper.appendChild(playerInput);
        }
        this.playerInput = playerInput;
        this.applyTextInputAttributes(playerInput);
        
        // Create the cursor if needed
        let cursor = document.getElementById('cursor');
        if (!cursor) {
            cursor = document.createElement('span');
            cursor.id = 'cursor';
            inputWrapper.appendChild(cursor);
        }
        this.cursor = cursor;
        
        // Set up input event handlers
        if (playerInput) {
            playerInput.addEventListener('input', this.handlePlayerInput);
            playerInput.addEventListener('keydown', this.handleInputKeyDown);
            
            // Auto-resize input field
            playerInput.addEventListener('input', () => {
                playerInput.style.height = 'auto';
                playerInput.style.height = playerInput.scrollHeight + 'px';
            });
        }
        
        // Position the cursor
        if (playerInput && cursor) {
            this.positionCursor(playerInput, cursor);
            this.setProcessState('ready', { reason: 'input-initialized' });
            this.focusInput();
            requestAnimationFrame(() => this.focusInput());
            setTimeout(() => this.focusInput(), 250);
        }
        
        console.log('UIInputHandler: Input elements setup complete');
    }

    focusInput() {
        if (document.body.dataset.gameRunning !== 'true') {
            return;
        }

        if (!this.playerInput) {
            this.playerInput = document.getElementById('player_input');
        }

        if (this.playerInput && !this.playerInput.disabled) {
            this.playerInput.focus();
        }
    }

    setProcessState(state, detail = {}) {
        const knownStates = [
            'ready',
            'command-waiting',
            'waiting-generating',
            'playing-generating',
            'playing-ready'
        ];
        const nextState = knownStates.includes(state) ? state : 'ready';

        this.applyMouseCursor(nextState);

        if (this.cursor) {
            knownStates.forEach(value => this.cursor.classList.remove(`cursor-${value}`));
            this.cursor.classList.add(`cursor-${nextState}`);
            this.cursor.dataset.processState = nextState;
            this.cursor.setAttribute('aria-label', 'text input cursor');
            this.cursor.innerHTML = '';
        }

        console.log(`Cursor process state: ${nextState}`, detail);
        this.setInputAvailability(nextState === 'ready');
    }

    setInputAvailability(enabled) {
        this.inputEnabled = Boolean(enabled) && this.inputMode === 'text';
        const commandInput = document.getElementById('command_input');
        if (commandInput) {
            commandInput.classList.toggle('fading', !this.inputEnabled);
            commandInput.setAttribute('aria-hidden', this.inputEnabled ? 'false' : 'true');
        }

        if (this.playerInput) {
            this.playerInput.disabled = !this.inputEnabled;
            this.playerInput.readOnly = !this.inputEnabled;
            if (this.inputEnabled && document.body.dataset.gameRunning === 'true') {
                this.focusInput();
            }
        }
    }

    applyTextInputAttributes(playerInput) {
        if (!playerInput) return;

        const attributes = {
            autocomplete: 'off',
            autocorrect: 'off',
            autocapitalize: 'sentences',
            spellcheck: 'true',
            'aria-autocomplete': 'none',
            'data-form-type': 'other',
            'data-1p-ignore': 'true',
            'data-lpignore': 'true',
            'data-bwignore': 'true'
        };

        Object.entries(attributes).forEach(([name, value]) => {
            playerInput.setAttribute(name, value);
        });
    }

    setMode(mode) {
        this.inputMode = ['text', 'choice', 'end'].includes(mode) ? mode : 'text';
        this.setInputAvailability(this.inputMode === 'text');
    }

    applyMouseCursor(state) {
        const root = document.documentElement;
        if (!root) {
            return;
        }

        root.dataset.processState = state;
        const cursor = this.getMouseCursor(state);
        if (cursor) {
            root.style.setProperty('--process-cursor', cursor);
        } else {
            root.style.removeProperty('--process-cursor');
        }
    }

    getMouseCursor(state) {
        if (state === 'ready') {
            return '';
        }

        const svg = this.getMouseCursorSvg(state);
        const fallback = state === 'command-waiting' ? 'wait' : 'progress';
        return `url("${this.toCursorDataUrl(svg)}") 12 12, ${fallback}`;
    }

    getMouseCursorSvg(state) {
        const stroke = '#222222';
        const common = `xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 24 24" fill="none" stroke="${stroke}" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"`;
        const icons = {
            'command-waiting': `<svg ${common}><path d="M5 22h14"/><path d="M5 2h14"/><path d="M17 22v-4.172a2 2 0 0 0-.586-1.414L12 12l-4.414 4.414A2 2 0 0 0 7 17.828V22"/><path d="M7 2v4.172a2 2 0 0 0 .586 1.414L12 12l4.414-4.414A2 2 0 0 0 17 6.172V2"/></svg>`,
            'waiting-generating': `<svg ${common}><path d="M12 2v4"/><path d="M12 18v4"/><path d="m4.93 4.93 2.83 2.83"/><path d="m16.24 16.24 2.83 2.83"/><path d="M2 12h4"/><path d="M18 12h4"/><path d="m4.93 19.07 2.83-2.83"/><path d="m16.24 7.76 2.83-2.83"/></svg>`,
            'playing-generating': `<svg ${common}><path d="M11 5 6 9H2v6h4l5 4z"/><path d="M15.54 8.46a5 5 0 0 1 0 7.07"/><path d="M19.07 4.93a10 10 0 0 1 0 14.14"/><path d="M12 2v3"/><path d="M12 19v3"/></svg>`,
            'playing-ready': `<svg ${common}><path d="M11 5 6 9H2v6h4l5 4z"/><path d="M15.54 8.46a5 5 0 0 1 0 7.07"/><path d="M19.07 4.93a10 10 0 0 1 0 14.14"/></svg>`
        };

        return icons[state] || icons['waiting-generating'];
    }

    toCursorDataUrl(svg) {
        return `data:image/svg+xml,${encodeURIComponent(svg.replace(/\s+/g, ' ').trim())}`;
    }

    /**
     * Handle player input changes
     * @param {Event} e - Input event
     */
    handlePlayerInput(e) {
        if (!this.playerInput) return;
        
        // Auto-resize the input field based on content
        this.playerInput.style.height = 'auto';
        this.playerInput.style.height = `${this.playerInput.scrollHeight}px`;
        
        // Update the cursor position with the current input text
        if (this.cursor) {
            this.positionCursor(this.playerInput, this.cursor);
        }
        
        // Use the parent class dispatchEvent method instead of custom _dispatchModuleEvent
        this.dispatchEvent('ui:input:change', { 
            text: this.playerInput.value
        });
    }

    /**
     * Handle key down events in the input field
     * @param {KeyboardEvent} e - Keyboard event
     */
    handleInputKeyDown(e) {
        if (!this.playerInput) return;
        
        // Check for Enter key
        if (e.key === 'Enter') {
            if (!e.shiftKey) {
                // Prevent default (new line) if not holding shift
                e.preventDefault();
                
                // Submit command
                this.submitCommand();
            }
        }
    }
    
    /**
     * Submit the current input as a command
     */
    submitCommand() {
        if (!this.playerInput || !this.playerInput.value.trim()) return;
        if (document.body.dataset.gameRunning !== 'true' || !this.inputEnabled) return;
        if (this.inputMode !== 'text') return;
        
        const command = this.playerInput.value.trim();
        console.log(`UIInputHandler: Submitting command: "${command}"`);
        
        this.addToHistory(command);
        
        this.dispatchEvent('ui:command', {
            type: 'input',
            text: command
        });
        
        // Clear input field
        this.playerInput.value = '';
        this.playerInput.style.height = 'auto';
        
        // Update cursor position
        if (this.cursor) {
            this.positionCursor(this.playerInput, this.cursor);
        }
        
        // Focus input field
        this.playerInput.focus();
    }

    clearHistory() {
        this.commandHistory = [];
        this.historyIndex = -1;
        if (!this.commandHistoryElement) {
            this.commandHistoryElement = document.getElementById('command_history');
        }
        if (this.commandHistoryElement) {
            this.commandHistoryElement.innerHTML = '';
        }
    }
    
    /**
     * Add command to history
     * @param {string} command - Command to add to history
     */
    addToHistory(command) {
        // Add to history array
        this.commandHistory.push(command);
        
        // Limit history size
        if (this.commandHistory.length > 50) {
            this.commandHistory.shift();
        }
        
        // Reset history index
        this.historyIndex = -1;
        
        // Update visual history if element exists
        if (this.commandHistoryElement && this.commandHistoryElement.appendChild) {
            const historyItem = document.createElement('div');
            historyItem.className = 'history-item';
            historyItem.dataset.turnId = 'pending';
            historyItem.innerHTML = `&gt; ${this.formatCommandHistory(command)}`;
            historyItem.addEventListener('click', () => {
                const turnId = historyItem.dataset.turnId;
                if (!turnId || turnId === 'pending') return;
                document.dispatchEvent(new CustomEvent('story:scroll-to-turn', {
                    detail: { turnId: Number(turnId) }
                }));
            });
            this.commandHistoryElement.appendChild(historyItem);
            
            // Limit visible history items
            while (this.commandHistoryElement.childElementCount > 10) {
                this.commandHistoryElement.removeChild(this.commandHistoryElement.firstChild);
            }
            
            // Scroll to bottom
            this.commandHistoryElement.scrollTop = this.commandHistoryElement.scrollHeight;
        }
    }

    bindHistoryToTurn(turnId) {
        if (!Number.isInteger(Number(turnId))) return;
        if (!this.commandHistoryElement) {
            this.commandHistoryElement = document.getElementById('command_history');
        }
        const pending = this.commandHistoryElement?.querySelector('.history-item[data-turn-id="pending"]');
        if (!pending) return;
        pending.dataset.turnId = String(turnId);
        pending.classList.remove('history-pending');
    }

    highlightHistoryTurn(turnId) {
        if (!this.commandHistoryElement || turnId == null) return;
        const id = String(turnId);
        this.commandHistoryElement.querySelectorAll('.history-item').forEach((item) => {
            item.classList.toggle('active', item.dataset.turnId === id);
        });
    }

    formatCommandHistory(command) {
        const parser = this.getModule('markup-parser') || window.MarkupParser;
        if (parser && typeof parser.markdownToHtml === 'function') {
            return parser.markdownToHtml(command);
        }

        return String(command)
            .replace(/&/g, '&amp;')
            .replace(/</g, '&lt;')
            .replace(/>/g, '&gt;');
    }

    isPlaybackActive() {
        const playbackCoordinator = this.getModule('playback-coordinator') || window.PlaybackCoordinator;
        return Boolean(playbackCoordinator && playbackCoordinator.isPlaying);
    }

    isSkippablePauseActive() {
        return document.documentElement.dataset.skippablePause === 'true';
    }

    /**
     * Resets the cursor position to the start.
     */
    resetCursorPosition() {
        if (this.cursor) {
            this.cursor.style.left = '0px';
            // Adjust top based on computed style padding or a default
            const computedStyle = window.getComputedStyle(this.playerInput);
            const paddingTop = parseFloat(computedStyle.paddingTop) || 6;
            this.cursor.style.top = `${paddingTop}px`;
        }
    }


    /**
     * Position cursor based on input text position
     * @param {HTMLTextAreaElement} inputElement - The input element
     * @param {HTMLElement} cursorElement - The visual cursor element
     */
    positionCursor(inputElement, cursorElement) {
        if (!inputElement || !cursorElement) return;
    
        this.cursor = cursorElement;
        this.playerInput = inputElement;
        const updatePosition = () => {
            try {
                const input = this.playerInput;
                const cursor = this.cursor;
                const caretPosition = input.selectionStart || 0;
                const inputText = input.value;
            
                // If no text, position cursor at the beginning based on padding
                if (inputText.length === 0 && caretPosition === 0) {
                    this.resetCursorPosition();
                    return;
                }
            
                // Create a temporary measurement div
                const div = document.createElement('div');
                const style = getComputedStyle(input);
            
                // Apply relevant styles from the textarea to the div
                div.style.position = 'absolute';
                div.style.top = '-9999px';
                div.style.left = '-9999px';
                div.style.width = style.width;
                div.style.height = 'auto';
                div.style.padding = style.padding;
                div.style.border = style.border;
                div.style.fontFamily = style.fontFamily;
                div.style.fontSize = style.fontSize;
                div.style.fontWeight = style.fontWeight;
                div.style.lineHeight = style.lineHeight;
                div.style.whiteSpace = 'pre-wrap';
                div.style.wordWrap = 'break-word';
                div.style.boxSizing = style.boxSizing;
            
                // Create spans for text before and after the caret, and a marker span
                const preCaretText = document.createTextNode(inputText.substring(0, caretPosition));
                const caretMarker = document.createElement('span');
                caretMarker.innerHTML = '&nbsp;'; // Use non-breaking space for measurement
                const postCaretText = document.createTextNode(inputText.substring(caretPosition));
            
                // Append spans to the div
                div.appendChild(preCaretText);
                div.appendChild(caretMarker);
                div.appendChild(postCaretText);
            
                // Append div to body for measurement
                document.body.appendChild(div);
            
                // Get position relative to the div's content box
                const markerRect = caretMarker.getBoundingClientRect();
                const divRect = div.getBoundingClientRect();
            
                // Calculate position relative to the input's top-left, considering scroll
                const cursorLeft = markerRect.left - divRect.left;
                const cursorTop = markerRect.top - divRect.top - input.scrollTop;
            
                // Set cursor position
                cursor.style.left = `${cursorLeft}px`;
                cursor.style.top = `${cursorTop}px`;
            
                // Clean up the temporary div
                document.body.removeChild(div);            } catch (error) {
                console.error('Error positioning cursor:', error);
            }
        };
        
        // Update on various events
        inputElement.addEventListener('input', updatePosition);
        inputElement.addEventListener('click', updatePosition);
        inputElement.addEventListener('keyup', updatePosition);
        inputElement.addEventListener('focus', updatePosition);
        
        // Initial position update
        updatePosition();
    }
}

// Create the singleton instance
const uiInputHandler = new UIInputHandlerModule();

// Export the module
export { uiInputHandler as UIInputHandler };
