/**
 * OutputRouter — routes OmkEvent to the appropriate renderer based on OutputProfile.stdoutMode.
 *
 * Invariant I-004: provider stdout MUST NOT bypass the router.
 * Raw provider stdout is never emitted directly; all output flows through a Renderer.
 */
import { createThemeRenderer, createNlpRenderer, createJsonRenderer, } from "./renderers.js";
import { getBuiltinTheme } from "../cli/theme/theme-registry.js";
import { resolveTheme } from "../cli/theme/theme-resolver.js";
import { createNlgRenderer } from "./nlg-renderer.js";
// ─── Implementation ──────────────────────────────────────────────────────────
class OutputRouterImpl {
    renderer;
    stdoutMode;
    themeRenderer;
    nlpRenderer;
    jsonRenderer;
    nlgRenderer;
    constructor(profile) {
        this.stdoutMode = profile.stdoutMode;
        // Resolve ThemePalette from the theme system (flag → env → config → terminal)
        const palette = (() => {
            try {
                const resolved = resolveTheme({ cwd: process.cwd() });
                return getBuiltinTheme(resolved.name);
            }
            catch {
                return undefined;
            }
        })();
        // Initialize NLG renderer with the resolved palette
        this.nlgRenderer = createNlgRenderer({ palette, write: process.stdout.write.bind(process.stdout) });
        switch (profile.stdoutMode) {
            case "theme":
            case "human": {
                const useColor = profile.color === "always" || (profile.color === "auto" && process.stdout.isTTY === true);
                const r = createThemeRenderer(useColor
                    ? process.stdout.write.bind(process.stdout)
                    : stripColorWriter, useColor ? palette : undefined);
                this.themeRenderer = r;
                this.renderer = r;
                break;
            }
            case "nlp": {
                const r = createNlpRenderer();
                this.nlpRenderer = r;
                this.renderer = r;
                break;
            }
            case "json":
            case "machine": {
                const r = createJsonRenderer();
                this.jsonRenderer = r;
                this.renderer = r;
                break;
            }
            case "raw": {
                // "raw" is an escape hatch that still routes through the NLP renderer
                // to enforce I-004: provider stdout never bypasses the router.
                const r = createNlpRenderer();
                this.nlpRenderer = r;
                this.renderer = r;
                break;
            }
            default: {
                const _exhaustive = profile.stdoutMode;
                void _exhaustive;
                const r = createNlpRenderer();
                this.nlpRenderer = r;
                this.renderer = r;
            }
        }
    }
    route(event) {
        if (this.themeRenderer) {
            routeToTheme(this.themeRenderer, event);
        }
        else if (this.nlpRenderer) {
            this.nlpRenderer.render(event);
        }
        else if (this.jsonRenderer) {
            this.jsonRenderer.render(event);
        }
    }
    routeTrace(trace) {
        if (this.nlgRenderer) {
            this.nlgRenderer.renderTurnResult(trace);
        }
        else if (this.themeRenderer) {
            // Fallback: use trace summary as progress
            const summary = trace.result.summary;
            this.themeRenderer.renderProgress(summary);
        }
        else if (this.jsonRenderer) {
            this.jsonRenderer.render({
                type: "result",
                timestamp: new Date().toISOString(),
                data: { kind: "result", content: JSON.stringify(trace), format: "json" },
            });
        }
    }
    renderStatusBar(provider, model, intent) {
        if (this.themeRenderer) {
            this.themeRenderer.renderStatusBar(provider, model, intent);
        }
    }
    flush() {
        if (this.themeRenderer) {
            this.themeRenderer.flush();
        }
        else if (this.nlpRenderer) {
            const text = this.nlpRenderer.flush();
            if (text.length > 0) {
                process.stdout.write(text + "\n");
            }
        }
        else if (this.jsonRenderer) {
            const records = this.jsonRenderer.flush();
            if (records.length > 0) {
                process.stdout.write(JSON.stringify(records, null, 2) + "\n");
            }
        }
    }
    getRenderer() {
        return this.renderer;
    }
}
// ─── Factory ─────────────────────────────────────────────────────────────────
export function createOutputRouter(profile) {
    return new OutputRouterImpl(profile);
}
// ─── Helpers ─────────────────────────────────────────────────────────────────
/**
 * Route an OmkEvent to the ThemeRenderer's typed methods.
 */
function routeToTheme(renderer, event) {
    if (!event.data) {
        return;
    }
    switch (event.data.kind) {
        case "turn_started":
            renderer.renderTurnStarted(event.data.intent, event.data.provider);
            break;
        case "progress":
            renderer.renderProgress(event.data.message, event.data.percent);
            break;
        case "mcp_status":
            renderer.renderMcpStatus(event.data.server, event.data.status);
            break;
        case "warning":
            renderer.renderWarning(event.data.message);
            break;
        case "result":
            renderer.renderResult(event.data.content);
            break;
        case "error":
            renderer.renderError(event.data.message, event.data.recoverable);
            break;
        case "turn_finished":
            renderer.renderTurnFinished(event.data.durationMs);
            break;
    }
}
/**
 * Writer that strips ANSI escape codes — used when profile.color is false.
 */
function stripColorWriter(text) {
    const stripped = text.replace(/\x1b\[[0-9;]*m/g, "");
    process.stdout.write(stripped);
    return true;
}
