/**
 * MK1 App - 4Runr Layout + Identity (Phase 2)
 *
 * Render:
 * - Layout engine computes panel positions
 * - Widget manager creates/manages all panels
 * - Operations feed (Truth Bus) displays events
 * - Command input at bottom
 *
 * Commands:
 * - dbg:size → prints to stderr log file, not UI
 * - dbg:panic → forces rebuild (destroy + recreate screen)
 * - dbg:layout → shows layout debug info in operations feed
 * - help → prints in operations feed
 */
import { MK1Kernel } from './kernel.js';
import { logger } from './logger.js';
import { fileLogger } from './log.js';
import { eventBus } from './core/eventBus.js';
import { feedStore } from './core/feedStore.js';
import { getSafeViewport } from './viewport/safeViewport.js';
import { computeLayout } from './layout/layoutEngine.js';
import { buildUI, destroyUI, updateOperationsFeed, updateCommandValue } from './ui/uiBuilder.js';
export class MK1App {
    kernel;
    uiRefs = null;
    layoutSpec = null;
    lastRebuildReason = 'initial';
    inputHandlersAttached = false;
    safeViewport = null;
    commandValue = '';
    resizeDebounceTimer = null;
    constructor() {
        this.kernel = new MK1Kernel();
    }
    /**
     * Start MK1 App
     */
    async start() {
        // Initialize feed store (subscribes to eventBus)
        feedStore;
        // Emit boot event
        eventBus.emit('SYS', 'MK1 boot');
        await this.kernel.start(() => {
            fileLogger.force('[MK1] kernel.start() callback called - about to render');
            this.render();
            fileLogger.force('[MK1] render() returned - setting up input handling');
            // Setup input handling after first render
            this.setupInputHandling();
            // Log that render completed
            fileLogger.force('[MK1] Initial render completed, input handlers attached');
        }, {
            onRebuild: async (cols, rows) => {
                // Reset input handlers flag (will re-attach after rebuild)
                this.inputHandlersAttached = false;
                await this.handleResize(cols, rows);
                // Re-setup input handling after rebuild
                this.setupInputHandling();
            },
        });
        logger.info('MK1 App started');
    }
    /**
     * Rebuild UI - destroy all widgets and recreate from layout spec
     */
    async rebuildUI(reason) {
        fileLogger.force(`[MK1] rebuildUI called: reason=${reason}`);
        const screen = this.kernel.getScreen();
        if (!screen) {
            fileLogger.force(`[MK1] rebuildUI: screen is null!`);
            return;
        }
        fileLogger.force(`[MK1] rebuildUI: screen=${screen.width}x${screen.height}`);
        // PHASE B: Log children before build
        const childrenBefore = screen.children?.length || 0;
        fileLogger.force(`[MK1] before buildUI: screen.children=${childrenBefore}`);
        // Get safe viewport (canvas)
        this.safeViewport = getSafeViewport(screen.width, screen.height);
        const canvas = {
            cols: this.safeViewport.safeCols,
            rows: this.safeViewport.safeRows,
        };
        fileLogger.force(`[MK1] rebuildUI: canvas=${canvas.cols}x${canvas.rows}`);
        // Destroy old UI
        destroyUI(this.uiRefs, screen);
        this.uiRefs = null;
        fileLogger.force(`[MK1] after destroyUI: screen.children=${screen.children?.length || 0}`);
        // Compute layout
        try {
            fileLogger.force(`[MK1] rebuildUI: computing layout...`);
            this.layoutSpec = computeLayout(canvas);
            // Phase A: Debug print layout info (from getSafeViewport and layout engine)
            // Step 5: Acceptance test logging - verify all values
            if (this.layoutSpec._debug) {
                const debug = this.layoutSpec._debug;
                fileLogger.force(`[LAYOUT ACCEPTANCE TEST] cols=${this.safeViewport.screenCols} rows=${this.safeViewport.screenRows} ` +
                    `SAFE_COLS=${debug.safeCols} safeRows=${debug.safeRows} ` +
                    `RIGHT_GAP=${this.safeViewport.rightMargin} BOTTOM_GAP=${this.safeViewport.bottomMargin} ` +
                    `LEFT_W=${debug.LEFT_W} OPS_W=${debug.centerW} RIGHT_W=${debug.RIGHT_W} ` +
                    `GUTTER=${debug.GUTTER} OPS_MAX_W=${debug.OPS_MAX_W} OPS_MIN_W=${debug.OPS_MIN_W} ` +
                    `availableForOps=${debug.availableForOps} ` +
                    `LEFT_X=${debug.leftX} OPS_X=${debug.centerX} RIGHT_X=${debug.rightX} ` +
                    `RIGHT_EDGE=${debug.rightEdge} gap=${debug.gap} ${debug.status}`);
                if (debug.status === 'ERROR') {
                    fileLogger.force(`[LAYOUT ERROR] rightEdge=${debug.rightEdge} > SAFE_COLS=${debug.safeCols} - SCROLLBAR WILL APPEAR!`);
                }
            }
            this.lastRebuildReason = reason;
            // Get git SHA (if available)
            const gitSha = process.env.GIT_SHA || undefined;
            // Build new UI
            fileLogger.force(`[MK1] about to call buildUI...`);
            this.uiRefs = buildUI(screen, this.layoutSpec, canvas, this.commandValue, gitSha);
            fileLogger.force(`[MK1] buildUI returned`);
            // PHASE B: Verify children are attached before rendering
            const childCount = screen.children?.length || 0;
            fileLogger.force(`[MK1] before render: screen.children=${childCount}`);
            if (childCount === 0) {
                fileLogger.force(`[MK1] CRITICAL: Screen has 0 children after buildUI! This means widgets weren't appended.`);
                eventBus.emit('ERROR', 'UI build failed: no widgets attached to screen');
            }
            else {
                fileLogger.force(`[MK1] Screen has ${childCount} children after buildUI - proceeding to render`);
            }
            // Phase D: Render screen (full rebuild cycle)
            screen.render();
            // PHASE B: Verify children still exist after render
            const childCountAfter = screen.children?.length || 0;
            fileLogger.force(`[MK1] after render: screen.children=${childCountAfter}`);
            if (childCountAfter === 0) {
                fileLogger.force(`[MK1] CRITICAL: Screen has 0 children after render! Widgets were destroyed.`);
            }
            else if (childCountAfter !== childCount) {
                fileLogger.force(`[MK1] WARNING: Children count changed from ${childCount} to ${childCountAfter} after render!`);
            }
            // Phase B: Run offender scanner AFTER render to detect any elements exceeding bounds
            // This is the "stop guessing" tool - it will tell us exactly what's wrong
            const { scanAndLogOffenders } = await import('./ui/offenderScanner.js');
            const offenders = scanAndLogOffenders(screen, this.safeViewport.safeCols, this.safeViewport.safeRows);
            if (offenders.length > 0) {
                eventBus.emit('ERROR', `Found ${offenders.length} elements exceeding safe bounds - check mk1.log`);
            }
            // Emit layout info
            eventBus.emit('SYS', `Layout: ${this.layoutSpec.mode} reason=${this.layoutSpec.reason}`);
            eventBus.emit('SYS', `canvas=${canvas.cols}x${canvas.rows} safeCols=${this.safeViewport.safeCols} safeRows=${this.safeViewport.safeRows} RIGHT_GAP=${this.safeViewport.rightMargin} BOTTOM_GAP=${this.safeViewport.bottomMargin}`);
            fileLogger.force(`[MK1] Rebuilt UI: ${reason} mode=${this.layoutSpec.mode} children=${childCount}`);
        }
        catch (error) {
            const errorMsg = error instanceof Error ? error.message : String(error);
            fileLogger.force(`[MK1] Layout computation failed: ${errorMsg}`);
            if (error instanceof Error && error.stack) {
                fileLogger.force(`[MK1] Stack: ${error.stack}`);
            }
            eventBus.emit('ERROR', `Layout error: ${errorMsg}`);
        }
    }
    /**
     * Render full layout (calls rebuildUI)
     */
    async render() {
        fileLogger.force('[MK1] render() called - about to call rebuildUI');
        await this.rebuildUI('initial');
        fileLogger.force('[MK1] render() completed - rebuildUI finished');
    }
    /**
     * Handle resize (debounced)
     */
    async handleResize(cols, rows) {
        // Debounce resize events (50ms)
        if (this.resizeDebounceTimer) {
            clearTimeout(this.resizeDebounceTimer);
        }
        this.resizeDebounceTimer = setTimeout(() => {
            // Preserve command input value
            const commandValue = this.commandValue;
            // Rebuild (destroy + recreate)
            this.rebuildUI(`resize:${cols}x${rows}`);
            // Restore command value
            this.commandValue = commandValue;
            if (this.uiRefs) {
                updateCommandValue(this.uiRefs, commandValue);
            }
            // Re-render
            const screen = this.kernel.getScreen();
            if (screen) {
                screen.render();
            }
        }, 50);
    }
    /**
     * Setup input handling (manual - works on all terminals)
     */
    setupInputHandling() {
        const screen = this.kernel.getScreen();
        if (!screen) {
            return;
        }
        // Prevent duplicate handlers
        if (this.inputHandlersAttached) {
            return;
        }
        this.inputHandlersAttached = true;
        // Ensure screen is focused to capture input
        if (screen.focus) {
            screen.focus();
        }
        // CRITICAL: Set up blessed's input handling properly
        // Blessed should handle all input - we just need to ensure it's set up correctly
        screen.key(['escape', 'q', 'Q'], () => {
            // Allow escape/q to exit
            this.stop();
            process.exit(0);
        });
        // Handle Ctrl+C
        screen.key(['C-c'], () => {
            this.stop();
            process.exit(0);
        });
        // CRITICAL: Set up keypress handler - this is how blessed processes input
        // This handler being active also helps keep the process alive
        screen.on('keypress', async (ch, key) => {
            // Log for debugging (only in debug mode) - use file logger
            if (process.env.TUI_DEBUG === '1') {
                fileLogger.debug(`Keypress: ch=${ch} key=${JSON.stringify(key)}`);
            }
            // Ensure screen stays focused (blessed sometimes loses focus)
            if (screen.focus) {
                screen.focus();
            }
            // Handle Enter key (submit)
            if (key && (key.name === 'enter' || key.name === 'return' || key.full === 'enter')) {
                if (this.commandValue.trim()) {
                    const command = this.commandValue.trim();
                    this.commandValue = '';
                    if (this.uiRefs) {
                        updateCommandValue(this.uiRefs, this.commandValue);
                    }
                    // Handle command
                    await this.handleCommand(command);
                    // Update operations feed and re-render
                    if (this.uiRefs) {
                        updateOperationsFeed(this.uiRefs);
                        const screen = this.kernel.getScreen();
                        if (screen) {
                            screen.render();
                        }
                    }
                }
                return;
            }
            // Handle Backspace
            if (key && (key.name === 'backspace' || key.name === 'delete' || key.full === 'backspace')) {
                if (this.commandValue.length > 0) {
                    this.commandValue = this.commandValue.slice(0, -1);
                    if (this.uiRefs) {
                        updateCommandValue(this.uiRefs, this.commandValue);
                        const screen = this.kernel.getScreen();
                        if (screen) {
                            screen.render();
                        }
                    }
                }
                return;
            }
            // Handle arrow keys and other special keys (ignore them for now)
            if (key && (key.name === 'up' || key.name === 'down' || key.name === 'left' || key.name === 'right')) {
                return;
            }
            // Handle printable characters (ASCII only)
            if (ch && typeof ch === 'string' && /[\x20-\x7E]/.test(ch)) {
                this.commandValue += ch;
                if (this.uiRefs) {
                    updateCommandValue(this.uiRefs, this.commandValue);
                    const screen = this.kernel.getScreen();
                    if (screen) {
                        screen.render();
                    }
                }
            }
        });
        // CRITICAL: Ensure blessed is handling all input
        // The issue is that escape sequences are being displayed as text
        // This happens when stdin is read directly instead of through blessed
        // Blessed should be the only thing reading from stdin
        fileLogger.debug('Input handling setup complete - blessed should capture all input');
    }
    /**
     * Handle command
     */
    async handleCommand(command) {
        if (command === 'dbg:size') {
            // Print to stderr log file, not UI
            const screen = this.kernel.getScreen();
            if (screen) {
                const info = {
                    'screen.cols': screen.width,
                    'screen.rows': screen.height,
                    'stdout.columns': process.stdout.columns,
                    'stdout.rows': process.stdout.rows,
                    'TERM': process.env.TERM,
                    'platform': process.platform,
                    'isTTY': process.stdout.isTTY,
                };
                logger.debug(`dbg:size output:\n${JSON.stringify(info, null, 2)}`);
            }
        }
        else if (command === 'dbg:panic') {
            // Force rebuild
            this.lastRebuildReason = 'panic';
            await this.kernel.panic();
        }
        else if (command === 'dbg:layout') {
            // Show layout debug info
            if (this.layoutSpec) {
                eventBus.emit('DEBUG', `Layout: ${this.layoutSpec.mode} reason=${this.layoutSpec.reason}`);
                if (this.safeViewport) {
                    eventBus.emit('DEBUG', `canvas: ${this.safeViewport.safeCols}x${this.safeViewport.safeRows}`);
                }
                for (const [name, rect] of Object.entries(this.layoutSpec.rects)) {
                    eventBus.emit('DEBUG', `${name}: (${rect.x},${rect.y}) ${rect.w}x${rect.h}`);
                }
            }
        }
        else if (command === 'dbg:widgets') {
            // Show widget info
            if (this.uiRefs) {
                const screen = this.kernel.getScreen();
                if (screen) {
                    const children = screen.children || [];
                    eventBus.emit('DEBUG', `Widgets: ${children.length} children`);
                    children.forEach((child, idx) => {
                        const type = child.type || 'unknown';
                        const left = child.left || 0;
                        const top = child.top || 0;
                        const width = child.width || 0;
                        const height = child.height || 0;
                        eventBus.emit('DEBUG', `  [${idx}] ${type} @ (${left},${top}) ${width}x${height}`);
                    });
                }
            }
        }
        else if (command === 'help') {
            // Show help in operations feed
            eventBus.emit('HELP', 'Commands: dbg:mode, dbg:grid, dbg:stack, dbg:size, dbg:panic, dbg:layout, dbg:widgets, help, exit');
        }
        else if (command === 'exit') {
            this.stop();
            process.exit(0);
        }
        else {
            // Unknown command - show in operations feed
            eventBus.emit('ERROR', `Unknown command: ${command} (type 'help')`);
        }
    }
    /**
     * Emit layout debug info
     */
    /**
     * Stop app
     */
    stop() {
        // Destroy UI
        const screen = this.kernel.getScreen();
        if (screen) {
            destroyUI(this.uiRefs, screen);
        }
        this.uiRefs = null;
        this.kernel.stop();
        logger.info('MK1 App stopped');
    }
}
/**
 * Start MK1 App (entry point)
 */
// Global keep-alive interval (set up before any potential errors)
let globalKeepAliveInterval = null;
export async function startMK1() {
    // CRITICAL: Set up keep-alive IMMEDIATELY to prevent process exit
    // This must be done before any async operations that might fail
    globalKeepAliveInterval = setInterval(() => {
        // This interval keeps the event loop alive
        // We don't need to do anything - just having the interval is enough
    }, 1000); // Check every second to keep event loop active
    // Store interval reference globally to prevent garbage collection
    globalThis.__MK1_KEEP_ALIVE__ = globalKeepAliveInterval;
    try {
        const app = new MK1App();
        await app.start();
        // CRITICAL: Store global reference to prevent garbage collection
        // This ensures the app, screen, and event handlers stay alive
        globalThis.__MK1_APP__ = app;
        // The blessed screen should keep the process alive by keeping stdin open
        // But we log here to confirm startup completed
        logger.debug('MK1 App startup completed - screen should keep process alive');
        // Explicitly ensure stdin stays open to keep process alive
        // This is a safety measure - blessed should handle this, but we ensure it
        if (process.stdin.isPaused()) {
            logger.warn('stdin was paused - resuming to keep process alive');
            process.stdin.resume();
        }
        // Verify stdin is readable
        if (!process.stdin.readable) {
            logger.error('stdin is not readable - this will cause the process to exit!');
            // Try to resume anyway
            process.stdin.resume();
        }
        else {
            logger.debug('stdin is readable - process should stay alive');
        }
        // Monitor stdin and screen in the keep-alive interval
        if (globalKeepAliveInterval) {
            // Update the interval to also monitor stdin and screen
            clearInterval(globalKeepAliveInterval);
            globalKeepAliveInterval = setInterval(() => {
                // Check if stdin is still readable
                if (!process.stdin.readable) {
                    logger.error('stdin became unreadable - process will exit!');
                    return;
                }
                // Check if stdin is paused and resume if needed
                if (process.stdin.isPaused()) {
                    logger.warn('stdin was paused - resuming to keep process alive');
                    process.stdin.resume();
                }
                // Verify screen is still alive (access via global singleton)
                const screen = globalThis.__MK1_SCREEN__;
                if (!screen) {
                    logger.error('Screen was destroyed - this should not happen!');
                }
            }, 5000); // Check every 5 seconds
            globalThis.__MK1_KEEP_ALIVE__ = globalKeepAliveInterval;
        }
        // Monitor process events to debug why it might exit
        process.on('beforeExit', (code) => {
            logger.warn(`[BEFORE_EXIT] Code: ${code} - Process is about to exit`);
            logger.warn('[BEFORE_EXIT] This should not happen if stdin is open or keep-alive is active');
        });
        // Clear interval on process exit (cleanup)
        process.once('exit', (code) => {
            logger.warn(`[EXIT] Code: ${code} - Process is exiting`);
            if (globalKeepAliveInterval) {
                clearInterval(globalKeepAliveInterval);
                globalKeepAliveInterval = null;
            }
            globalThis.__MK1_APP__ = undefined;
            globalThis.__MK1_KEEP_ALIVE__ = undefined;
        });
        // CRITICAL: The blessed screen MUST keep stdin open to keep the process alive
        // We've set up multiple safety mechanisms:
        // 1. Keep-alive interval (keeps event loop active)
        // 2. Blessed screen (should keep stdin open)
        // 3. Explicit stdin resume (ensures stdin is active)
        // 
        // The process will stay alive as long as:
        // - The keep-alive interval is running (event loop has timers)
        // - Blessed screen is listening to stdin (keeps stdin open)
        // - stdin is in flowing mode (not paused)
        // Final verification that everything is set up correctly
        const screen = globalThis.__MK1_SCREEN__;
        if (screen) {
            logger.debug('Screen is registered and should be keeping stdin open');
        }
        else {
            logger.error('Screen is not registered - this is a problem!');
        }
        if (process.stdin.readable && !process.stdin.isPaused()) {
            logger.debug('stdin is readable and active - process should stay alive');
        }
        else {
            logger.warn('stdin may not keep process alive - keep-alive interval will help');
        }
        logger.debug('Process keep-alive mechanisms active - screen should keep stdin open, interval keeps event loop alive');
        // CRITICAL: Do NOT return here - the process must stay alive
        // The blessed screen and keep-alive interval will keep it running
        // If we return, the async IIFE completes and process might exit
        // So we just log and let the event loop continue
    }
    catch (error) {
        const errorMsg = error instanceof Error ? error.message : String(error);
        logger.error(`Failed to start MK1 App: ${errorMsg}`);
        if (error instanceof Error && error.stack) {
            logger.error(`Stack trace: ${error.stack}`);
        }
        // Clean up keep-alive on error
        if (globalKeepAliveInterval) {
            clearInterval(globalKeepAliveInterval);
            globalKeepAliveInterval = null;
        }
        throw error;
    }
}
//# sourceMappingURL=mk1App.js.map