import chalk from 'chalk';
// Filter console.log while listr owns the terminal.
// Blocks [broker] noise and [workflow HH:MM] timing lines, but lets the
// observer URL and channel name through so users can track the run.
function installOutputFilter() {
    const orig = console.log.bind(console);
    console.log = (...args) => {
        const str = String(args[0] ?? '');
        // Always show the observer URL and channel so users can follow the run
        if (str.includes('Observer:') || str.includes('agentrelay.com') || str.includes('Channel: wf-')) {
            orig(...args);
            return;
        }
        // Block [broker] lines and [workflow HH:MM] timing lines
        if (/\[broker\]/.test(str) || /\[workflow\s+\d{2}:\d{2}\]/.test(str))
            return;
        orig(...args);
    };
    return () => {
        console.log = orig;
    };
}
/**
 * Creates a listr2-based renderer for TypeScript workflows.
 *
 * @example
 * ```typescript
 * import { workflow, createWorkflowRenderer } from '@agent-relay/sdk/workflows';
 *
 * const renderer = createWorkflowRenderer();
 * const [result] = await Promise.all([
 *   workflow('my-workflow').step(...).run({ onEvent: renderer.onEvent }),
 *   renderer.start(),
 * ]);
 * renderer.unmount();
 * ```
 */
export function createWorkflowRenderer() {
    const stepHandles = new Map();
    let resolveWorkflow;
    let rejectWorkflow;
    const workflowDone = new Promise((resolve, reject) => {
        resolveWorkflow = resolve;
        rejectWorkflow = reject;
    });
    // Prevent unhandled rejection if run:failed fires before the header task
    // reaches `await workflowDone`.
    workflowDone.catch(() => { });
    let setHeader = () => { };
    // eslint-disable-next-line @typescript-eslint/no-explicit-any
    let listr = null;
    const pendingAdds = [];
    async function ensureListr() {
        if (listr)
            return listr;
        const { Listr } = await import('listr2');
        listr = new Listr([
            {
                title: chalk.dim('Workflow starting...'),
                task: async (_ctx, task) => {
                    setHeader = (text) => {
                        task.title = text;
                    };
                    await workflowDone;
                },
            },
        ], {
            concurrent: true,
            renderer: process.stdout.isTTY ? 'default' : 'verbose',
            rendererOptions: {
                collapseErrors: false,
                showErrorMessage: true,
            },
        });
        for (const task of pendingAdds)
            listr.add(task);
        pendingAdds.length = 0;
        return listr;
    }
    const addTask = (task) => {
        if (listr)
            listr.add(task);
        else
            pendingAdds.push(task);
    };
    const onEvent = (event) => {
        switch (event.type) {
            case 'run:started': {
                setHeader(chalk.dim(`[workflow] run ${event.runId.slice(0, 8)}...`));
                break;
            }
            case 'step:started': {
                let resolveStep;
                let rejectStep;
                let taskRef = null;
                let skipped = false;
                const done = new Promise((resolve, reject) => {
                    resolveStep = resolve;
                    rejectStep = reject;
                });
                // Prevent unhandled rejection if the step fails before the listr
                // task function has started and reached `await done`.
                done.catch(() => { });
                stepHandles.set(event.stepName, {
                    resolve: resolveStep,
                    reject: rejectStep,
                    setOutput: (text) => {
                        if (taskRef)
                            taskRef.output = text;
                    },
                    markSkipped: () => {
                        skipped = true;
                        if (taskRef)
                            taskRef.title = chalk.dim(`${event.stepName} (skipped)`);
                    },
                });
                addTask({
                    title: chalk.white(event.stepName),
                    task: async (_ctx, task) => {
                        taskRef = task;
                        if (skipped)
                            taskRef.title = chalk.dim(`${event.stepName} (skipped)`);
                        await done;
                    },
                    rendererOptions: { persistentOutput: true },
                });
                break;
            }
            case 'step:owner-assigned': {
                const handle = stepHandles.get(event.stepName);
                if (handle) {
                    handle.setOutput(chalk.dim(`> Owner: ${event.ownerName}`) +
                        (event.specialistName ? chalk.dim(` · specialist: ${event.specialistName}`) : ''));
                }
                break;
            }
            case 'step:retrying': {
                stepHandles.get(event.stepName)?.setOutput(chalk.yellow(`Retrying (attempt ${event.attempt})`));
                break;
            }
            case 'step:nudged': {
                stepHandles.get(event.stepName)?.setOutput(chalk.dim(`> Nudge #${event.nudgeCount}`));
                break;
            }
            case 'step:force-released': {
                stepHandles.get(event.stepName)?.setOutput(chalk.yellow('> Force-released'));
                break;
            }
            case 'step:review-completed': {
                stepHandles
                    .get(event.stepName)
                    ?.setOutput(chalk.dim(`> Review: ${event.decision} by ${event.reviewerName}`));
                break;
            }
            case 'step:owner-timeout': {
                stepHandles.get(event.stepName)?.setOutput(chalk.red(`> Owner ${event.ownerName} timed out`));
                break;
            }
            case 'step:agent-report': {
                const handle = stepHandles.get(event.stepName);
                if (handle) {
                    const model = event.report.model ? `:${event.report.model}` : '';
                    handle.setOutput(chalk.dim(`> Report collected (${event.report.cli}${model})`));
                }
                break;
            }
            case 'step:completed': {
                stepHandles.get(event.stepName)?.resolve();
                break;
            }
            case 'step:skipped': {
                const handle = stepHandles.get(event.stepName);
                if (handle) {
                    handle.markSkipped();
                    handle.resolve();
                }
                else {
                    // Step was skipped without ever being started (downstream of a failure).
                    addTask({
                        title: chalk.dim(`${event.stepName} (skipped)`),
                        task: async () => { },
                        rendererOptions: { persistentOutput: true },
                    });
                }
                break;
            }
            case 'step:failed': {
                stepHandles.get(event.stepName)?.reject(new Error(event.error ?? 'Step failed'));
                break;
            }
            case 'run:completed': {
                setHeader(chalk.green('Workflow completed'));
                resolveWorkflow();
                break;
            }
            case 'run:failed': {
                setHeader(chalk.red(`Workflow failed: ${event.error ?? 'unknown error'}`));
                rejectWorkflow(new Error(event.error ?? 'Workflow failed'));
                break;
            }
            case 'run:cancelled': {
                setHeader(chalk.yellow('Workflow cancelled'));
                resolveWorkflow();
                break;
            }
            case 'broker:event':
                break;
            default: {
                const _exhaustive = event;
                void _exhaustive;
            }
        }
    };
    let restoreConsole;
    return {
        onEvent,
        start: async () => {
            restoreConsole = installOutputFilter();
            const l = await ensureListr();
            return l.run().catch(() => {
                // Step failures are already represented in the workflow result.
            });
        },
        unmount: () => {
            restoreConsole?.();
            restoreConsole = undefined;
        },
    };
}
//# sourceMappingURL=listr-renderer.js.map