/**
 * MK1 UI Builder - Single function that creates all widgets
 *
 * Rules:
 * - Only place widgets are created
 * - All positions come from layout spec
 * - Never hardcode positions
 */
import blessed from 'neo-blessed';
import { fileLogger } from '../log.js';
import { feedStore } from '../core/feedStore.js';
import { setShown } from './safe.js';
import { safeTextForBox, safeTextLines } from './safeText.js';
/**
 * Destroy all UI widgets
 */
export function destroyUI(uiRefs, screen) {
    if (!uiRefs) {
        return;
    }
    fileLogger.debug(`[UI_BUILDER] Destroying ${uiRefs.rootChildren.length} widgets`);
    // Destroy only the widgets we created (tracked in rootChildren)
    for (const child of uiRefs.rootChildren) {
        try {
            // Check if child is still attached before destroying
            if (child && typeof child.destroy === 'function') {
                child.destroy();
            }
        }
        catch (error) {
            fileLogger.warn(`Error destroying widget: ${error}`);
        }
    }
    // Clear references
    uiRefs.rootChildren = [];
    uiRefs.header = undefined;
    uiRefs.posture = undefined;
    uiRefs.resources = undefined;
    uiRefs.assets = undefined;
    uiRefs.operations = undefined;
    uiRefs.network = undefined;
    uiRefs.capabilities = undefined;
    uiRefs.command = undefined;
    fileLogger.debug(`[UI_BUILDER] Destroy complete, screen has ${screen.children?.length || 0} children remaining`);
}
/**
 * Format operations feed content
 * CRITICAL: Ensure lines don't exceed panel width to prevent overflow
 */
function formatOperationsFeed(maxWidth) {
    const events = feedStore.getRecent(20);
    if (events.length === 0) {
        return 'Waiting for events...';
    }
    // Format events as lines, truncate if needed
    const lines = events.map((event) => {
        const time = new Date(event.timestamp).toLocaleTimeString();
        let line = `[${time}] [${event.tag}] ${event.message}`;
        // Truncate line if it exceeds maxWidth
        if (maxWidth && line.length > maxWidth) {
            line = line.substring(0, maxWidth - 3) + '...';
        }
        return line;
    });
    return lines.join('\n');
}
/**
 * Get placeholder content for a panel
 */
function getPlaceholderContent(name) {
    const placeholders = {
        posture: 'Posture status\n\nHealthy',
        resources: 'System resources\n\nCPU: --\nMemory: --',
        assets: 'Agent assets\n\nCount: 0',
        network: 'Network status\n\nConnected',
        capabilities: 'Capabilities\n\nList: --',
    };
    return placeholders[name] || `${name} content`;
}
/**
 * Build header text with layout info
 */
function buildHeaderText(spec, canvas, gitSha) {
    const sha = gitSha ? gitSha.substring(0, 7) : 'dev';
    // Phase C: This text will be truncated in buildUI using safeTextForBox
    return `MK1 ${sha} layout=${spec.mode} canvas=${canvas.cols}x${canvas.rows} reason=${spec.reason}`;
}
/**
 * Clamp box dimensions to safe viewport
 * Ensures no element extends past safeCols/safeRows
 * CRITICAL: In 0-indexed system, if safeCols=280, usable columns are 0-279
 *           So left + width must be <= safeCols (meaning max right edge is at column safeCols-1)
 */
function clampBox(rect, safeCols, safeRows) {
    // Clamp position to valid range (0 to safeCols-1 for x, 0 to safeRows-1 for y)
    const left = Math.max(0, Math.min(rect.x, safeCols - 1));
    const top = Math.max(0, Math.min(rect.y, safeRows - 1));
    // CRITICAL: Ensure left + width < safeCols (strictly less)
    // If safeCols=280, usable columns are 0-279 (280 columns)
    // If left=0 and width=280, right edge is at column 280 (1 past usable)
    // So we need: left + width < safeCols, meaning width < safeCols - left
    // Or equivalently: width <= safeCols - left - 1
    const maxWidth = Math.max(1, safeCols - left - 1); // Ensure right edge < safeCols
    const maxHeight = Math.max(1, safeRows - top - 1); // Ensure bottom edge < safeRows
    let width = Math.max(1, Math.min(rect.w, maxWidth));
    let height = Math.max(1, Math.min(rect.h, maxHeight));
    // Final safety check - ensure right edge is strictly less than safeCols
    if (left + width >= safeCols) {
        width = Math.max(1, safeCols - left - 1);
        fileLogger.force(`[CLAMP] FORCED: left=${left} width clamped to ${width} (was ${rect.w}), rightEdge=${left + width} < safeCols=${safeCols}`);
    }
    if (top + height >= safeRows) {
        height = Math.max(1, safeRows - top - 1);
        fileLogger.force(`[CLAMP] FORCED: top=${top} height clamped to ${height} (was ${rect.h}), bottomEdge=${top + height} < safeRows=${safeRows}`);
    }
    // Verify final result - must be strictly less
    if (left + width >= safeCols || top + height >= safeRows) {
        fileLogger.force(`[CLAMP] ERROR: Still invalid after clamping! left=${left} width=${width} top=${top} height=${height} safeCols=${safeCols} safeRows=${safeRows}`);
    }
    return { left, top, width, height };
}
/**
 * Build UI from layout spec
 *
 * This is the ONLY place widgets are created.
 */
export function buildUI(screen, spec, canvas, commandValue = '', gitSha) {
    const uiRefs = {
        rootChildren: [],
        commandValue,
    };
    // Clamp all rects before creating widgets
    // canvas.cols/rows already account for SAFE_PAD, so use them directly
    // But ensure we never exceed them
    const safeCols = canvas.cols;
    const safeRows = canvas.rows;
    // Phase A: Use safeCols directly (already has RIGHT_GAP applied in getSafeViewport)
    // No more usableCols - safeCols IS the usable width
    fileLogger.force(`[UI_BUILDER] Building UI with safeCols=${safeCols} safeRows=${safeRows}`);
    // Phase D: Create header - MUST use safeCols width and truncate text
    // Phase C: Header text MUST be truncated to prevent long lines causing scrollbars
    const headerRect = clampBox(spec.rects.header, safeCols, safeRows);
    const headerTextRaw = buildHeaderText(spec, canvas, gitSha);
    const headerContent = safeTextForBox(headerTextRaw, headerRect.width, false, 0, 0); // No borders, no padding
    fileLogger.force(`[UI_BUILDER] Header: left=${headerRect.left} width=${headerRect.width} rightEdge=${headerRect.left + headerRect.width} safeCols=${safeCols} contentLength=${headerContent.length}`);
    // Phase E: Critical anti-scroll rule - verify header doesn't exceed safeCols
    if (headerRect.left + headerRect.width > safeCols) {
        throw new Error(`[UI_BUILDER ERROR] Header exceeds safeCols: left=${headerRect.left} width=${headerRect.width} ` +
            `rightEdge=${headerRect.left + headerRect.width} > safeCols=${safeCols}`);
    }
    const header = blessed.box({
        left: headerRect.left,
        top: headerRect.top,
        width: headerRect.width,
        height: headerRect.height,
        content: headerContent, // Phase C: Already truncated
        style: {
            fg: 'yellow',
        },
        tags: false, // Phase C: Disable tags to prevent escape sequences
    });
    // Append to screen FIRST (before any other operations)
    screen.append(header);
    uiRefs.rootChildren.push(header);
    uiRefs.header = header;
    // Ensure it's shown (use safe method)
    setShown(header, true);
    // Create panels
    const panels = [
        { name: 'posture', title: 'POSTURE' },
        { name: 'resources', title: 'RESOURCES' },
        { name: 'assets', title: 'ASSETS' },
        { name: 'operations', title: 'OPERATIONS' },
        { name: 'network', title: 'NETWORK' },
        { name: 'capabilities', title: 'CAPABILITIES' },
    ];
    for (const { name, title } of panels) {
        const rect = spec.rects[name];
        if (!rect) {
            fileLogger.warn(`Missing rect for panel: ${name}`);
            continue;
        }
        // Clamp dimensions before creating widget
        const clampedRect = clampBox(rect, safeCols, safeRows);
        // Phase C1/C2: Ensure panels use explicit integers from layout spec (no %, no right: 0)
        // CRITICAL: All values must be integers, not strings, not percentages
        const panelLeft = Math.floor(clampedRect.left);
        const panelTop = Math.floor(clampedRect.top);
        const panelWidth = Math.floor(clampedRect.width);
        const panelHeight = Math.floor(clampedRect.height);
        // Verify these are actual integers (sanity check)
        if (isNaN(panelLeft) || isNaN(panelTop) || isNaN(panelWidth) || isNaN(panelHeight)) {
            throw new Error(`[UI_BUILDER] Invalid panel dimensions for ${name}: left=${panelLeft} top=${panelTop} width=${panelWidth} height=${panelHeight}`);
        }
        fileLogger.force(`[UI_BUILDER] ${name}: left=${panelLeft} top=${panelTop} width=${panelWidth} height=${panelHeight} rightEdge=${panelLeft + panelWidth}`);
        let widget;
        // CLOSED BOXES: Every panel has all 4 borders (top, bottom, left, right) to form a complete rectangle
        // All panels have all borders for visual closure
        const hasLeftBorder = true;
        const hasRightBorder = true;
        const hasTopBorder = true;
        const hasBottomBorder = true;
        // CLOSED BOXES: All borders are always present
        // User: "boarder on the right side of the network and capabilities tab have zero margins or padding"
        // For NETWORK and CAPABILITIES (right column), remove space before right border to save 1 char
        const isRightColumn = name === 'network' || name === 'capabilities';
        const rightBorderPadding = isRightColumn ? 0 : 1; // Zero padding for right column panels
        const leftBorderPadding = 1; // Always 1 space after left border
        // Content width: panelWidth minus borders and padding
        // Structure: leftBorder(│) + leftPadding(space) + content + rightPadding(space?) + rightBorder(│) = panelWidth
        // For normal panels: 1 + 1 + content + 1 + 1 = panelWidth → content = panelWidth - 4
        // For right-column (zero padding): 1 + 1 + content + 0 + 1 = panelWidth → content = panelWidth - 3
        const contentWidth = Math.max(1, panelWidth - 1 - leftBorderPadding - rightBorderPadding - 1);
        fileLogger.force(`[UI_BUILDER] ${name} contentWidth=${contentWidth} (panelWidth=${panelWidth} rightBorderPadding=${rightBorderPadding} isRightColumn=${isRightColumn})`);
        // Build closed box borders (all 4 sides) - FULL WIDTH
        // Every panel must be a complete closed rectangle with all 4 sides connected
        const leftChar = '│';
        const rightChar = '│';
        const topLeftChar = '┌';
        const topRightChar = '┐';
        const bottomLeftChar = '└';
        const bottomRightChar = '┘';
        const topHorizontalChar = '─';
        const bottomHorizontalChar = '─';
        const separatorLeftChar = '├';
        const separatorRightChar = '┤';
        const separatorHorizontalChar = '─';
        // Helper function to ensure line is exactly panelWidth (CRITICAL for closed boxes)
        const ensureWidth = (line, targetWidth, label) => {
            if (line.length === targetWidth) {
                return line; // Perfect - already correct width
            }
            else if (line.length > targetWidth) {
                fileLogger.force(`[BORDER] ${name} ${label} too long: ${line.length} > ${targetWidth}, truncating`);
                return line.substring(0, targetWidth);
            }
            else {
                // Pad to exact width - CRITICAL for closed boxes
                // For borders, pad with ─ characters for horizontal lines, spaces for content
                if (label.includes('Border') && (line.startsWith('┌') || line.startsWith('└'))) {
                    // For top/bottom borders, pad with ─ characters in the middle
                    const corner1 = line[0];
                    const corner2 = line[line.length - 1];
                    const currentLineLength = line.length - 2; // Exclude corners
                    const needed = targetWidth - 2 - currentLineLength;
                    if (needed > 0) {
                        const fillChar = '─';
                        return `${corner1}${line.substring(1, line.length - 1)}${fillChar.repeat(needed)}${corner2}`;
                    }
                }
                // For other lines (title, separator, body), pad with spaces
                return line.padEnd(targetWidth, ' ');
            }
        };
        // Horizontal line MUST span full width minus corner chars to form complete box
        // panelWidth includes borders, so: corners (2) + horizontal line (panelWidth - 2) = panelWidth
        const horizontalLineLength = Math.max(0, panelWidth - 2); // Full width minus 2 corner chars
        const topLine = topHorizontalChar.repeat(horizontalLineLength);
        const bottomLine = bottomHorizontalChar.repeat(horizontalLineLength);
        const separatorLine = separatorHorizontalChar.repeat(horizontalLineLength);
        // Build closed box borders - these MUST be exactly panelWidth to form complete rectangles
        const topBorder = `${topLeftChar}${topLine}${topRightChar}`;
        const bottomBorder = `${bottomLeftChar}${bottomLine}${bottomRightChar}`;
        const separator = `${separatorLeftChar}${separatorLine}${separatorRightChar}`; // Separator with full borders
        // Title row with borders on both sides
        // User: Right column panels (NETWORK, CAPABILITIES) have zero padding before right border
        const paddedTitle = title.substring(0, contentWidth).padEnd(contentWidth, ' ');
        const titleRow = rightBorderPadding === 0
            ? `${leftChar} ${paddedTitle}${rightChar}` // No space before right border
            : `${leftChar} ${paddedTitle} ${rightChar}`; // Normal spacing
        // Ensure all border lines are exactly panelWidth (forms complete closed rectangles)
        const safeTopBorder = ensureWidth(topBorder, panelWidth, 'topBorder');
        const safeTitleRow = ensureWidth(titleRow, panelWidth, 'titleRow');
        const safeSeparator = ensureWidth(separator, panelWidth, 'separator');
        const safeBottomBorder = ensureWidth(bottomBorder, panelWidth, 'bottomBorder');
        // Content area width (inside borders)
        const maxContentWidth = contentWidth;
        if (name === 'operations') {
            // Operations panel with feed content (center column, not right column, so has normal padding)
            // Phase C: Use safeTextForBox to truncate operations feed content
            const feedContent = formatOperationsFeed(maxContentWidth);
            // Operations is in center column, so has normal padding: leftBorder(│) + padding(1) + content + padding(1) + rightBorder(│) = panelWidth
            // internal width = panelWidth - 4 (borders + padding)
            const internalWidth = panelWidth - 4; // 2 for borders (left+right), 2 for padding (left+right) - operations has normal padding
            const safeFeedContent = safeTextLines(feedContent, internalWidth);
            const contentLines = safeFeedContent.split('\n');
            // Build body lines with left and right borders - each line must be exactly panelWidth
            const bodyLines = contentLines.map(line => {
                // Phase C: Each line is already truncated by safeTextLines, but ensure it fits inside borders
                const safeLine = line.length > maxContentWidth ? line.substring(0, maxContentWidth - 3) + '...' : line;
                const bodyLine = `${leftChar} ${safeLine.padEnd(maxContentWidth, ' ')} ${rightChar}`;
                // Ensure body line is exactly panelWidth (same as borders)
                return ensureWidth(bodyLine, panelWidth, 'bodyLine');
            });
            // Calculate how much space we've used (all borders are always present)
            const headerHeight = 3; // top border, title row, separator
            const footerHeight = 1; // bottom border
            const usedContentHeight = bodyLines.length;
            const remainingHeight = Math.max(0, panelHeight - headerHeight - footerHeight - usedContentHeight);
            // Empty body lines must also be exactly panelWidth with borders
            const emptyBodyLine = ensureWidth(`${leftChar} ${' '.repeat(maxContentWidth)} ${rightChar}`, panelWidth, 'emptyBodyLine');
            const emptyBodyLines = Array(remainingHeight).fill(emptyBodyLine);
            // Build content with all borders (closed box) - every line is exactly panelWidth
            const fullContent = `${safeTopBorder}\n${safeTitleRow}\n${safeSeparator}\n${bodyLines.join('\n')}\n${emptyBodyLines.join('\n')}\n${safeBottomBorder}`;
            widget = blessed.box({
                left: panelLeft,
                top: panelTop,
                width: panelWidth,
                height: panelHeight,
                content: fullContent,
                style: {
                    fg: 'cyan',
                    bg: 'black',
                },
                tags: false,
                scrollable: false,
                alwaysScroll: false,
            });
        }
        else {
            // Other panels with placeholder content
            // Phase C: Use safeTextLines to truncate placeholder content
            const placeholder = getPlaceholderContent(name);
            // hasBorders=true (2 cols), padding=1 each side = 4 total, so internal width = panelWidth - 4
            const internalWidth = panelWidth - 4; // 2 for borders (left+right), 2 for padding (left+right)
            const safePlaceholder = safeTextLines(placeholder, internalWidth);
            const contentLines = safePlaceholder.split('\n');
            // Build body lines with left and right borders - each line must be exactly panelWidth
            // User: Right column panels have zero padding before right border
            const bodyLines = contentLines.map(line => {
                // Phase C: Each line is already truncated by safeTextLines, but ensure it fits inside borders
                const safeLine = line.length > maxContentWidth ? line.substring(0, maxContentWidth - 3) + '...' : line;
                const bodyLine = rightBorderPadding === 0
                    ? `${leftChar} ${safeLine.padEnd(maxContentWidth, ' ')}${rightChar}` // No space before right border
                    : `${leftChar} ${safeLine.padEnd(maxContentWidth, ' ')} ${rightChar}`; // Normal spacing
                // Ensure body line is exactly panelWidth (same as borders)
                return ensureWidth(bodyLine, panelWidth, 'bodyLine');
            });
            // Calculate how much space we've used (all borders are always present)
            const headerHeight = 3; // top border, title row, separator
            const footerHeight = 1; // bottom border
            const usedContentHeight = bodyLines.length;
            const remainingHeight = Math.max(0, panelHeight - headerHeight - footerHeight - usedContentHeight);
            // Empty body lines must also be exactly panelWidth with borders
            // User: Right column panels have zero padding before right border
            const emptyBodyLinePattern = rightBorderPadding === 0
                ? `${leftChar} ${' '.repeat(maxContentWidth)}${rightChar}` // No space before right border
                : `${leftChar} ${' '.repeat(maxContentWidth)} ${rightChar}`; // Normal spacing
            const emptyBodyLine = ensureWidth(emptyBodyLinePattern, panelWidth, 'emptyBodyLine');
            const emptyBodyLines = Array(remainingHeight).fill(emptyBodyLine);
            // Build content with all borders (closed box) - every line is exactly panelWidth
            const fullContent = `${safeTopBorder}\n${safeTitleRow}\n${safeSeparator}\n${bodyLines.join('\n')}\n${emptyBodyLines.join('\n')}\n${safeBottomBorder}`;
            widget = blessed.box({
                left: panelLeft,
                top: panelTop,
                width: panelWidth,
                height: panelHeight,
                content: fullContent,
                style: {
                    fg: 'cyan',
                    bg: 'black', // Solid black background, not faded
                },
                tags: false,
                scrollable: false,
                alwaysScroll: false,
            });
        }
        // Append to screen FIRST
        screen.append(widget);
        uiRefs.rootChildren.push(widget);
        uiRefs[name] = widget;
        // Ensure it's shown (use safe method)
        setShown(widget, true);
    }
    // Create command line (clamp dimensions)
    // Command spans full width - ensure it's clamped
    const commandRect = clampBox(spec.rects.command, safeCols, safeRows);
    fileLogger.force(`[UI_BUILDER] Command: left=${commandRect.left} width=${commandRect.width} rightEdge=${commandRect.left + commandRect.width} safeCols=${safeCols}`);
    const prompt = '4runr> ' + commandValue;
    const command = blessed.box({
        left: commandRect.left,
        top: commandRect.top,
        width: commandRect.width,
        height: commandRect.height,
        content: prompt,
        style: {
            fg: 'green',
        },
        tags: true,
        scrollable: false,
        alwaysScroll: false,
    });
    // Append to screen FIRST
    screen.append(command);
    uiRefs.rootChildren.push(command);
    uiRefs.command = command;
    // Ensure it's shown (use safe method)
    setShown(command, true);
    // Add separator line right below command line - use balanced width
    // Balance: Use 70% for footer (enough visual but safe from overflow)
    const footerLineY = commandRect.top + commandRect.height;
    if (footerLineY < safeRows) {
        const FOOTER_WIDTH_PERCENT = 0.70; // Balance: enough visual but safe
        const maxSeparatorWidth = Math.floor(commandRect.width * FOOTER_WIDTH_PERCENT);
        const footerLine = '─'.repeat(Math.max(0, maxSeparatorWidth));
        // Hard clamp: ensure footer never exceeds safeCols
        const footerWidth = Math.min(maxSeparatorWidth, safeCols - commandRect.left - 2); // -2 for safety margin
        const footer = blessed.box({
            left: commandRect.left,
            top: footerLineY,
            width: footerWidth,
            height: 1,
            content: footerLine.substring(0, footerWidth), // Truncate content to match width
            style: {
                fg: 'cyan',
            },
            tags: false,
            scrollable: false,
            alwaysScroll: false,
        });
        screen.append(footer);
        uiRefs.rootChildren.push(footer);
        setShown(footer, true);
        fileLogger.force(`[FOOTER] width=${footerWidth} (70% of ${commandRect.width}) rightEdge=${commandRect.left + footerWidth} safeCols=${safeCols}`);
    }
    // Phase B: Offender scanner is now run AFTER render in mk1App.ts
    // (Called via scanAndLogOffenders which is imported there)
    // No need to run it here - it will catch offenders after screen.render()
    // Phase B: Log basic build info for debugging
    const childCount = screen.children?.length || 0;
    fileLogger.force(`[UI_BUILDER] Built ${uiRefs.rootChildren.length} widgets, screen has ${childCount} children, safeCols=${safeCols} safeRows=${safeRows}`);
    // Phase B: Offender scanner is now run AFTER render in mk1App.ts
    // (Called via scanAndLogOffenders which is imported there)
    // No need to run it here - it will catch offenders after screen.render()
    return uiRefs;
}
/**
 * Update operations feed in existing UI
 */
export function updateOperationsFeed(uiRefs) {
    if (!uiRefs.operations) {
        return;
    }
    // CRITICAL: Ensure content doesn't cause overflow
    // Get current widget dimensions and clamp content
    const widget = uiRefs.operations;
    const maxWidth = (widget.width || 0) - 0; // Use full width (header already has margin)
    const feedContent = formatOperationsFeed(maxWidth);
    // Preserve header when updating content
    const currentContent = (widget.content || '');
    const headerLine = currentContent.split('\n')[0] || '';
    const newContent = headerLine ? `${headerLine}\n${feedContent}` : feedContent;
    uiRefs.operations.setContent(newContent);
    uiRefs.operations.content = newContent;
}
/**
 * Update command line value
 */
export function updateCommandValue(uiRefs, value) {
    uiRefs.commandValue = value;
    if (uiRefs.command) {
        const prompt = '4runr> ' + value;
        uiRefs.command.setContent(prompt);
        uiRefs.command.content = prompt;
    }
}
//# sourceMappingURL=uiBuilder.js.map