import { isAbsolute, relative, resolve, sep } from "node:path";
import { truncateToWidth, visibleWidth } from "@voxcode-dev/voxcode-tui";
import { theme } from "../theme/theme.js";
/**
 * Sanitize text for display in a single-line status.
 * Removes newlines, tabs, carriage returns, and other control characters.
 */
function sanitizeStatusText(text) {
    return text
        .replace(/[\r\n\t]/g, " ")
        .replace(/ +/g, " ")
        .trim();
}
/**
 * Format token counts for compact footer display.
 */
function formatTokens(count) {
    if (count < 1000)
        return count.toString();
    if (count < 10000)
        return `${(count / 1000).toFixed(1)}k`;
    if (count < 1000000)
        return `${Math.round(count / 1000)}k`;
    if (count < 10000000)
        return `${(count / 1000000).toFixed(1)}M`;
    return `${Math.round(count / 1000000)}M`;
}
/**
 * Format a duration in seconds to a human-readable string.
 * Example: 1h 23m, 45m 12s, 30s
 */
function formatDuration(seconds) {
    const hrs = Math.floor(seconds / 3600);
    const mins = Math.floor((seconds % 3600) / 60);
    const secs = Math.floor(seconds % 60);
    if (hrs > 0) {
        return `${hrs}h ${mins}m`;
    }
    if (mins > 0) {
        return `${mins}m ${secs}s`;
    }
    return `${secs}s`;
}
export function formatCwdForFooter(cwd, home) {
    if (!home)
        return cwd;
    const resolvedCwd = resolve(cwd);
    const resolvedHome = resolve(home);
    const relativeToHome = relative(resolvedHome, resolvedCwd);
    const isInsideHome = relativeToHome === "" ||
        (relativeToHome !== ".." && !relativeToHome.startsWith(`..${sep}`) && !isAbsolute(relativeToHome));
    if (!isInsideHome)
        return cwd;
    return relativeToHome === "" ? "~" : `~${sep}${relativeToHome}`;
}
/**
 * Footer component that shows pwd, token stats, and context usage.
 * Computes token/context stats from session, gets git branch and extension statuses from provider.
 */
export class FooterComponent {
    autoCompactEnabled = true;
    session;
    footerData;
    constructor(session, footerData) {
        this.session = session;
        this.footerData = footerData;
    }
    setSession(session) {
        this.session = session;
    }
    setAutoCompactEnabled(enabled) {
        this.autoCompactEnabled = enabled;
    }
    /**
     * No-op: git branch caching now handled by provider.
     * Kept for compatibility with existing call sites in interactive-mode.
     */
    invalidate() {
        // No-op: git branch is cached/invalidated by provider
    }
    /**
     * Clean up resources.
     * Git watcher cleanup now handled by provider.
     */
    dispose() {
        // Git watcher cleanup handled by provider
    }
    render(width) {
        const state = this.session.state;
        // Calculate cumulative usage from ALL session entries (not just post-compaction messages)
        let totalInput = 0;
        let totalOutput = 0;
        let totalCacheRead = 0;
        let totalCacheWrite = 0;
        let totalCost = 0;
        for (const entry of this.session.sessionManager.getEntries()) {
            if (entry.type === "message" && entry.message.role === "assistant") {
                totalInput += entry.message.usage.input;
                totalOutput += entry.message.usage.output;
                totalCacheRead += entry.message.usage.cacheRead;
                totalCacheWrite += entry.message.usage.cacheWrite;
                totalCost += entry.message.usage.cost.total;
            }
        }
        // Calculate context usage from session (handles compaction correctly).
        // After compaction, tokens are unknown until the next LLM response.
        const contextUsage = this.session.getContextUsage();
        const contextWindow = contextUsage?.contextWindow ?? state.model?.contextWindow ?? 0;
        const contextPercentValue = contextUsage?.percent ?? 0;
        const contextPercent = contextUsage?.percent !== null ? contextPercentValue.toFixed(1) : "?";
        // Replace home directory with ~
        const pwd = formatCwdForFooter(this.session.sessionManager.getCwd(), process.env.HOME || process.env.USERPROFILE);
        // Build info segments
        const pwdSegments = [];
        // Add git branch if available
        const branch = this.footerData.getGitBranch();
        if (branch) {
            pwdSegments.push(branch);
        }
        // Add session name if set
        const sessionName = this.session.sessionManager.getSessionName();
        if (sessionName) {
            pwdSegments.push(sessionName);
        }
        // Build stats segments
        const statsSegments = [];
        // Model name + thinking level
        const displayName = state.model?.name || state.model?.id || "no-model";
        if (state.model?.reasoning) {
            const thinkingLevel = state.thinkingLevel || "off";
            statsSegments.push(thinkingLevel === "off" ? `${displayName} off` : `${displayName} ${thinkingLevel}`);
        }
        else {
            statsSegments.push(displayName);
        }
        if (this.footerData.getAvailableProviderCount() > 1 && state.model) {
            statsSegments[0] = `${state.model.provider}:${statsSegments[0]}`;
        }
        // Token stats
        if (totalInput)
            statsSegments.push(`↑${formatTokens(totalInput)}`);
        if (totalOutput)
            statsSegments.push(`↓${formatTokens(totalOutput)}`);
        if (totalCacheRead)
            statsSegments.push(`R${formatTokens(totalCacheRead)}`);
        if (totalCacheWrite)
            statsSegments.push(`W${formatTokens(totalCacheWrite)}`);
        // Cost
        const usingSubscription = state.model ? this.session.modelRegistry.isUsingOAuth(state.model) : false;
        if (totalCost || usingSubscription) {
            const costStr = `$${totalCost.toFixed(3)}${usingSubscription ? " (sub)" : ""}`;
            statsSegments.push(costStr);
        }
        // Context usage
        const autoIndicator = this.autoCompactEnabled ? " auto" : "";
        const contextPercentDisplay = contextPercent === "?"
            ? `?/${formatTokens(contextWindow)}${autoIndicator}`
            : `${contextPercent}%/${formatTokens(contextWindow)}${autoIndicator}`;
        let contextFormatted;
        if (contextPercentValue > 90) {
            contextFormatted = theme.fg("error", contextPercentDisplay);
        }
        else if (contextPercentValue > 70) {
            contextFormatted = theme.fg("warning", contextPercentDisplay);
        }
        else {
            contextFormatted = contextPercentDisplay;
        }
        statsSegments.push(contextFormatted);
        // Duration
        const elapsed = Date.now() - this.session.createdAt;
        const durationStr = formatDuration(elapsed / 1000);
        statsSegments.push(durationStr);
        // Build stats line with middot separators
        // Start from the right: always keep duration, then work leftwards
        const separator = theme.fg("dim", " • ");
        const separatorWidth = visibleWidth(" • ");
        let statsLineRight = "";
        let statsLineRightWidth = 0;
        let statsLineLeft = "";
        let statsLineLeftWidth = 0;
        // Build from the right (duration first, then keep adding left segments)
        statsLineRight = statsSegments[statsSegments.length - 1];
        statsLineRightWidth = visibleWidth(statsLineRight);
        for (let i = statsSegments.length - 2; i >= 0; i--) {
            const seg = statsSegments[i];
            const segWidth = visibleWidth(seg);
            const needed = segWidth + separatorWidth + statsLineRightWidth;
            if (needed <= width) {
                statsLineRight = seg + separator + statsLineRight;
                statsLineRightWidth = segWidth + separatorWidth + statsLineRightWidth;
            }
            else {
                // Remaining segments go to left side if space permits
                const totalNeeded = statsLineLeftWidth +
                    (statsLineLeftWidth > 0 ? separatorWidth : 0) +
                    segWidth +
                    separatorWidth +
                    statsLineRightWidth;
                if (totalNeeded <= width) {
                    if (statsLineLeftWidth > 0) {
                        statsLineLeft = seg + separator + statsLineLeft;
                    }
                    else {
                        statsLineLeft = seg;
                    }
                    statsLineLeftWidth = segWidth + (statsLineLeftWidth > 0 ? separatorWidth : 0);
                }
                else if (statsLineLeftWidth === 0) {
                    // No space left, truncate
                    break;
                }
            }
        }
        let statsLine;
        if (statsLineLeft) {
            const padding = " ".repeat(Math.max(0, width - statsLineLeftWidth - separatorWidth - statsLineRightWidth));
            statsLine = statsLineLeft + separator + padding + statsLineRight;
        }
        else {
            const padding = " ".repeat(Math.max(0, width - statsLineRightWidth));
            statsLine = padding + statsLineRight;
        }
        // Apply dim to the entire stats line, but preserve color codes already in it (from context %)
        // The dim wrapper goes around the already-colored context segment; since color codes
        // end with reset, wrapping in dim for the whole line doesn't work. Instead, build the
        // line without wrapping context in dim, and dim everything else.
        const dimStatsLine = theme.fg("dim", statsLine);
        // Build PWD line with segments
        let pwdLine = pwd;
        for (const seg of pwdSegments) {
            pwdLine += theme.fg("dim", ` • ${seg}`);
        }
        pwdLine = truncateToWidth(theme.fg("dim", pwdLine), width, theme.fg("dim", "..."));
        const lines = [pwdLine, dimStatsLine];
        // Add extension statuses on a single line, sorted by key alphabetically
        const extensionStatuses = this.footerData.getExtensionStatuses();
        if (extensionStatuses.size > 0) {
            const sortedStatuses = Array.from(extensionStatuses.entries())
                .sort(([a], [b]) => a.localeCompare(b))
                .map(([, text]) => sanitizeStatusText(text));
            const statusLine = sortedStatuses.join(" ");
            // Truncate to terminal width with dim ellipsis for consistency with footer style
            lines.push(truncateToWidth(statusLine, width, theme.fg("dim", "...")));
        }
        return lines;
    }
}
//# sourceMappingURL=footer.js.map