/**
 * oclif typed flags → `RunConfig` 装配。
 *
 * 主体 `parseRunConfig` 是「CLI > eval.yaml > 硬编码 default」精度链的统一落点 ——
 * 把 oclif strict 模式接受过的 flag values + 可选 eval.yaml 合并成 eval 子命令运行
 * 所需的完整 `RunConfig`,后续 `executeEvaluationPipeline` 与 doctor / batch 都从这
 * 里读字段。
 *
 * 3 个 sub-routine 拆到同级 parse-run-config/ 目录,主文件保留它们的 re-export
 * 让外部 import surface(`parseJudgeModelsArg` / `parseJudgeModelsArgOrExit`)
 * 不动:
 *   - judge-models.ts: --judge-models 字符串解析
 *   - samples-discovery.ts: 未传 --samples 时的路径发现
 *   - variant-resolution.ts: --control / --treatment / eval.yaml.variants 三态合并
 *
 * 主体 parseRunConfig 自身保留约 100 行的 field-default fanout —— 每个字段一行
 * `cli ?? evalConfig ?? default`,刻意不再细拆,否则只是把一长串 ?? 散到多文件,
 * 反而失去「精度链一目了然」的可读性。
 */
import { resolve, join } from 'node:path';
import { homedir } from 'node:os';
import { loadEvalConfig } from '../../inputs/eval-config.js';
import { DEFAULT_MODEL } from '../../executors/shared.js';
import { parseJudgeModelsArgOrExit } from './parse-run-config/judge-models.js';
import { discoverSamplesPath } from './parse-run-config/samples-discovery.js';
import { resolveVariantSpecs } from './parse-run-config/variant-resolution.js';
export { parseJudgeModelsArg, parseJudgeModelsArgOrExit } from './parse-run-config/judge-models.js';
export const DEFAULT_REPORTS_DIR = join(homedir(), '.oh-my-knowledge', 'reports');
/**
 * 接 typed flags(来自 oclif Command.parse() 输出)。oclif strict 模式已经在
 * 上游对未知 flag 拦截 exit 2,这里不再 parseArgs。eval-runner 等业务 caller
 * 把 oclif flags 当 values 喂进来。
 */
export function parseRunConfig(values) {
    if (values.variants !== undefined) {
        throw new Error(`--variants 已在 v0.16 废除，请改用 --control <expr> 与 --treatment <v1,v2,...>\n`
            + `  迁移示例：--variants baseline,my-skill  →  --control baseline --treatment my-skill\n`
            + `  复杂场景可用 --config eval.yaml（参见 docs/specs/terminology-spec.md）`);
    }
    // 1) Load --config (if provided). All subsequent fields fall back to it when CLI is silent.
    const evalConfig = values.config
        ? loadEvalConfig(values.config)
        : null;
    const skillDir = resolve(values['skill-dir'] ?? 'skills');
    // 2) Resolve samples path: CLI > config > <skillDir>/<treatment>/.omk/ discovery > cwd default.
    // The .omk/ discovery only fires when exactly one --treatment is given, so omk knows
    // which skill's bundled samples to use. The dir form (loadSamples handles both file + dir)
    // means a skill can split samples across multiple files (workflow.json / platform.json / ...).
    const cliSamples = values.samples;
    let samplesFile;
    if (cliSamples) {
        samplesFile = cliSamples;
    }
    else if (evalConfig?.samples) {
        samplesFile = evalConfig.samples; // already resolved against config file dir
    }
    else {
        samplesFile = discoverSamplesPath(values, skillDir);
    }
    // 3) Resolve variantSpecs: CLI > config > batch > error。dedup 在 helper 里。
    const variantSpecs = resolveVariantSpecs(values, evalConfig, skillDir);
    // 4) Apply CLI > config > hard-coded default for all other fields.
    const executorName = values.executor ?? evalConfig?.executor ?? 'claude';
    // model fallback 链:CLI > eval.yaml > DEFAULT_MODEL(opus 4.7)。
    // 改 default 时同步 src/cli/commands/eval.ts 里 --model flag description 的默认值文案。
    const model = values.model ?? evalConfig?.model ?? DEFAULT_MODEL;
    // judgeModels: unified judge config. Parse --judge-models (CLI) or evalConfig.judgeModels (yaml).
    // 1 entry = single judge, ≥ 2 entries = ensemble. Format `executor:model[,executor:model]`.
    // 出口 RunConfig.judgeModels 保证非空 (default `[{executor, model: 'haiku'}]`)。
    const cliJudgesRaw = values['judge-models'];
    const parsedJudges = cliJudgesRaw !== undefined ? parseJudgeModelsArgOrExit(cliJudgesRaw) : undefined;
    const judgeModels = parsedJudges
        ?? evalConfig?.judgeModels
        ?? [{ executor: executorName, model: 'haiku' }];
    const outputDir = resolve(values['output-dir'] ?? DEFAULT_REPORTS_DIR);
    const concurrencyRaw = values.concurrency !== undefined
        ? Number(values.concurrency)
        : evalConfig?.concurrency ?? 1;
    const concurrency = Math.max(1, Number(concurrencyRaw) || 1);
    const timeoutSec = values.timeout !== undefined
        ? Number(values.timeout)
        : evalConfig?.timeoutMs
            ? evalConfig.timeoutMs / 1000
            : 120;
    const timeoutMs = Math.max(1, Number(timeoutSec) || 120) * 1000;
    const noJudge = values['no-judge'] ?? evalConfig?.noJudge ?? false;
    const noCache = values['no-cache'] ?? evalConfig?.noCache ?? false;
    const dryRun = values['dry-run'] ?? false;
    const skipConnectivity = values['skip-connectivity'] ?? false;
    const skipDoctor = values['skip-doctor'] ?? evalConfig?.skipDoctor ?? false;
    const mcpConfig = values['mcp-config'] ?? evalConfig?.mcpConfig;
    const verbose = values.verbose ?? false;
    const retry = Math.max(0, Number(values.retry ?? 0) || 0);
    const resume = values.resume;
    const blind = values.blind ?? evalConfig?.blind ?? false;
    const layeredStats = values['layered-stats'] ?? false;
    // strict-baseline default true. Reconcile both flag forms with eval.yaml fallback.
    // Priority: --no-strict-baseline > --strict-baseline > eval.yaml strictBaseline > true。
    const noStrictFlag = values['no-strict-baseline'];
    const strictFlag = values['strict-baseline'];
    const strictBaseline = noStrictFlag === true
        ? false
        : strictFlag === true
            ? true
            : (evalConfig?.strictBaseline ?? true);
    // extract eval.yaml variant.allowedSkills overrides (per-variant). Always
    // wins over strictBaseline default. Empty object when no eval.yaml or no overrides.
    const variantAllowedSkills = {};
    if (evalConfig?.variants) {
        for (const v of evalConfig.variants) {
            if (v.allowedSkills !== undefined) {
                variantAllowedSkills[v.name] = v.allowedSkills;
            }
        }
    }
    // effort:CLI > evalConfig > 默认 'low'。校验合法值,不合法就 throw(early surface error)。
    const VALID_EFFORTS = new Set(['low', 'medium', 'high', 'xhigh', 'max']);
    const effortRaw = values.effort ?? evalConfig?.effort ?? 'low';
    if (!VALID_EFFORTS.has(effortRaw)) {
        throw new Error(`--effort must be one of low/medium/high/xhigh/max (got "${effortRaw}")`);
    }
    const effort = effortRaw;
    const noDiagnostic = values['no-diagnostic'] ?? evalConfig?.noDiagnostic ?? false;
    return {
        values: values,
        config: {
            samplesPath: resolve(samplesFile),
            skillDir,
            variantSpecs,
            model,
            outputDir,
            noJudge,
            noCache,
            dryRun,
            concurrency,
            timeoutMs,
            executorName,
            skipConnectivity,
            skipDoctor,
            lang: undefined, // CLI 入口在 commands/eval-runner.ts 里注入
            mcpConfig,
            verbose,
            retry,
            resume,
            blind,
            layeredStats,
            budget: evalConfig?.budget,
            strictBaseline,
            judgeModels,
            effort,
            noDiagnostic,
            ...(Object.keys(variantAllowedSkills).length > 0 && { variantAllowedSkills }),
        },
        evalConfig,
    };
}
//# sourceMappingURL=parse-run-config.js.map