/**
 * Coder footer for the Pi TUI.
 *
 * Uses transparent backgrounds with foreground-only ANSI true-color text.
 * Includes a braille spinner animation when an agent is actively working.
 *
 * Layout:
 *   [brand] [branch] > [model/agent]        [hub] | [N] label  token-stats
 *
 * Observer awareness (ASCII only):
 *   [3] SwiftRaven — 3 observers watching, session label "SwiftRaven"
 */
const RESET = '\x1b[0m';
const SEP = '>';
function fg(color, text) {
    return `\x1b[38;2;${color[0]};${color[1]};${color[2]}m${text}`;
}
// ──────────────────────────────────────────────
// Color palette (foreground only, no backgrounds)
// ──────────────────────────────────────────────
const FG_BRAND = [100, 200, 255];
const FG_MODEL = [215, 135, 175];
const FG_AGENT = [130, 200, 130];
const FG_BRANCH = [150, 180, 150];
const FG_HUB_OK = [80, 200, 120];
const FG_HUB_WARN = [245, 179, 66];
const FG_HUB_ERR = [220, 80, 80];
const FG_DIM = [100, 110, 120];
const FG_OBSERVER = [140, 180, 220];
// ──────────────────────────────────────────────
// Braille spinner
// ──────────────────────────────────────────────
const SPINNER_FRAMES = [
    '\u280B',
    '\u2819',
    '\u2839',
    '\u2838',
    '\u283C',
    '\u2834',
    '\u2826',
    '\u2827',
    '\u2807',
    '\u280F',
];
// ──────────────────────────────────────────────
// Footer builder (transparent bg, foreground only)
// ──────────────────────────────────────────────
/** Strip ANSI escape sequences to get visible character count. */
function visibleLength(str) {
    // eslint-disable-next-line no-control-regex
    return str.replace(/\x1b\[[0-9;]*m/g, '').length;
}
/** Truncate an ANSI-colored string to a maximum visible width. */
function truncateAnsi(str, maxWidth) {
    let visible = 0;
    let i = 0;
    while (i < str.length && visible < maxWidth) {
        if (str[i] === '\x1b') {
            // Skip entire ANSI escape sequence
            const end = str.indexOf('m', i);
            if (end !== -1) {
                i = end + 1;
            }
            else {
                i++;
            }
        }
        else {
            visible++;
            i++;
        }
    }
    return str.slice(0, i) + RESET;
}
function buildFooter(left, rightText, width) {
    // Safety margin for Unicode characters that may be double-width
    const safeWidth = width - 4;
    const leftLen = visibleLength(left);
    const rightLen = visibleLength(rightText);
    const total = leftLen + 1 + rightLen;
    if (total > safeWidth) {
        const maxLeft = safeWidth - rightLen - 1;
        if (maxLeft > 0) {
            return truncateAnsi(left, maxLeft) + ' ' + rightText;
        }
        return truncateAnsi(left + ' ' + rightText, safeWidth);
    }
    const gap = safeWidth - leftLen - rightLen;
    return left + ' '.repeat(gap) + rightText;
}
// ──────────────────────────────────────────────
// Minimal component
// ──────────────────────────────────────────────
class FooterComponent {
    getText;
    _unsubscribeBranch;
    constructor(getText, footerData, cleanupSpinner) {
        this.getText = getText;
        this._cleanupSpinner = cleanupSpinner;
        this._unsubscribeBranch = footerData.onBranchChange(() => {
            // Triggers TUI refresh
        });
    }
    _cleanupSpinner;
    render(width) {
        const text = this.getText(width);
        // Final safety: ensure line never exceeds terminal width
        if (visibleLength(text) > width) {
            return [truncateAnsi(text, width)];
        }
        return [text];
    }
    invalidate() {
        // no-op
    }
    dispose() {
        this._unsubscribeBranch?.();
        this._cleanupSpinner();
    }
}
// ──────────────────────────────────────────────
// Token stat formatters
// ──────────────────────────────────────────────
function formatTokens(n) {
    if (n >= 1_000_000)
        return `${(n / 1_000_000).toFixed(1)}M`;
    if (n >= 1_000)
        return `${(n / 1_000).toFixed(1)}K`;
    return String(n);
}
function formatCost(n) {
    if (n === 0)
        return '$0.00';
    if (n < 0.01)
        return `$${n.toFixed(4)}`;
    return `$${n.toFixed(2)}`;
}
// ──────────────────────────────────────────────
// Public API
// ──────────────────────────────────────────────
/**
 * Set up the Coder footer (transparent bg, foreground-colored text).
 * Call this once with the extension context to replace Pi's default footer.
 *
 * Includes a braille spinner animation when an agent is actively working.
 *
 * @param pi Extension API
 * @param ctx  Extension context with UI access
 * @param getHubStatus  Callback that returns current Hub connection status
 * @param getObserverState  Optional callback that returns observer count + session label
 */
export function setupCoderFooter(pi, ctx, getHubStatus, getObserverState) {
    if (!ctx.hasUI)
        return;
    ctx.ui.setWorkingIndicator({ frames: [] }); // turn off the working indicator since we use our own spinner
    let pendingRequests = 0;
    let spinnerTimer = null;
    let spinnerFrame = 0;
    let activeAgentRunning = false;
    let requestFooterRender;
    const startSpinner = () => {
        if (spinnerTimer)
            return;
        spinnerTimer = setInterval(() => {
            spinnerFrame = (spinnerFrame + 1) % SPINNER_FRAMES.length;
            requestFooterRender?.();
        }, 80);
    };
    const stopSpinner = () => {
        if (!spinnerTimer)
            return;
        clearInterval(spinnerTimer);
        spinnerTimer = null;
        spinnerFrame = 0;
    };
    const syncSpinner = () => {
        if (pendingRequests > 0 || activeAgentRunning) {
            startSpinner();
        }
        else {
            stopSpinner();
        }
    };
    pi.on('before_provider_request', () => {
        pendingRequests += 1;
        if (pendingRequests === 1) {
            startSpinner();
        }
    });
    pi.on('after_provider_response', () => {
        pendingRequests = Math.max(0, pendingRequests - 1);
        syncSpinner();
    });
    pi.on('session_shutdown', () => {
        pendingRequests = 0;
        activeAgentRunning = false;
        stopSpinner();
    });
    ctx.ui.setFooter((tui, _theme, footerData) => {
        requestFooterRender = () => tui.requestRender();
        const getText = (width) => {
            // Detect active agent
            const activeAgent = footerData.getExtensionStatuses().get('active_agent');
            activeAgentRunning = !!activeAgent;
            syncSpinner();
            // Token stats from session messages
            let inputTokens = 0;
            let outputTokens = 0;
            let totalCost = 0;
            // TODO (jhaynie): we need to think about how to handle our AI Gateway costs vs whats coming from pi
            for (const entry of ctx.sessionManager.getBranch()) {
                if (entry.type === 'message') {
                    const msg = entry.message;
                    if (msg.role === 'assistant' && msg.usage) {
                        inputTokens += msg.usage.input;
                        outputTokens += msg.usage.output;
                        totalCost += msg.usage.cost.total;
                    }
                }
            }
            const tokenStr = `\u2191${formatTokens(inputTokens)} \u2193${formatTokens(outputTokens)} ${formatCost(totalCost)}`;
            // LEFT side: brand > branch > model/agent
            const leftParts = [];
            // Brand (with spinner)
            const brandChar = spinnerTimer ? SPINNER_FRAMES[spinnerFrame] : '\u2A3A';
            leftParts.push(fg(FG_BRAND, ` ${brandChar}`));
            // Branch
            const branch = footerData.getGitBranch();
            if (branch) {
                leftParts.push('  ');
                leftParts.push(fg(FG_BRANCH, branch));
            }
            // Model or active agent
            leftParts.push(fg(FG_DIM, ` ${SEP} `));
            if (activeAgent) {
                leftParts.push(fg(FG_AGENT, activeAgent));
            }
            else {
                const modelId = ctx.model ? String(ctx.model.id ?? '?') : '?';
                leftParts.push(fg(FG_MODEL, modelId));
            }
            const left = leftParts.join('');
            // RIGHT side: hub status + observer info + token stats
            const rightParts = [];
            const hubStatus = getHubStatus();
            if (hubStatus === 'connected') {
                rightParts.push(fg(FG_HUB_OK, 'Hub'));
            }
            else if (hubStatus === 'reconnecting') {
                rightParts.push(fg(FG_HUB_WARN, 'Hub...'));
            }
            else {
                rightParts.push(fg(FG_HUB_ERR, 'Hub Off'));
            }
            // Observer awareness (ASCII only, no emojis)
            if (getObserverState && hubStatus === 'connected') {
                const obs = getObserverState();
                if (obs.count > 0 || obs.label) {
                    rightParts.push(fg(FG_DIM, ' | '));
                    if (obs.count > 0) {
                        rightParts.push(fg(FG_OBSERVER, `[${obs.count}]`));
                        if (obs.label)
                            rightParts.push(' ');
                    }
                    if (obs.label) {
                        rightParts.push(fg(FG_OBSERVER, obs.label));
                    }
                }
            }
            rightParts.push(fg(FG_DIM, `  ${tokenStr}`) + RESET);
            const rightText = rightParts.join('');
            return buildFooter(left, rightText, width);
        };
        const cleanupSpinner = () => {
            if (spinnerTimer) {
                clearInterval(spinnerTimer);
                spinnerTimer = null;
            }
        };
        return new FooterComponent(getText, footerData, cleanupSpinner);
    });
}
//# sourceMappingURL=footer.js.map