class ClaudeCodeWebInterface {
    constructor() {
        this.terminal = null;
        this.fitAddon = null;
        this.webLinksAddon = null;
        this.socket = null;
        this.connectionId = null;
        this.currentClaudeSessionId = null;
        this.currentClaudeSessionName = null;
        this.reconnectAttempts = 0;
        this.maxReconnectAttempts = 5;
        this.reconnectDelay = 1000;
        this.folderMode = true; // Always use folder mode
        this.currentFolderPath = null;
        this.claudeSessions = [];
        this.isCreatingNewSession = false;
        this.isMobile = this.detectMobile();
        this.currentMode = 'chat';
        this.planDetector = null;
        // Aliases for assistants (populated from /api/config)
        this.aliases = { claude: 'Claude', codex: 'Codex' };


        // Initialize the session tab manager
        this.sessionTabManager = null;

        // Usage stats
        this.usageStats = null;
        this.usageUpdateTimer = null;
        this.sessionStats = null;
        this.sessionTimer = null;
        this.sessionTimerInterval = null;

        this.splitContainer = null;
        this.init();
    }

    // Simple fetch wrapper (no auth)
    async authFetch(url, options = {}) {
        return fetch(url, options);
    }

    async init() {
        // Read LaunchPod settings (set by the host React app)
        this.loadLaunchPodSettings();

        await this.loadConfig();
        this.setupTerminal();
        this.setupUI();
        this.setupPlanDetector();
        this.loadSettings();
        this.applyAliasesToUI();
        this.disablePullToRefresh();

        // Show loading while we initialize
        this.showOverlay('loadingSpinner');

        // Initialize the session tab manager and wait for sessions to load
        this.sessionTabManager = new SessionTabManager(this);
        await this.sessionTabManager.init();

        // Initialize split container
        if (window.SplitContainer) {
            this.splitContainer = new window.SplitContainer(this);
            this.splitContainer.setupDropZones();
        }

        // Show mode switcher on mobile
        if (this.isMobile) {
            this.showModeSwitcher();
        }

        // Check if there are existing sessions
        console.log('[Init] Checking sessions, tabs.size:', this.sessionTabManager.tabs.size);
        if (this.sessionTabManager.tabs.size > 0) {
            console.log('[Init] Found sessions, switching to first tab...');
            // Sessions exist - switch to the first one (this will handle connecting)
            const firstTabId = this.sessionTabManager.tabs.keys().next().value;
            console.log('[Init] Switching to tab:', firstTabId);
            await this.sessionTabManager.switchToTab(firstTabId);

            // Hide overlay completely since we have sessions
            console.log('[Init] About to hide overlay');
            this.hideOverlay();
            console.log('[Init] Overlay should be hidden now');
        } else {
            console.log('[Init] No sessions found, auto-creating first session');
            // No sessions - auto-create first session
            this.hideOverlay();
            await this.sessionTabManager.createNewSession('Session 1');
        }

        window.addEventListener('resize', () => {
            this.fitTerminal();
        });

        window.addEventListener('beforeunload', () => {
            this.disconnect();
        });
    }

    loadLaunchPodSettings() {
        try {
            const raw = localStorage.getItem('launchpod-settings');
            if (raw) {
                const s = JSON.parse(raw);
                if (s.workingDir) this.selectedWorkingDir = s.workingDir;
                if (s.defaultAgent) this._launchpadDefaultAgent = s.defaultAgent;
            }
        } catch { /* ignore */ }
    }

    async loadConfig() {
        try {
            const res = await fetch('/terminal/api/config');
            if (res.ok) {
                const cfg = await res.json();
                if (cfg?.aliases) {
                    this.aliases = {
                        claude: cfg.aliases.claude || 'Claude',
                        codex: cfg.aliases.codex || 'Codex'
                    };
                }
                if (typeof cfg.folderMode === 'boolean') {
                    this.folderMode = cfg.folderMode;
                }
            }
        } catch (_) { /* best-effort */ }
    }

    getAlias(kind) {
        if (this.aliases && this.aliases[kind]) {
            return this.aliases[kind];
        }
        // Default aliases
        if (kind === 'codex') return 'Codex';
        if (kind === 'agent') return 'Cursor';
        return 'Claude';
    }

    applyAliasesToUI() {
        // Start prompt buttons (removed from DOM, but keep safe)
        const startBtn = document.getElementById('startBtn');
        const dangerousSkipBtn = document.getElementById('dangerousSkipBtn');
        const startCodexBtn = document.getElementById('startCodexBtn');
        const dangerousCodexBtn = document.getElementById('dangerousCodexBtn');
        const startAgentBtn = document.getElementById('startAgentBtn');
        if (startBtn) startBtn.textContent = `Start ${this.getAlias('claude')}`;
        if (dangerousSkipBtn) dangerousSkipBtn.textContent = `Dangerous ${this.getAlias('claude')}`;
        if (startCodexBtn) startCodexBtn.textContent = `Start ${this.getAlias('codex')}`;
        if (dangerousCodexBtn) dangerousCodexBtn.textContent = `Dangerous ${this.getAlias('codex')}`;
        if (startAgentBtn) startAgentBtn.textContent = `Start ${this.getAlias('agent')}`;
    }

    detectMobile() {
        // Check for touch capability and common mobile user agents
        const hasTouchScreen = 'ontouchstart' in window ||
                              navigator.maxTouchPoints > 0 ||
                              navigator.msMaxTouchPoints > 0;

        const mobileUserAgent = /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent);

        // Also check viewport width for tablets
        const smallViewport = window.innerWidth <= 1024;

        return hasTouchScreen && (mobileUserAgent || smallViewport);
    }

    disablePullToRefresh() {
        // Prevent pull-to-refresh on touchmove
        let lastY = 0;

        document.addEventListener('touchstart', (e) => {
            lastY = e.touches[0].clientY;
        }, { passive: false });

        document.addEventListener('touchmove', (e) => {
            const y = e.touches[0].clientY;
            const scrollTop = window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop || 0;

            // Prevent pull-to-refresh when at the top and trying to scroll up
            if (scrollTop === 0 && y > lastY) {
                e.preventDefault();
            }

            lastY = y;
        }, { passive: false });

        // Also prevent overscroll on the terminal element
        const terminal = document.getElementById('terminal');
        if (terminal) {
            terminal.addEventListener('touchmove', (e) => {
                e.stopPropagation();
            }, { passive: false });
        }
    }

    showModeSwitcher() {
        // Create mode switcher button if it doesn't exist
        if (!document.getElementById('modeSwitcher')) {
            const modeSwitcher = document.createElement('div');
            modeSwitcher.id = 'modeSwitcher';
            modeSwitcher.className = 'mode-switcher';
            modeSwitcher.innerHTML = `
                <button id="escapeBtn" class="escape-btn" title="Send Escape key">
                    <svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
                        <circle cx="12" cy="12" r="10"/>
                        <line x1="12" y1="8" x2="12" y2="12"/>
                        <line x1="12" y1="16" x2="12.01" y2="16"/>
                    </svg>
                </button>
                <button id="modeSwitcherBtn" class="mode-switcher-btn" data-mode="${this.currentMode}" title="Switch mode (Shift+Tab)">
                    <svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
                        <rect x="3" y="3" width="18" height="18" rx="2" ry="2"/>
                        <line x1="9" y1="9" x2="15" y2="15"/>
                        <line x1="15" y1="9" x2="9" y2="15"/>
                    </svg>
                </button>
            `;
            document.body.appendChild(modeSwitcher);

            // Add event listener for mode switcher
            document.getElementById('modeSwitcherBtn').addEventListener('click', () => {
                this.switchMode();
            });

            // Add event listener for escape button
            document.getElementById('escapeBtn').addEventListener('click', () => {
                this.sendEscape();
            });
        }
    }

    sendEscape() {
        // Send ESC key to terminal
        if (this.socket && this.socket.readyState === WebSocket.OPEN) {
            // Send ESC key (ASCII 27 or \x1b)
            this.send({ type: 'input', data: '\x1b' });
        }

        // Add visual feedback
        const btn = document.getElementById('escapeBtn');
        if (btn) {
            btn.classList.add('pressed');
            setTimeout(() => {
                btn.classList.remove('pressed');
            }, 200);
        }
    }

    switchMode() {
        // Toggle between modes
        const modes = ['chat', 'code', 'plan'];
        const currentIndex = modes.indexOf(this.currentMode);
        const nextIndex = (currentIndex + 1) % modes.length;
        this.currentMode = modes[nextIndex];

        // Update button data attribute for styling
        const btn = document.getElementById('modeSwitcherBtn');
        if (btn) {
            btn.setAttribute('data-mode', this.currentMode);
            btn.title = `Switch mode (Shift+Tab) - Current: ${this.currentMode.charAt(0).toUpperCase() + this.currentMode.slice(1)}`;
        }

        // Send Shift+Tab to terminal to trigger actual mode switch in Claude Code
        if (this.socket && this.socket.readyState === WebSocket.OPEN) {
            // Send Shift+Tab key combination (ESC[Z is the terminal sequence for Shift+Tab)
            this.send({ type: 'input', data: '\x1b[Z' });
        }

        // Add visual feedback
        if (btn) {
            btn.classList.add('switching');
            setTimeout(() => {
                btn.classList.remove('switching');
            }, 300);
        }
    }

    setupTerminal() {
        // Adjust font size for mobile devices
        const isMobile = this.detectMobile();
        const fontSize = isMobile ? 12 : 14;

        this.terminal = new Terminal({
            fontSize: fontSize,
            fontFamily: 'JetBrains Mono, Fira Code, Monaco, Consolas, monospace',
            theme: {
                background: 'transparent',
                foreground: '#f0f6fc',
                cursor: '#58a6ff',
                cursorAccent: '#0d1117',
                selection: 'rgba(88, 166, 255, 0.3)',
                black: '#484f58',
                red: '#ff7b72',
                green: '#7ee787',
                yellow: '#ffa657',
                blue: '#79c0ff',
                magenta: '#d2a8ff',
                cyan: '#a5f3fc',
                white: '#b1bac4',
                brightBlack: '#6e7681',
                brightRed: '#ffa198',
                brightGreen: '#56d364',
                brightYellow: '#ffdf5d',
                brightBlue: '#79c0ff',
                brightMagenta: '#d2a8ff',
                brightCyan: '#a5f3fc',
                brightWhite: '#f0f6fc'
            },
            allowProposedApi: true,
            scrollback: 10000,
            rightClickSelectsWord: false,
            allowTransparency: true,
            // Disable focus tracking to prevent ^[[I and ^[[O sequences
            windowOptions: {
                reportFocus: false
            }
        });

        this.fitAddon = new FitAddon.FitAddon();
        this.webLinksAddon = new WebLinksAddon.WebLinksAddon();

        this.terminal.loadAddon(this.fitAddon);
        this.terminal.loadAddon(this.webLinksAddon);

        this.terminal.open(document.getElementById('terminal'));
        this.fitTerminal();

        this.terminal.onData((data) => {
            if (this.socket && this.socket.readyState === WebSocket.OPEN) {
                // Filter out focus tracking sequences before sending
                const filteredData = data.replace(/\x1b\[\[?[IO]/g, '');
                if (filteredData) {
                    this.send({ type: 'input', data: filteredData });
                }
            }
        });

        this.terminal.onResize(({ cols, rows }) => {
            if (this.socket && this.socket.readyState === WebSocket.OPEN) {
                this.send({ type: 'resize', cols, rows });
            }
        });
    }

    showSessionSelectionModal() {
        // Create a simple modal to show existing sessions
        const modal = document.createElement('div');
        modal.className = 'session-modal active';
        modal.id = 'sessionSelectionModal';
        modal.innerHTML = `
            <div class="modal-content">
                <div class="modal-header">
                    <h2>Select a Session</h2>
                    <button class="close-btn" id="closeSessionSelection">&times;</button>
                </div>
                <div class="modal-body">
                    <div class="session-list">
                        ${this.claudeSessions.map(session => {
                            const statusIcon = `<span class=\"dot ${session.active ? 'dot-on' : 'dot-idle'}\"></span>`;
                            const clientsText = session.connectedClients === 1 ? '1 client' : `${session.connectedClients} clients`;
                            return `
                                <div class="session-item" data-session-id="${session.id}" style="cursor: pointer; padding: 15px; border: 1px solid #333; border-radius: 5px; margin-bottom: 10px;">
                                    <div class="session-info">
                                        <span class="session-status">${statusIcon}</span>
                                        <div class="session-details">
                                            <div class="session-name">${session.name}</div>
                                            <div class="session-meta">${clientsText} • ${new Date(session.created).toLocaleString()}</div>
                                            ${session.workingDir ? `<div class=\"session-folder\" title=\"${session.workingDir}\"><span class=\"icon\" aria-hidden=\"true\">${window.icons?.folder?.(14) || ''}</span> ${session.workingDir}</div>` : ''}
                                        </div>
                                    </div>
                                </div>
                            `;
                        }).join('')}
                    </div>
                </div>
            </div>
        `;

        document.body.appendChild(modal);

        // Add event listeners
        modal.querySelectorAll('.session-item').forEach(item => {
            item.addEventListener('click', async () => {
                const sessionId = item.dataset.sessionId;
                await this.joinSession(sessionId);
                modal.remove();
            });
        });

        document.getElementById('closeSessionSelection').addEventListener('click', () => {
            modal.remove();
            this.hideOverlay();
        });

        // Close on background click
        modal.addEventListener('click', (e) => {
            if (e.target === modal) {
                modal.remove();
                this.hideOverlay();
            }
        });
    }

    setupUI() {
        const startBtn = document.getElementById('startBtn');
        const dangerousSkipBtn = document.getElementById('dangerousSkipBtn');
        const startCodexBtn = document.getElementById('startCodexBtn');
        const dangerousCodexBtn = document.getElementById('dangerousCodexBtn');
        const startAgentBtn = document.getElementById('startAgentBtn');
        // Mobile menu buttons (keeping for mobile support)
        const closeMenuBtn = document.getElementById('closeMenuBtn');

        if (startBtn) startBtn.addEventListener('click', () => this.startClaudeSession());
        if (dangerousSkipBtn) dangerousSkipBtn.addEventListener('click', () => this.startClaudeSession({ dangerouslySkipPermissions: true }));
        if (startCodexBtn) startCodexBtn.addEventListener('click', () => this.startCodexSession());
        if (dangerousCodexBtn) dangerousCodexBtn.addEventListener('click', () => this.startCodexSession({ dangerouslySkipPermissions: true }));
        if (startAgentBtn) startAgentBtn.addEventListener('click', () => this.startAgentSession());
        // Mobile menu event listeners
        if (closeMenuBtn) closeMenuBtn.addEventListener('click', () => this.closeMobileMenu());

        // Mobile sessions button
        const sessionsBtnMobile = document.getElementById('sessionsBtnMobile');
        if (sessionsBtnMobile) {
            sessionsBtnMobile.addEventListener('click', () => {
                this.closeMobileMenu();
            });
        }

        this.setupErrorBanner();
    }

    setupErrorBanner() {
        const closeBtn = document.getElementById('errorBannerClose');
        if (closeBtn) {
            closeBtn.addEventListener('click', () => {
                const banner = document.getElementById('errorBanner');
                if (banner) banner.style.display = 'none';
            });
        }
    }

    connect(sessionId = null) {
        return new Promise((resolve, reject) => {
            const protocol = location.protocol === 'https:' ? 'wss:' : 'ws:';
            let wsUrl = `${protocol}//${location.host}/terminal/ws`;
            if (sessionId) {
                wsUrl += `?sessionId=${sessionId}`;
            }

            this.updateStatus('Connecting...');
            // Only show loading spinner if overlay is already visible
            // Don't force it to show if we're handling restored sessions
            if (document.getElementById('overlay').style.display !== 'none') {
                this.showOverlay('loadingSpinner');
            }

            try {
                this.socket = new WebSocket(wsUrl);

                this.socket.onopen = () => {
                    this.reconnectAttempts = 0;
                    this.updateStatus('Connected');
                    console.log('Connected to server');

                    // Load available sessions
                    this.loadSessions();

                    // Only show start prompt if we don't have sessions AND no current session
                    // The init() method will handle showing/hiding overlays for restored sessions
                    if (!this.currentClaudeSessionId && (!this.sessionTabManager || this.sessionTabManager.tabs.size === 0)) {
                        // No start prompt overlay - auto-create session instead
                        this.hideOverlay();
                    }

                    resolve();
                };

            this.socket.onmessage = (event) => {
                this.handleMessage(JSON.parse(event.data));
            };

            this.socket.onclose = (event) => {
                this.updateStatus('Disconnected');

                if (!event.wasClean && this.reconnectAttempts < this.maxReconnectAttempts) {
                    setTimeout(() => this.reconnect(), this.reconnectDelay * Math.pow(2, this.reconnectAttempts));
                    this.reconnectAttempts++;
                } else {
                    this.showError('Connection lost. Please check your network and try again.');
                }
            };

            this.socket.onerror = (error) => {
                console.error('WebSocket error:', error);
                this.showError('Failed to connect to the server');
                reject(error);
            };

        } catch (error) {
            console.error('Failed to create WebSocket:', error);
            this.showError('Failed to create connection');
            reject(error);
        }
        });
    }

    disconnect() {
        if (this.socket) {
            this.socket.close();
            this.socket = null;
        }
    }

    reconnect() {
        this.disconnect();
        setTimeout(() => {
            this.connect().catch(err => console.error('Reconnection failed:', err));
        }, 1000);
    }

    send(data) {
        if (this.socket && this.socket.readyState === WebSocket.OPEN) {
            this.socket.send(JSON.stringify(data));
        }
    }

    handleMessage(message) {
        switch (message.type) {
            case 'connected':
                this.connectionId = message.connectionId;
                break;

            case 'session_created':
                this.currentClaudeSessionId = message.sessionId;
                this.currentClaudeSessionName = message.sessionName;
                this.updateWorkingDir(message.workingDir);
                this.updateSessionButton(message.sessionName);
                this.loadSessions();

                // Add tab for the new session if using tab manager
                if (this.sessionTabManager) {
                    this.sessionTabManager.addTab(message.sessionId, message.sessionName, 'idle', message.workingDir);
                    this.sessionTabManager.switchToTab(message.sessionId);
                }

                // Show start prompt so user can pick agent
                this.showStartPrompt(message.workingDir);
                break;

            case 'session_joined':
                console.log('[session_joined] Message received, active:', message.active, 'tabs:', this.sessionTabManager?.tabs.size);
                this.currentClaudeSessionId = message.sessionId;
                this.currentClaudeSessionName = message.sessionName;
                this.updateWorkingDir(message.workingDir);
                this.updateSessionButton(message.sessionName);

                // Update tab status
                if (this.sessionTabManager) {
                    this.sessionTabManager.updateTabStatus(message.sessionId, message.active ? 'active' : 'idle');
                }

                // Notify split container of session change
                if (this.splitContainer) {
                    this.splitContainer.onTabSwitch(message.sessionId);
                }

                // Resolve pending join promise if it exists
                if (this.pendingJoinResolve && this.pendingJoinSessionId === message.sessionId) {
                    this.pendingJoinResolve();
                    this.pendingJoinResolve = null;
                    this.pendingJoinSessionId = null;
                }

                // Replay output buffer if available
                if (message.outputBuffer && message.outputBuffer.length > 0) {
                    this.terminal.clear();
                    message.outputBuffer.forEach(data => {
                        // Filter out focus tracking sequences (^[[I and ^[[O)
                        const filteredData = data.replace(/\x1b\[\[?[IO]/g, '');
                        this.terminal.write(filteredData);
                    });
                }

                // Show appropriate UI based on session state
                console.log('[session_joined] Checking if should show overlay. Active:', message.active);
                if (message.active) {
                    console.log('[session_joined] Session is active, hiding overlay');
                    this.hideOverlay();
                    // Don't auto-focus to avoid focus tracking sequences
                } else {
                    // Session exists but Claude is not running
                    const isNewSession = !message.outputBuffer || message.outputBuffer.length === 0;

                    if (isNewSession) {
                        console.log('[session_joined] New session detected, showing start prompt');
                        this.showStartPrompt(message.workingDir);
                    } else {
                        console.log('[session_joined] Existing session with stopped Claude, showing restart info');
                        this.terminal.writeln(`\r\n\x1b[33m${this.getAlias('claude')} has stopped in this session. Use the + button to start a new session.\x1b[0m`);
                        this.hideOverlay();
                    }
                }
                break;

            case 'session_left':
                this.currentClaudeSessionId = null;
                this.currentClaudeSessionName = null;
                this.updateSessionButton('Sessions');
                this.terminal.clear();

                // Update tab status
                if (this.sessionTabManager && message.sessionId) {
                    this.sessionTabManager.updateTabStatus(message.sessionId, 'disconnected');
                }

                // Hide overlay when switching tabs
                if (this.sessionTabManager && this.sessionTabManager.tabs.size > 0) {
                    this.hideOverlay();
                }
                break;

            case 'claude_started':
                this.hideOverlay();
                this.hideStartPrompt();
                this.loadSessions();
                this.requestUsageStats();

                // Update tab status to active
                if (this.sessionTabManager && this.currentClaudeSessionId) {
                    this.sessionTabManager.updateTabStatus(this.currentClaudeSessionId, 'active');
                }
                break;
            case 'codex_started':
                this.hideOverlay();
                this.hideStartPrompt();
                this.loadSessions();
                this.requestUsageStats();
                if (this.sessionTabManager && this.currentClaudeSessionId) {
                    this.sessionTabManager.updateTabStatus(this.currentClaudeSessionId, 'active');
                }
                break;
            case 'agent_started':
                this.hideOverlay();
                this.hideStartPrompt();
                this.loadSessions();
                this.requestUsageStats();
                if (this.sessionTabManager && this.currentClaudeSessionId) {
                    this.sessionTabManager.updateTabStatus(this.currentClaudeSessionId, 'active');
                }
                break;

            case 'claude_stopped':
                this.terminal.writeln(`\r\n\x1b[33m${this.getAlias('claude')} stopped\x1b[0m`);
                this.hideOverlay();
                this.loadSessions();
                break;
            case 'codex_stopped':
                this.terminal.writeln(`\r\n\x1b[33mCodex Code stopped\x1b[0m`);
                this.hideOverlay();
                this.loadSessions();
                break;
            case 'agent_stopped':
                this.terminal.writeln(`\r\n\x1b[33m${this.getAlias('agent')} stopped\x1b[0m`);
                this.hideOverlay();
                this.loadSessions();
                break;

            case 'output':
                // Filter out focus tracking sequences (^[[I and ^[[O)
                const filteredData = message.data.replace(/\x1b\[\[?[IO]/g, '');
                this.terminal.write(filteredData);

                // Update session activity indicator with output data
                if (this.sessionTabManager && this.currentClaudeSessionId) {
                    this.sessionTabManager.markSessionActivity(this.currentClaudeSessionId, true, message.data);
                }

                // Pass output to plan detector
                if (this.planDetector) {
                    this.planDetector.processOutput(message.data);
                }
                break;

            case 'exit':
                this.terminal.writeln(`\r\n\x1b[33m${this.getAlias('claude')} exited with code ${message.code}\x1b[0m`);

                // Mark session as error if non-zero exit code
                if (this.sessionTabManager && this.currentClaudeSessionId && message.code !== 0) {
                    this.sessionTabManager.markSessionError(this.currentClaudeSessionId, true);
                }

                this.hideOverlay();
                this.loadSessions();
                break;

            case 'error':
                this.showError(message.message);

                // Mark session as having an error
                if (this.sessionTabManager && this.currentClaudeSessionId) {
                    this.sessionTabManager.markSessionError(this.currentClaudeSessionId, true);
                }
                break;

            case 'info':
                // Info message
                if (message.message.includes('not running')) {
                    this.hideOverlay();
                }
                break;

            case 'session_deleted':
                this.showError(message.message);
                this.currentClaudeSessionId = null;
                this.currentClaudeSessionName = null;
                this.updateSessionButton('Sessions');
                if (this.sessionTabManager && message.sessionId) {
                    this.sessionTabManager.closeSession(message.sessionId, { skipServerRequest: true });
                }
                this.loadSessions();
                break;

            case 'pong':
                break;

            case 'usage_update':
                this.updateUsageDisplay(
                    message.sessionStats,
                    message.dailyStats,
                    message.sessionTimer,
                    message.analytics,
                    message.burnRate,
                    message.plan,
                    message.limits
                );
                break;

            default:
                console.log('Unknown message type:', message.type);
        }
    }

    autoStartDefaultAgent() {
        const agent = this._launchpadDefaultAgent || this.loadSettings().defaultAgent || 'claude';

        switch (agent) {
            case 'codex':
                this.startCodexSession();
                break;
            case 'agent':
                this.startAgentSession();
                break;
            default:
                this.startClaudeSession();
                break;
        }
    }

    showStartPrompt(workingDir) {
        this.hideOverlay();
        const panel = document.getElementById('startPromptPanel');
        const dirEl = document.getElementById('startPromptDir');
        if (panel) panel.style.display = 'flex';
        if (dirEl) dirEl.textContent = workingDir || 'Default directory';
    }

    hideStartPrompt() {
        const panel = document.getElementById('startPromptPanel');
        if (panel) panel.style.display = 'none';
    }

    startClaudeSession(options = {}) {
        // If no session, create one first
        if (!this.currentClaudeSessionId) {
            const sessionName = `Session ${new Date().toLocaleString()}`;
            this.send({
                type: 'create_session',
                name: sessionName,
                workingDir: this.selectedWorkingDir
            });
            // Wait for session creation, then start Claude
            setTimeout(() => {
                this.send({ type: 'start_claude', options });
            }, 500);
        } else {
            this.send({ type: 'start_claude', options });
        }

        this.hideStartPrompt();
        this.showOverlay('loadingSpinner');
        const loadingText = options.dangerouslySkipPermissions ?
            `Starting ${this.getAlias('claude')} (skipping permissions)...` :
            `Starting ${this.getAlias('claude')}...`;
        document.getElementById('loadingSpinner').querySelector('p').textContent = loadingText;
    }

    startCodexSession(options = {}) {
        // If no session, create one first
        if (!this.currentClaudeSessionId) {
            const sessionName = `Session ${new Date().toLocaleString()}`;
            this.send({
                type: 'create_session',
                name: sessionName,
                workingDir: this.selectedWorkingDir
            });
            // Wait for session creation, then start Codex
            setTimeout(() => {
                this.send({ type: 'start_codex', options });
            }, 500);
        } else {
            this.send({ type: 'start_codex', options });
        }

        this.hideStartPrompt();
        this.showOverlay('loadingSpinner');
        const loadingText = options.dangerouslySkipPermissions ?
            `Starting ${this.getAlias('codex')} (bypassing approvals and sandbox)...` :
            `Starting ${this.getAlias('codex')}...`;
        document.getElementById('loadingSpinner').querySelector('p').textContent = loadingText;
    }

    startAgentSession(options = {}) {
        // If no session, create one first
        if (!this.currentClaudeSessionId) {
            const sessionName = `Session ${new Date().toLocaleString()}`;
            this.send({
                type: 'create_session',
                name: sessionName,
                workingDir: this.selectedWorkingDir
            });
            // Wait for session creation, then start Agent
            setTimeout(() => {
                this.send({ type: 'start_agent', options });
            }, 500);
        } else {
            this.send({ type: 'start_agent', options });
        }

        this.hideStartPrompt();
        this.showOverlay('loadingSpinner');
        const loadingText = `Starting ${this.getAlias('agent')}...`;
        document.getElementById('loadingSpinner').querySelector('p').textContent = loadingText;
    }

    clearTerminal() {
        this.terminal.clear();
    }

    toggleMobileMenu() {
        const mobileMenu = document.getElementById('mobileMenu');
        const hamburgerBtn = document.getElementById('hamburgerBtn');
        mobileMenu.classList.toggle('active');
        if (hamburgerBtn) hamburgerBtn.classList.toggle('active');
    }

    closeMobileMenu() {
        const mobileMenu = document.getElementById('mobileMenu');
        const hamburgerBtn = document.getElementById('hamburgerBtn');
        mobileMenu.classList.remove('active');
        if (hamburgerBtn) hamburgerBtn.classList.remove('active');
    }

    fitTerminal() {
        if (this.fitAddon) {
            try {
                this.fitAddon.fit();

                // On mobile, ensure terminal doesn't exceed viewport width
                if (this.isMobile) {
                    const terminalElement = document.querySelector('.xterm');
                    if (terminalElement) {
                        const viewportWidth = window.innerWidth;
                        const currentWidth = terminalElement.offsetWidth;

                        if (currentWidth > viewportWidth) {
                            // Reduce columns to fit viewport
                            const charWidth = currentWidth / this.terminal.cols;
                            const maxCols = Math.floor((viewportWidth - 20) / charWidth);
                            this.terminal.resize(maxCols, this.terminal.rows);
                        }
                    }
                }
            } catch (error) {
                console.error('Error fitting terminal:', error);
            }
        }
    }

    updateStatus(status) {
        // Status display removed with header - status now shown in tabs
        console.log('Status:', status);
    }

    updateWorkingDir(dir) {
        // Working dir display removed with header - shown in tab titles
        console.log('Working directory:', dir);
    }

    showOverlay(contentId) {
        const overlay = document.getElementById('overlay');
        // Only loadingSpinner exists now
        const loadingSpinner = document.getElementById('loadingSpinner');
        if (loadingSpinner) {
            loadingSpinner.style.display = contentId === 'loadingSpinner' ? 'block' : 'none';
        }

        overlay.style.display = 'flex';
    }

    hideOverlay() {
        const overlay = document.getElementById('overlay');
        if (overlay) {
            console.log('[hideOverlay] Hiding overlay, current display:', overlay.style.display);
            overlay.style.display = 'none';
            console.log('[hideOverlay] Overlay hidden, new display:', overlay.style.display);
        } else {
            console.error('[hideOverlay] Overlay element not found!');
        }
    }

    showError(message) {
        const banner = document.getElementById('errorBanner');
        const bannerText = document.getElementById('errorBannerText');
        if (banner && bannerText) {
            bannerText.textContent = message;
            banner.style.display = 'flex';
            // Auto-hide after 10 seconds
            setTimeout(() => {
                if (banner) banner.style.display = 'none';
            }, 10000);
        }
    }

    loadSettings() {
        const defaults = {
            fontSize: 14,
            showTokenStats: true,
            theme: 'dark',
            defaultAgent: 'claude'
        };

        try {
            const saved = localStorage.getItem('launchpod-terminal-settings');
            return saved ? { ...defaults, ...JSON.parse(saved) } : defaults;
        } catch (error) {
            console.error('Failed to load settings:', error);
            return defaults;
        }
    }

    startHeartbeat() {
        setInterval(() => {
            if (this.socket && this.socket.readyState === WebSocket.OPEN) {
                this.send({ type: 'ping' });
            }
        }, 30000);
    }

    async closeSession() {
        try {
            // Send close session message via WebSocket if connected
            if (this.socket && this.socket.readyState === WebSocket.OPEN) {
                this.send({ type: 'close_session' });
            }

            // Clear the working directory on the server
            const response = await fetch('/terminal/api/close-session', {
                method: 'POST',
                headers: {
                    'Content-Type': 'application/json'
                }
            });

            if (!response.ok) {
                const error = await response.json();
                throw new Error(error.message || 'Failed to close session');
            }

            // Reset the local state
            this.selectedWorkingDir = null;
            this.currentFolderPath = null;

            // Disconnect WebSocket
            this.disconnect();

            // Clear terminal
            this.clearTerminal();

        } catch (error) {
            console.error('Failed to close session:', error);
            this.showError(`Failed to close session: ${error.message}`);
        }
    }

    // Session Management Methods
    toggleSessionDropdown() {
        // Session dropdown removed with header - using tabs instead
    }

    async loadSessions() {
        try {
            const response = await fetch('/terminal/api/sessions/list');
            if (!response.ok) throw new Error('Failed to load sessions');

            const data = await response.json();
            this.claudeSessions = data.sessions;
            this.renderSessionList();
        } catch (error) {
            console.error('Failed to load sessions:', error);
        }
    }

    renderSessionList() {
        // This method is deprecated - sessions are now displayed as tabs
        return;
    }

    handleSessionAction(action, sessionId) {
        switch (action) {
            case 'join':
                this.joinSession(sessionId);
                break;
            case 'leave':
                this.leaveSession();
                break;
            case 'delete':
                this.deleteSession(sessionId);
                break;
        }
    }

    async joinSession(sessionId) {
        // Ensure we're connected first
        if (!this.socket || this.socket.readyState !== WebSocket.OPEN) {
            // Check if we're already connecting (readyState === 0 means CONNECTING)
            if (this.socket && this.socket.readyState === WebSocket.CONNECTING) {
                // Wait for existing connection to complete
                await new Promise((resolve) => {
                    const checkConnection = setInterval(() => {
                        if (this.socket.readyState === WebSocket.OPEN) {
                            clearInterval(checkConnection);
                            resolve();
                        }
                    }, 50);
                    // Timeout after 5 seconds
                    setTimeout(() => {
                        clearInterval(checkConnection);
                        resolve();
                    }, 5000);
                });
            } else {
                // No socket or socket is closed, create new connection
                await this.connect();
                // Wait a bit for connection to establish
                await new Promise(resolve => setTimeout(resolve, 100));
            }
        }

        // Create a promise that resolves when we receive session_joined message
        return new Promise((resolve) => {
            // Store the resolve function to call when we get the response
            this.pendingJoinResolve = resolve;
            this.pendingJoinSessionId = sessionId;

            // Send the join request
            this.send({ type: 'join_session', sessionId });

            // Request usage stats when joining a session
            this.requestUsageStats();

            // Set a timeout in case the response never comes
            setTimeout(() => {
                if (this.pendingJoinResolve) {
                    this.pendingJoinResolve = null;
                    this.pendingJoinSessionId = null;
                    resolve(); // Resolve anyway after timeout
                }
            }, 2000);
        });
    }

    leaveSession() {
        this.send({ type: 'leave_session' });
    }

    async deleteSession(sessionId) {
        if (!confirm('Are you sure you want to delete this session? This will stop any running Claude process.')) {
            return;
        }

        try {
            const response = await fetch(`/terminal/api/sessions/${sessionId}`, {
                method: 'DELETE'
            });

            if (!response.ok) throw new Error('Failed to delete session');

            this.loadSessions();

            if (sessionId === this.currentClaudeSessionId) {
                this.currentClaudeSessionId = null;
                this.currentClaudeSessionName = null;
                this.updateSessionButton('Sessions');
                this.terminal.clear();
                this.hideOverlay();
            }
        } catch (error) {
            console.error('Failed to delete session:', error);
            this.showError('Failed to delete session');
        }
    }

    updateSessionButton(text) {
        // Session button removed with header - using tabs instead
        console.log('Session:', text);
    }

    setupPlanDetector() {
        // Initialize plan detector
        this.planDetector = new PlanDetector();

        // Set up callbacks
        this.planDetector.onPlanDetected = (plan) => {
            this.showPlanPanel(plan);
        };

        this.planDetector.onPlanModeChange = (isActive) => {
            this.updatePlanModeIndicator(isActive);
        };

        // Set up inline panel buttons
        const acceptBtn = document.getElementById('acceptPlanBtn');
        const rejectBtn = document.getElementById('rejectPlanBtn');
        const toggleBtn = document.getElementById('planToggleBtn');

        if (acceptBtn) acceptBtn.addEventListener('click', () => this.acceptPlan());
        if (rejectBtn) rejectBtn.addEventListener('click', () => this.rejectPlan());
        if (toggleBtn) toggleBtn.addEventListener('click', () => this.togglePlanPanel());

        // Start monitoring
        this.planDetector.startMonitoring();
    }

    showPlanPanel(plan) {
        const panel = document.getElementById('planInlinePanel');
        const content = document.getElementById('planContent');

        // Format the plan content
        let formattedContent = plan.content;

        // Convert markdown to basic HTML for better display
        formattedContent = formattedContent
            .replace(/^### (.*?)$/gm, '<h3>$1</h3>')
            .replace(/^## (.*?)$/gm, '<h2>$1</h2>')
            .replace(/^- (.*?)$/gm, '• $1')
            .replace(/\*\*(.*?)\*\*/g, '<strong>$1</strong>')
            .replace(/\*(.*?)\*/g, '<em>$1</em>')
            .replace(/`([^`]+)`/g, '<code>$1</code>');

        content.innerHTML = formattedContent;
        if (panel) {
            panel.style.display = 'block';
            panel.classList.remove('collapsed');
        }

        // Play a subtle notification sound (optional)
        this.playNotificationSound();
    }

    hidePlanPanel() {
        const panel = document.getElementById('planInlinePanel');
        if (panel) panel.style.display = 'none';
    }

    togglePlanPanel() {
        const panel = document.getElementById('planInlinePanel');
        if (panel) {
            panel.classList.toggle('collapsed');
        }
    }

    acceptPlan() {
        // Send acceptance to Claude
        if (this.socket && this.socket.readyState === WebSocket.OPEN) {
            this.socket.send(JSON.stringify({
                type: 'input',
                data: 'y\n' // Send 'y' to accept the plan
            }));
        }

        this.hidePlanPanel();
        this.planDetector.clearBuffer();

        // Show confirmation
        this.showNotification('Plan accepted! Claude will begin implementation.');
    }

    rejectPlan() {
        // Send rejection to Claude
        if (this.socket && this.socket.readyState === WebSocket.OPEN) {
            this.socket.send(JSON.stringify({
                type: 'input',
                data: 'n\n' // Send 'n' to reject the plan
            }));
        }

        this.hidePlanPanel();
        this.planDetector.clearBuffer();

        // Show confirmation
        this.showNotification('Plan rejected. You can provide feedback to Claude.');
    }

    updatePlanModeIndicator(isActive) {
        // No explicit status area in current UI - plan panel handles this
        if (!isActive) {
            this.hidePlanPanel();
        }
    }

    requestUsageStats() {
        if (this.socket && this.socket.readyState === WebSocket.OPEN) {
            this.socket.send(JSON.stringify({ type: 'get_usage' }));
        }

        // Start periodic updates if not already running
        if (!this.usageUpdateTimer) {
            this.usageUpdateTimer = setInterval(() => {
                this.requestUsageStats();
            }, 10000); // Update every 10 seconds
        }
    }

    startSessionTimerUpdate() {
        // Token usage timer removed - no UI elements to update
        return;
    }

    updateUsageDisplay(sessionStats, dailyStats, sessionTimer, analytics, burnRate, plan, limits) {
        // Token usage display removed - no UI elements to update
        return;
    }

    getBurnRateIndicator(rate) {
        // Minimalist indicator using a line chart icon and label
        const icon = window.icons?.chartLine?.(12) || '';
        if (rate > 1000) return `<span class="icon" aria-hidden="true">${icon}</span> Very high`;
        if (rate > 500) return `<span class="icon" aria-hidden="true">${icon}</span> High`;
        if (rate > 100) return `<span class="icon" aria-hidden="true">${icon}</span> Moderate`;
        if (rate > 50) return `<span class="icon" aria-hidden="true">${icon}</span> Low`;
        return `<span class="icon" aria-hidden="true">${icon}</span> Very low`;
    }

    showNotification(message) {
        // Simple notification - you could enhance this with a toast notification
        const notification = document.createElement('div');
        notification.className = 'notification';
        notification.textContent = message;
        notification.style.cssText = `
            position: fixed;
            top: 20px;
            right: 20px;
            background: var(--accent);
            color: white;
            padding: 12px 20px;
            border-radius: 8px;
            z-index: 10002;
            animation: slideIn 0.3s ease;
        `;

        document.body.appendChild(notification);

        setTimeout(() => {
            notification.style.animation = 'slideOut 0.3s ease';
            setTimeout(() => notification.remove(), 300);
        }, 3000);
    }

    playNotificationSound() {
        // Optional: Play a subtle sound when plan is detected
        try {
            const audio = new Audio('data:audio/wav;base64,UklGRnoGAABXQVZFZm10IBAAAAABAAEAQB8AAEAfAAABAAgAZGF0YQoGAACBhYqFbF1fdJivrJBhNjVgodDbq2EcBj+a2/LDciUFLIHO8tiJNwgZaLvt559NEAxQp+PwtmMcBjiR1/LMeSwFJHfH8N2QQAoUXrTp66hVFApGn+DyvmwhBRld0Oy9diMFl2+z2e7NeSgFxYvg+8SEIwW3we6eVg0FqOTupjMBSanLvV0OBba37J5QCgU4cLvfvn0cBUCd1Oq2yFSvvayILgm359+2pw8HVqfu3LNDCEij59+NLwBarvfZN20aBVGU4OyrdR0Ff5/i5paFFDGD0+ylVBYF3NTaz38nBThl4fDbmU0NF1PD5uyqUBcIJJDO5buGNggMoNvyx08FB1er/OykQRIKrau3mHs0BQ5azvfZx30VBbDe3LVmFAVK0PC1vnoPC42S4ObNozsJB1Ox58+TYyAKL5zN9r19JAWFz9P6s4s6C2uz+L2VJwUUncflwpdMC0HD5d5sFAVWv+PYiEQIDXq16eyxlSAK57vi75NkBqOZ88WzlnAHl9TmsS8JBaLj4rQ8BigO1/rPuIMtBjGI1PG+kCcFxoTg+bxnMwfSfOL55LVeCn/R+Mltbw8FBpP48KBwKgtDqPDfnzsLCJDZ/dpTWRUHo+S6+M9+lQdRp/DdnysJFXG559GdWwgTgN7z04k2Be/B8d2AUAILJLTy2Y8xBZmduvneOxYFy6H24LhpGgWunuznm0sTDbXm9bldBQuK6u7LfxUIPLH74Z5CBRt37uWmTRgB7ez+0ogeCi+J0Oe4X');
            audio.volume = 0.3;
            audio.play();
        } catch (e) {
            // Ignore sound errors
        }
    }

}

// Add animation keyframes
const style = document.createElement('style');
style.textContent = `
    @keyframes slideIn {
        from { transform: translateX(100%); opacity: 0; }
        to { transform: translateX(0); opacity: 1; }
    }
    @keyframes slideOut {
        from { transform: translateX(0); opacity: 1; }
        to { transform: translateX(100%); opacity: 0; }
    }
`;
document.head.appendChild(style);

document.addEventListener('DOMContentLoaded', () => {
    const app = new ClaudeCodeWebInterface();
    window.app = app;
    app.startHeartbeat();
});
