/**
 * MK2 App - Main application entry point
 *
 * MK2 Hotfix: Kill Scrollbars + Fix Command Input
 * - Increased guards (RIGHT_GUARD=4, BOTTOM_GUARD=3, TOP_GUARD=1)
 * - Stricter clamping (>= not >)
 * - Real command textbox with input handling
 * - Focus and readInput() for server terminals
 */
import { MK2Kernel } from './kernel.js';
import blessed from 'neo-blessed';
import { computeLayout } from './layout/computeLayout.js';
import { applyLayout } from './layout/applyLayout.js';
import { createDebugOverlay } from './debug/overlay.js';
import { getDims } from './dimensions.js';
export class MK2App {
    kernel;
    boxes = [];
    debugOverlay = null;
    commandBox = null; // MK2 Hotfix: textbox type may not be in Widgets namespace
    operationsBox = null;
    errorOverlay = null; // Track error overlay separately
    constructor() {
        this.kernel = new MK2Kernel();
        // CRITICAL FIX: Process-level signal handlers
        this.setupSignalHandlers();
    }
    /**
     * Setup signal handlers for Ctrl+C (CRITICAL FIX)
     */
    setupSignalHandlers() {
        process.on('SIGINT', () => {
            this.exitClean('SIGINT');
        });
        process.on('SIGTERM', () => {
            this.exitClean('SIGTERM');
        });
    }
    /**
     * Clean exit (CRITICAL FIX)
     */
    exitClean(reason) {
        try {
            process.stderr.write(`\n[MK2] Exiting (${reason})...\n`);
        }
        catch { }
        this.stop();
        process.exit(0);
    }
    /**
     * Start MK2 App
     */
    async start() {
        process.stderr.write('[DEBUG] MK2App.start: entry\n');
        try {
            process.stderr.write('[DEBUG] MK2App.start: calling kernel.start\n');
            await this.kernel.start(async () => {
                process.stderr.write('[DEBUG] MK2App.start: render callback\n');
                await this.render();
            }, {
                onRebuild: async (cols, rows) => {
                    process.stderr.write(`[DEBUG] MK2App.start: onRebuild ${cols}x${rows}\n`);
                    await this.handleResize(cols, rows);
                },
            });
            process.stderr.write('[DEBUG] MK2App.start: kernel.start completed\n');
        }
        catch (error) {
            const errorMsg = error.message || String(error);
            const errorStack = error.stack || '';
            process.stderr.write(`[ERROR] MK2App.start failed: ${errorMsg}\n${errorStack}\n`);
            throw error;
        }
    }
    /**
     * Render layout - MK2 Hotfix: Single source of truth for dimensions
     * FIXED: Check dimensions first, then decide to show overlay or dashboard
     */
    async render() {
        const screen = this.kernel.getScreen();
        if (!screen) {
            return;
        }
        // FIXED: Get dimensions from single source of truth
        const dims = getDims(screen);
        // FIXED: Check dimensions FIRST - decide which view to show
        if (dims.tooSmall) {
            // Show error overlay, hide dashboard
            this.showTooSmallOverlay(screen, dims);
            return;
        }
        // Dimensions are OK - show dashboard, hide overlay
        // CRITICAL: Destroy any existing overlay first
        if (this.errorOverlay) {
            try {
                if (typeof this.errorOverlay.destroy === 'function') {
                    this.errorOverlay.destroy();
                }
            }
            catch { }
            this.errorOverlay = null;
        }
        await this.showDashboard(screen, dims);
    }
    /**
     * Show dashboard (normal UI)
     * FIXED: Atomic rendering - validate all rects first, then create all boxes
     */
    async showDashboard(screen, dims) {
        // Destroy error overlay if it exists
        if (this.errorOverlay) {
            try {
                if (typeof this.errorOverlay.destroy === 'function') {
                    this.errorOverlay.destroy();
                }
            }
            catch { }
            this.errorOverlay = null;
        }
        // Kill old children - hard rebuild (atomic clear)
        this.destroyAll();
        // CRITICAL: Clear all blessed children to prevent stacking
        // This ensures we don't have leftover nodes from previous renders
        try {
            const blessedChildren = [...(screen.children || [])];
            for (const child of blessedChildren) {
                try {
                    if (typeof child.destroy === 'function') {
                        child.destroy();
                    }
                    else if (typeof child.detach === 'function') {
                        child.detach();
                    }
                    else if (child.parent) {
                        // Force remove from parent
                        try {
                            child.parent.remove(child);
                        }
                        catch { }
                    }
                }
                catch { }
            }
            // Clear the children array
            screen.children = [];
        }
        catch { }
        const cols = dims.detectedW;
        const rows = dims.detectedH;
        try {
            // Compute layout
            const spec = await computeLayout(cols, rows);
            // FIXED: Validate all rects BEFORE creating any boxes (atomic check)
            // Use safe viewport for bounds checking
            const invalidRects = [];
            const rectNames = ['posture', 'resources', 'assets', 'operations', 'network', 'capabilities', 'command'];
            // Get safe viewport for bounds checking
            const { getSafeViewport, validateRect, CMD_MIN_W, CMD_MIN_H, MIN_PANEL_W, MIN_PANEL_H } = await import('./safeViewport.js');
            const viewport = getSafeViewport(screen);
            for (const name of rectNames) {
                const rect = spec.rects[name];
                // CRITICAL: Use constants from safeViewport.ts
                const minW = name === 'command' ? CMD_MIN_W : MIN_PANEL_W;
                const minH = name === 'command' ? CMD_MIN_H : MIN_PANEL_H;
                if (rect.w < minW || rect.h < minH) {
                    // CRITICAL: Auto-switch to fallback layout if any rect violates minimums
                    invalidRects.push(`${name}: w=${rect.w} h=${rect.h} (min: ${minW}x${minH}) - LAYOUT ERROR`);
                    continue; // Skip further validation for this rect
                }
                // Validate rect using safe viewport
                const validation = validateRect(rect, viewport);
                if (!validation.valid) {
                    // CRITICAL: Try to clamp the rect to fit within bounds
                    const clampedW = Math.max(minW, viewport.safe.w - rect.x - 2); // -2 for borders
                    const clampedH = Math.max(minH, viewport.safe.h - rect.y - 2);
                    if (clampedW >= minW && clampedH >= minH) {
                        // Clamp succeeded - log and update rect
                        try {
                            const fs = require('fs');
                            fs.appendFileSync('/tmp/mk2_layout.log', `[VALIDATE] ${name}: clamped from ${rect.w}x${rect.h} to ${clampedW}x${clampedH}\n`);
                            fs.appendFileSync('/tmp/mk2_layout.log', `[VALIDATE] ${name}: errors: ${validation.errors.join(', ')}\n`);
                        }
                        catch { }
                        rect.w = clampedW;
                        rect.h = clampedH;
                    }
                    else {
                        // Cannot clamp - report error
                        invalidRects.push(`${name}: ${validation.errors.join(', ')} - cannot clamp to minimum`);
                    }
                }
            }
            // If any rect is invalid, show error overlay instead of partial render
            if (invalidRects.length > 0) {
                const errorMsg = `Invalid layout rects:\n${invalidRects.join('\n')}`;
                try {
                    const fs = require('fs');
                    fs.appendFileSync('/tmp/mk2_layout.log', `[DASHBOARD] Invalid rects detected:\n${invalidRects.join('\n')}\n`);
                }
                catch { }
                this.showErrorOverlay(screen, dims, errorMsg);
                return;
            }
            // Create debug overlay (top line) - only if DEBUG_LAYOUT=1
            const debugOverlay = createDebugOverlay(screen, spec);
            if (debugOverlay) {
                this.debugOverlay = debugOverlay;
            }
            // Apply layout - create all boxes (except command) - ATOMIC: all or nothing
            this.boxes = applyLayout(screen, spec);
            // Verify all boxes were created
            if (this.boxes.length === 0) {
                const errorMsg = 'No boxes created from layout';
                try {
                    const fs = require('fs');
                    fs.appendFileSync('/tmp/mk2_layout.log', `[DASHBOARD] ${errorMsg}\n`);
                }
                catch { }
                this.showErrorOverlay(screen, dims, errorMsg);
                return;
            }
            // Find operations box for logging commands
            this.operationsBox = this.boxes.find(b => {
                const label = b.content || b.label || '';
                return typeof label === 'string' && label.includes('OPERATIONS');
            }) || this.boxes[3] || null;
            // Create command textbox (MK2 Hotfix: Real textbox with input)
            // Note: textbox may not be in blessed namespace directly, try (blessed as any).textbox
            const Textbox = blessed.textbox || blessed.Textbox;
            if (!Textbox) {
                throw new Error('blessed.textbox not available - using fallback box');
            }
            this.commandBox = Textbox({
                parent: screen,
                top: spec.rects.command.y,
                left: spec.rects.command.x,
                width: spec.rects.command.w,
                height: spec.rects.command.h,
                border: { type: 'line' }, // Always use borders - layout accounts for them
                label: ' 4r ',
                inputOnFocus: true,
                keys: true,
                mouse: false, // MK2 Hotfix: Don't rely on mouse
                vi: false,
                style: {
                    fg: 'cyan',
                    border: { fg: 'cyan' },
                },
            });
            // MK2 Hotfix: Command submission handler
            this.commandBox.on('submit', (value) => {
                const text = (value || '').trim();
                if (text) {
                    this.appendOps(`[CMD] ${text}`);
                    if (this.commandBox && typeof this.commandBox.clearValue === 'function') {
                        this.commandBox.clearValue();
                    }
                    screen.render();
                }
            });
            // MK2 Hotfix: Keyboard handlers
            screen.key([':', '/'], () => {
                if (this.commandBox) {
                    this.commandBox.focus();
                    this.commandBox.readInput();
                }
            });
            screen.key(['escape'], () => {
                // Exit command mode - focus pop or next element
                if (typeof screen.focusPop === 'function') {
                    screen.focusPop();
                }
                else if (typeof screen.focusNext === 'function') {
                    screen.focusNext();
                }
                screen.render();
            });
            // CRITICAL FIX: Blessed-level Ctrl+C handlers
            screen.key(['C-c'], () => {
                this.exitClean('screen C-c');
            });
            // Also bind to program if available
            if (screen.program) {
                screen.program.key(['C-c'], () => {
                    this.exitClean('program C-c');
                });
            }
            // Render - CRITICAL: Must render after creating all boxes
            screen.render();
            // Force a second render after a short delay to ensure everything is visible
            setTimeout(() => {
                if (this.kernel.getScreen()) {
                    this.kernel.getScreen()?.render();
                }
            }, 50);
            // DIAGNOSTIC: Validate geometry after render (non-blocking)
            // Only log violations, don't block rendering
            setTimeout(async () => {
                try {
                    const { getSafeViewport } = await import('./safeViewport.js');
                    const { validateScreenGeometry } = await import('./geometryValidator.js');
                    const viewport = getSafeViewport(screen);
                    const geometryReport = await validateScreenGeometry(screen, viewport.safe.w, viewport.safe.h);
                    // Log to stderr (will be in violation log) if there are issues
                    if (geometryReport.violations.length > 0) {
                        process.stderr.write(`[WARN] ${geometryReport.violations.length} geometry violations detected. See /tmp/mk2_layout.log\n`);
                    }
                }
                catch (error) {
                    // Ignore diagnostic errors
                }
            }, 100);
            // MK2 Hotfix: Force focus and readInput after a short delay (critical for server terminals)
            setTimeout(() => {
                if (this.commandBox) {
                    if (typeof this.commandBox.focus === 'function') {
                        this.commandBox.focus();
                    }
                    if (typeof this.commandBox.readInput === 'function') {
                        this.commandBox.readInput();
                    }
                    screen.render();
                }
            }, 30);
        }
        catch (error) {
            // Layout computation failed - re-check dimensions using single source of truth
            const newDims = getDims(screen);
            const errorMsg = error instanceof Error ? error.message : String(error);
            // Log the error
            try {
                const fs = require('fs');
                fs.appendFileSync('/tmp/mk2_layout.log', `[DASHBOARD] Layout computation failed: ${errorMsg}\n`);
                fs.appendFileSync('/tmp/mk2_layout.log', `[DASHBOARD] Dimensions: detected=${newDims.detectedW}x${newDims.detectedH} safe=${newDims.safeW}x${newDims.safeH} tooSmall=${newDims.tooSmall}\n`);
            }
            catch { }
            // FIXED: Check dimensions using single source of truth
            // Only show "too small" overlay if dimensions are actually too small
            // Otherwise show generic error overlay
            if (newDims.tooSmall) {
                // Actually too small - show "too small" overlay
                this.showTooSmallOverlay(screen, newDims);
            }
            else {
                // Dimensions are OK but layout failed - this is a layout bug
                // Show error overlay with actual error (not "too small")
                try {
                    const fs = require('fs');
                    fs.appendFileSync('/tmp/mk2_layout.log', `[DASHBOARD] Layout bug: dimensions OK but computation failed\n`);
                }
                catch { }
                this.showErrorOverlay(screen, newDims, errorMsg);
            }
        }
    }
    /**
     * Show "too small" error overlay
     * FIXED: Separate method to manage overlay lifecycle
     * CRITICAL: Must destroy dashboard elements first, not just render behind them
     */
    showTooSmallOverlay(screen, dims) {
        // CRITICAL: Destroy all dashboard elements first
        this.destroyAll();
        // Then show overlay
        this.showErrorOverlay(screen, dims, 'Terminal too small');
    }
    /**
     * Show error overlay (generic - can be "too small" or layout error)
     */
    showErrorOverlay(screen, dims, errorMsg) {
        // Destroy dashboard elements
        this.destroyAll();
        // Destroy old error overlay if exists
        if (this.errorOverlay) {
            try {
                if (typeof this.errorOverlay.destroy === 'function') {
                    this.errorOverlay.destroy();
                }
            }
            catch { }
        }
        // Create error overlay with debug info
        this.errorOverlay = this.createErrorBox(screen, dims, errorMsg);
        screen.append(this.errorOverlay);
        screen.render();
    }
    /**
     * Append text to operations box
     */
    appendOps(text) {
        if (!this.operationsBox) {
            return;
        }
        const currentContent = this.operationsBox.content || '';
        const lines = currentContent.split('\n').filter((l) => l.trim());
        lines.push(text);
        // Keep last 20 lines
        const keepLines = lines.slice(-20);
        this.operationsBox.setContent(keepLines.join('\n'));
        this.kernel.getScreen()?.render();
    }
    /**
     * Handle resize - MK2 Hotfix: Preserve input focus
     */
    async handleResize(cols, rows) {
        // Hard rebuild - destroy all, recompute, re-render
        // Command box will be recreated and focused
        this.render();
    }
    /**
     * Destroy all children (kill old boxes on resize)
     * FIXED: Also destroy error overlay and clear blessed screen children
     */
    destroyAll() {
        // Destroy debug overlay
        if (this.debugOverlay) {
            try {
                if (typeof this.debugOverlay.destroy === 'function') {
                    this.debugOverlay.destroy();
                }
            }
            catch {
                // Ignore
            }
            this.debugOverlay = null;
        }
        // Destroy error overlay
        if (this.errorOverlay) {
            try {
                if (typeof this.errorOverlay.destroy === 'function') {
                    this.errorOverlay.destroy();
                }
            }
            catch {
                // Ignore
            }
            this.errorOverlay = null;
        }
        // CRITICAL: Also clear blessed screen children to prevent stacking
        const blessedScreen = this.kernel.getScreen();
        if (blessedScreen) {
            try {
                const blessedChildren = [...(blessedScreen.children || [])];
                for (const child of blessedChildren) {
                    try {
                        if (typeof child.destroy === 'function') {
                            child.destroy();
                        }
                        else if (typeof child.detach === 'function') {
                            child.detach();
                        }
                        else if (child.parent) {
                            try {
                                child.parent.remove(child);
                            }
                            catch { }
                        }
                    }
                    catch { }
                }
                // Clear the children array
                blessedScreen.children = [];
            }
            catch { }
        }
        // Destroy command box
        if (this.commandBox) {
            try {
                if (typeof this.commandBox.destroy === 'function') {
                    this.commandBox.destroy();
                }
            }
            catch {
                // Ignore
            }
            this.commandBox = null;
        }
        // Destroy all boxes
        this.boxes.forEach((box) => {
            try {
                if (typeof box.destroy === 'function') {
                    box.destroy();
                }
            }
            catch {
                // Ignore
            }
        });
        this.boxes = [];
        this.operationsBox = null;
        // Also clear screen children (safety)
        const screen = this.kernel.getScreen();
        if (screen) {
            const children = screen.children || [];
            children.forEach((child) => {
                try {
                    if (typeof child.destroy === 'function') {
                        child.destroy();
                    }
                }
                catch {
                    // Ignore
                }
            });
        }
    }
    /**
     * Create error box for terminal too small or other errors
     * FIXED: Use dimensions from single source of truth, include debug booleans
     */
    createErrorBox(screen, dims, customErrorMsg) {
        const errorW = Math.min(80, Math.max(50, dims.detectedW - 10));
        const errorH = customErrorMsg ? 14 : 12; // More space if custom error
        const errorX = Math.max(0, Math.floor((dims.detectedW - errorW) / 2));
        const errorY = Math.max(0, Math.floor((dims.detectedH - errorH) / 2));
        // FIXED: Include debug booleans in overlay
        // Determine title based on error type
        const isTooSmall = dims.tooSmall || (customErrorMsg && customErrorMsg.includes('Terminal too small'));
        const title = isTooSmall ? ' TERMINAL TOO SMALL ' : ' ERROR ';
        const lines = [
            title,
            '',
            `Detected: ${dims.detectedW} x ${dims.detectedH}`,
            `Required: ${dims.requiredW} x ${dims.requiredH}`,
            `Safe: ${dims.safeW} x ${dims.safeH}`,
            '',
            `tooSmall=${dims.tooSmall}`,
            `wFail=${dims.wFail} (${dims.safeW} < ${dims.requiredW})`,
            `hFail=${dims.hFail} (${dims.safeH} < ${dims.requiredH})`,
        ];
        if (customErrorMsg) {
            lines.push('', `Error: ${customErrorMsg}`);
        }
        if (isTooSmall) {
            lines.push('', 'Resize terminal to continue', 'App will auto-reload on resize');
        }
        else {
            lines.push('', 'Check /tmp/mk2_layout.log for details');
        }
        const content = lines.join('\n');
        return blessed.box({
            parent: screen,
            top: errorY,
            left: errorX,
            width: errorW,
            height: errorH,
            content,
            border: { type: 'line' },
            tags: false,
            style: {
                fg: 'yellow',
                border: { fg: 'yellow' },
            },
        });
    }
    /**
     * Stop app
     */
    stop() {
        this.kernel.stop();
    }
}
/**
 * Start MK2 App (entry point)
 */
export async function startMK2() {
    process.stderr.write('[DEBUG] startMK2: entry\n');
    // Keep-alive to prevent process exit
    const keepAlive = setInterval(() => {
        // Keep event loop alive
    }, 1000);
    globalThis.__MK2_KEEP_ALIVE__ = keepAlive;
    process.stderr.write('[DEBUG] startMK2: keepAlive set\n');
    try {
        process.stderr.write('[DEBUG] startMK2: creating MK2App instance\n');
        const app = new MK2App();
        process.stderr.write('[DEBUG] startMK2: calling app.start()\n');
        await app.start();
        process.stderr.write('[DEBUG] startMK2: app.start() completed\n');
        // Store global reference
        globalThis.__MK2_APP__ = app;
        // Keep stdin open
        if (process.stdin.isPaused()) {
            process.stdin.resume();
        }
        process.stderr.write('[DEBUG] startMK2: fully initialized\n');
    }
    catch (error) {
        const errorMsg = error.message || String(error);
        const errorStack = error.stack || '';
        process.stderr.write(`\n[ERROR] startMK2 failed: ${errorMsg}\n${errorStack}\n`);
        process.exit(1);
    }
}
//# sourceMappingURL=mk2App.js.map