/**
 * ============================================================
 *  plugins/workflow-runner/src/index.ts —— /workflow 与 /workflows 命令的胶水层
 * ------------------------------------------------------------
 *  作为 pluginRunner 与 runner 之间的桥：
 *    - runWorkflowFromCommand：解析 /workflow <name> --input k=v args 调度
 *    - runWorkflowsList：处理 /workflows 列表展示（yield "text" 事件让 UI 显示）
 *
 *  R1 红线：本文件只在 pluginRunner 命中 workflow 模式时才被 import。
 *  不污染默认 T-A-O-R 路径。
 * ============================================================
 */
import { findWorkflowByName, listWorkflows } from './loader.js';
import { runWorkflow } from './runner.js';
/**
 * 把 "/workflow <name> [pos1 pos2 ...] [--input k=v ...]" 拆成 name + 显式 inputs + 位置参数。
 * 简单解析器（不依赖第三方）：
 *   - 第一个非 flag token = name；末尾 ".yaml" / ".yml" 自动剥（便于直接用文件名）
 *   - 之后所有非 flag token 收进 positional[]，由调用方按 def.inputs 声明顺序映射
 *   - --input k=v 显式 KV，优先级高于位置参数（同名 input 不会被位置参数覆盖）
 *   - 不识别其他 flag（保守跳过其后一个 token）
 */
export function parseWorkflowArgs(rawArgs) {
    const tokens = tokenizeArgs(rawArgs);
    const inputs = {};
    const positional = [];
    let name = null;
    for (let i = 0; i < tokens.length; i++) {
        const t = tokens[i];
        if (t === '--input') {
            const kv = tokens[i + 1];
            if (!kv)
                break;
            const eq = kv.indexOf('=');
            if (eq > 0) {
                inputs[kv.slice(0, eq)] = kv.slice(eq + 1);
            }
            i++;
            continue;
        }
        if (t.startsWith('--')) {
            // 未知 flag：跳过其后一个 token（保守地认为它带值）
            i++;
            continue;
        }
        if (name === null) {
            name = t.replace(/\.ya?ml$/i, '');
        }
        else {
            positional.push(t);
        }
    }
    return { name, inputs, positional };
}
/** 简单 token 化：双引号包裹的整体当一个 token；其它按空白切。 */
function tokenizeArgs(s) {
    const out = [];
    let i = 0;
    while (i < s.length) {
        while (i < s.length && /\s/.test(s[i]))
            i++;
        if (i >= s.length)
            break;
        if (s[i] === '"' || s[i] === "'") {
            const q = s[i];
            i++;
            let acc = '';
            while (i < s.length && s[i] !== q) {
                acc += s[i++];
            }
            if (i < s.length)
                i++;
            out.push(acc);
        }
        else {
            let acc = '';
            while (i < s.length && !/\s/.test(s[i])) {
                acc += s[i++];
            }
            out.push(acc);
        }
    }
    return out;
}
/**
 * 把字符串形态的 input 值按 InputDef.type 转成合适的 JS 类型 + 校验。
 * 校验失败抛 Error，pluginRunner 会把它转成 error event。
 */
function coerceInputs(raw, defs) {
    const out = {};
    if (!defs)
        return { ...raw };
    for (const def of defs) {
        const rawVal = raw[def.name];
        if (rawVal === undefined) {
            if (def.required) {
                throw new Error(`缺少必填 input: ${def.name}`);
            }
            if (def.default !== undefined) {
                out[def.name] = def.default;
            }
            continue;
        }
        if (def.type === 'number') {
            const n = parseFloat(rawVal);
            if (Number.isNaN(n))
                throw new Error(`input ${def.name} 必须是数字，收到 "${rawVal}"`);
            out[def.name] = n;
        }
        else if (def.type === 'enum') {
            if (!def.values?.includes(rawVal)) {
                throw new Error(`input ${def.name} 必须是 [${def.values?.join(', ')}] 之一，收到 "${rawVal}"`);
            }
            out[def.name] = rawVal;
        }
        else {
            out[def.name] = rawVal;
        }
    }
    // 未声明的 input 也透传（让 yaml 里可以引用 ad-hoc 变量）
    for (const k of Object.keys(raw)) {
        if (!(k in out))
            out[k] = raw[k];
    }
    return out;
}
/** /workflow <name> [pos...] [--input k=v] —— 主入口，由 pluginRunner 调用。 */
export async function* runWorkflowFromCommand(rawArgs, opts) {
    const { name, inputs: rawInputs, positional } = parseWorkflowArgs(rawArgs);
    if (!name) {
        yield {
            type: 'error',
            error: '/workflow 用法: /workflow <name> [val ...] [--input key=val ...]\n输入 /workflows 查看可用工作流。',
        };
        return;
    }
    const def = await findWorkflowByName(name);
    if (!def) {
        yield {
            type: 'error',
            error: `未找到 workflow "${name}"。输入 /workflows 查看可用列表。`,
        };
        return;
    }
    // 位置参数严格按"第 i 个位置参数 ↔ def.inputs[i]"对应。
    // --input k=v 显式优先：已显式的 input 不被位置参数覆盖（对应的位置参数被丢弃，会 warn）。
    // 多余的位置参数（超过 def.inputs 数量）也被丢弃（会 warn）。
    const overriddenByKV = [];
    if (positional.length > 0 && def.inputs) {
        const max = Math.min(positional.length, def.inputs.length);
        for (let i = 0; i < max; i++) {
            const inputDef = def.inputs[i];
            if (inputDef.name in rawInputs) {
                // 已被 --input 占位，位置参数被丢弃
                if (rawInputs[inputDef.name] !== positional[i]) {
                    overriddenByKV.push(`${inputDef.name}=${JSON.stringify(rawInputs[inputDef.name])} (位置参数 ${JSON.stringify(positional[i])} 被忽略)`);
                }
            }
            else {
                rawInputs[inputDef.name] = positional[i];
            }
        }
    }
    // warn：超量位置参数（用户写多了，提示一下）
    if (def.inputs && positional.length > def.inputs.length) {
        const dropped = positional.slice(def.inputs.length);
        yield {
            type: 'warning',
            message: `⚠ 忽略了 ${dropped.length} 个多余位置参数: ${dropped.map((d) => JSON.stringify(d)).join(' ')}（workflow "${name}" 只声明了 ${def.inputs.length} 个 input）`,
        };
    }
    // warn：位置参数被 --input 覆盖
    if (overriddenByKV.length > 0) {
        yield {
            type: 'warning',
            message: `⚠ --input 优先级高于位置参数，以下位置参数被覆盖：${overriddenByKV.join('; ')}`,
        };
    }
    let coerced;
    try {
        coerced = coerceInputs(rawInputs, def.inputs);
    }
    catch (e) {
        yield { type: 'error', error: e.message };
        return;
    }
    const ctx = {
        provider: opts.provider,
        signal: opts.signal,
        history: opts.history,
        cwd: opts.cwd ?? process.cwd(),
        tools: opts.tools ?? [],
    };
    // 透传 runWorkflow 的事件，并观察是否出现终结事件（error / interrupted = 失败或中断，
    // 与 gateway broker 的 TERMINAL 判定同一套逻辑）。成功（没见到终结事件）则在末尾补一条
    // 通用 `turn_done`，让对外流式协议以通用终结事件收口——否则成功 workflow 只发私有
    // workflow_done：gateway TERMINAL 不认它 → 子进程关闭时误补 interrupted；
    // `-p --output-format json` 因无 turn_done → stop_reason 兜底 error → ok:false（成功却报失败）。
    // 与 runWorkflowsList 末尾的 turn_done 是同一约定；失败/中断不补（error/interrupted 已是终结）。
    let sawTerminal = false;
    for await (const ev of runWorkflow(def, coerced, ctx)) {
        if (ev.type === 'error' || ev.type === 'interrupted')
            sawTerminal = true;
        yield ev;
    }
    if (!sawTerminal)
        yield { type: 'turn_done' };
}
/** /workflows —— 列出可用 workflow。yield 一条 text 事件让 UI 显示。 */
export async function* runWorkflowsList() {
    const list = await listWorkflows();
    if (list.length === 0) {
        yield {
            type: 'text',
            delta: '当前没有可用工作流。在 cwd/workflows/ 下放 *.yaml 即可（参考 workflows/schema.json 或 docs/workflow-design.md）。\n',
        };
        yield { type: 'turn_done' };
        return;
    }
    const lines = ['可用工作流：'];
    for (const w of list) {
        lines.push(`  /workflow ${w.name}  —  ${w.description.split('\n')[0].trim()}`);
    }
    lines.push('');
    lines.push('运行示例:');
    lines.push('  /workflow <name> <val>            位置参数（按 inputs 声明顺序，第一个 input 直接写值）');
    lines.push('  /workflow <name>.yaml <val>       .yaml 后缀可省');
    lines.push('  /workflow <name> --input k=v      显式 KV（多 input 或想跳过中间 input 时用）');
    yield { type: 'text', delta: lines.join('\n') + '\n' };
    yield { type: 'turn_done' };
}
