/**
 * ============================================================
 *  voteHelpers.ts —— vote 节点的 voter 调用辅助
 * ------------------------------------------------------------
 *  对抗式审查：N 个 voter 跑同一 prompt + refuted: boolean 反向投票。
 *  策略上倾向 refuted=true（找漏洞），所以阈值算的是"驳回票数"。
 *  详见 ADR-05。
 * ============================================================
 */
import { execLlmStep } from './stepExecutors/llm.js';
/** voter 输出 schema（内置，不接受用户自定义） */
export const VERDICT_SCHEMA = {
    type: 'object',
    additionalProperties: false,
    required: ['refuted', 'reasoning'],
    properties: {
        refuted: { type: 'boolean', description: '是否驳回此 finding' },
        reasoning: { type: 'string', description: '核心论证（≤200 字）' },
    },
};
export function buildVoterPrompt(rules, item) {
    const itemStr = typeof item === 'string' ? item : JSON.stringify(item, null, 2);
    return `你是一个对抗式审查者，你的核心任务是积极找出下面这个 finding 的逻辑漏洞、证据不足、或论证它是伪命题。

**被审查的 finding**：
${itemStr}

**审查规则**：
${rules}

**重要心理学定位**：你应当倾向于 refuted=true。只在你**找不到任何合理的反驳角度**且这个 finding 论证扎实时，才输出 refuted=false。"老好人式赞同"是错误的。

输出 JSON，refuted=true 表示你成功找到反驳理由，false 表示你认为它确实成立。reasoning 字段写明你的核心论证（≤200 字）。`;
}
/** 单个 voter 调用。包装 execLlmStep，强制 VERDICT_SCHEMA + 对抗式 prompt。
 *  解析失败时默认 refuted=false（保守保留 item，避免 LLM 失误导致正确 finding 被误删）。 */
export async function runVoter(rules, item, vars, ctx) {
    const virtualStep = {
        id: 'vote_voter', // 仅 ID，不影响事件流
        llm: buildVoterPrompt(rules, item),
        output_schema: VERDICT_SCHEMA,
    };
    try {
        // execLlmStep 现为 AsyncGenerator：手动 drain 取 return 值。voter 必须静默——
        // N 个 voter 在 Promise.all 里并行，若把它们的 text 事件向上转发会在 UI 互相打架。
        const gen = execLlmStep(virtualStep, vars, ctx);
        let res = await gen.next();
        while (!res.done)
            res = await gen.next();
        const result = res.value;
        const verdict = extractVerdict(result.raw);
        if (verdict)
            return verdict;
        return { refuted: false, reasoning: '(voter 输出格式异常，保守保留)' };
    }
    catch (e) {
        return {
            refuted: false,
            reasoning: `(voter 调用失败: ${e.message}，保守保留)`,
        };
    }
}
/**
 * 从 execLlmStep 的 raw 提取 { refuted, reasoning }：
 *   1. raw 直接含字段（B 改完 llm.ts 串通 output_schema 后的形态）
 *   2. raw.text / raw.result 是 JSON 字符串，parse 后含字段（B 未改完时的 fallback）
 *   3. raw.text / raw.result 含 ```json fenced block，剥壳后含字段
 *   4. 其余 → null
 */
function extractVerdict(raw) {
    if (typeof raw.refuted === 'boolean' &&
        typeof raw.reasoning === 'string') {
        return { refuted: raw.refuted, reasoning: raw.reasoning };
    }
    for (const k of ['text', 'result']) {
        const v = raw[k];
        if (typeof v !== 'string' || v.length === 0)
            continue;
        const parsed = tryParseJsonLike(v);
        if (parsed &&
            typeof parsed === 'object' &&
            typeof parsed.refuted === 'boolean' &&
            typeof parsed.reasoning === 'string') {
            return {
                refuted: parsed.refuted,
                reasoning: parsed.reasoning,
            };
        }
    }
    return null;
}
function tryParseJsonLike(s) {
    // 1. 直接 JSON
    try {
        return JSON.parse(s);
    }
    catch {
        // 2. 剥 ```json ... ``` 围栏
        const fence = s.match(/```(?:json)?\s*([\s\S]*?)```/);
        if (fence) {
            try {
                return JSON.parse(fence[1].trim());
            }
            catch {
                return null;
            }
        }
        return null;
    }
}
