import { init_providers, resolveModel } from "../../providers/index.js";
import { JUDGE_REASON_EN } from "../../i18n/goals-bundle.js";
import { goalUiLocaleOrFallback, judgeReasonText, judgeResponseLanguageNote, localizeJudgeReasonText } from "./goal-locale.js";
import { DEFAULT_JUDGE_TIMEOUT_MS, extractAssistantText, getAssistantMessageErrorReason, resolveGoalJudgeApiKey, stripCodeFences, truncateGoalText } from "./judge.js";
import { complete } from "@earendil-works/pi-ai";
//#region src/agent/goals/checklist-judge.ts
init_providers();
const GOAL_TRUNC = 4e3;
const RESPONSE_SNIPPET = 4e3;
const HISTORY_TRUNC = 24e3;
const DECOMPOSE_SYSTEM = "You break a user goal into an EXTREMELY detailed checklist of concrete, verifiable completion criteria. Bias toward more items. Each item is one factual statement about finished work.\n\nReply ONLY with a single JSON object on one line:\n{\"items\":[{\"text\":\"...\"},...]}\nUse at least 3 items when the goal warrants it.";
const DECOMPOSE_USER = "Goal:\n{goal}\n\nReturn JSON only.";
const EVALUATE_CHECKLIST_SYSTEM = "You evaluate an autonomous agent's progress on a goal with a numbered checklist.\nFor each pending item, decide if evidence in the agent snippet and/or history excerpt shows it is satisfied.\nFlip pending→completed only with clear evidence; pending→impossible only if truly unachievable here.\nDo not regress completed/impossible items — omit them from updates.\n\nReply ONLY with one JSON object on one line. Field \"updates\" is an array of {\"index\": <1-based number>, \"status\": \"completed\" or \"impossible\", \"evidence\": \"short citation\"}. Field \"new_items\" is an array of {\"text\": \"...\"} for missing criteria. Field \"reason\" is one sentence. updates and new_items may be empty arrays.";
function extractJsonObject(raw) {
	if (!raw?.trim()) return null;
	const text = stripCodeFences(raw);
	try {
		const data = JSON.parse(text);
		return data && typeof data === "object" && !Array.isArray(data) ? data : null;
	} catch {
		const start = text.indexOf("{");
		const end = text.lastIndexOf("}");
		if (start === -1 || end <= start) return null;
		try {
			const data = JSON.parse(text.slice(start, end + 1));
			return data && typeof data === "object" && !Array.isArray(data) ? data : null;
		} catch {
			return null;
		}
	}
}
async function decomposeGoalChecklist(opts) {
	const locale = goalUiLocaleOrFallback(opts.uiLocale);
	const goal = opts.goal.trim();
	if (!goal) return {
		items: [],
		parseFailed: false,
		errorReason: judgeReasonText("empty_goal", locale)
	};
	let model;
	try {
		model = resolveModel(opts.judgeModelRef);
	} catch {
		return {
			items: [],
			parseFailed: false,
			errorReason: judgeReasonText("judge_model_not_configured", locale)
		};
	}
	const timeoutMs = typeof opts.judgeTimeoutMs === "number" && Number.isFinite(opts.judgeTimeoutMs) ? Math.max(5e3, Math.min(12e4, Math.floor(opts.judgeTimeoutMs))) : DEFAULT_JUDGE_TIMEOUT_MS;
	const controller = new AbortController();
	const timer = setTimeout(() => controller.abort(), timeoutMs);
	const merged = opts.signal ? AbortSignal.any([opts.signal, controller.signal]) : controller.signal;
	try {
		const user = {
			role: "user",
			content: `${DECOMPOSE_SYSTEM}\n\n${DECOMPOSE_USER.replace("{goal}", truncateGoalText(goal, GOAL_TRUNC))}` + judgeResponseLanguageNote(locale),
			timestamp: Date.now()
		};
		const apiKey = await resolveGoalJudgeApiKey(model);
		const result = await complete(model, { messages: [user] }, {
			apiKey,
			maxTokens: 2e3,
			temperature: 0,
			signal: merged
		});
		if (getAssistantMessageErrorReason(result)) return {
			items: [],
			parseFailed: false,
			errorReason: judgeReasonText("decompose_call_failed", locale)
		};
		const data = extractJsonObject(extractAssistantText(result.content));
		if (!data) return {
			items: [],
			parseFailed: true,
			errorReason: judgeReasonText("decompose_reply_not_json", locale)
		};
		const rawItems = data.items ?? data.checklist;
		if (!Array.isArray(rawItems)) return {
			items: [],
			parseFailed: true,
			errorReason: judgeReasonText("missing_items_array", locale)
		};
		const items = [];
		for (const row of rawItems) if (typeof row === "string") {
			const t = row.trim();
			if (t) items.push({ text: t });
		} else if (row && typeof row === "object" && !Array.isArray(row)) {
			const t = String(row.text ?? "").trim();
			if (t) items.push({ text: t });
		}
		if (!items.length) return {
			items: [],
			parseFailed: true,
			errorReason: judgeReasonText("empty_checklist", locale)
		};
		return {
			items,
			parseFailed: false
		};
	} catch {
		return {
			items: [],
			parseFailed: false,
			errorReason: judgeReasonText("decompose_call_failed", locale)
		};
	} finally {
		clearTimeout(timer);
	}
}
async function evaluateGoalChecklistJudge(opts) {
	const locale = goalUiLocaleOrFallback(opts.uiLocale);
	let model;
	try {
		model = resolveModel(opts.judgeModelRef);
	} catch {
		return {
			parsed: {
				updates: [],
				newItems: [],
				reason: judgeReasonText("judge_model_not_configured", locale)
			},
			parseFailed: false
		};
	}
	const userBody = `Goal:\n${truncateGoalText(opts.goal, 2e3)}\n\nCurrent checklist (1-based indices):\n${opts.numberedChecklist}\n\nAgent's most recent response (snippet):\n${truncateGoalText(opts.lastResponse, RESPONSE_SNIPPET)}\n\nRecent conversation excerpt (JSON or text, may be truncated):\n${truncateGoalText(opts.historyExcerpt, HISTORY_TRUNC) || "(none)"}`;
	const timeoutMs = typeof opts.judgeTimeoutMs === "number" && Number.isFinite(opts.judgeTimeoutMs) ? Math.max(5e3, Math.min(12e4, Math.floor(opts.judgeTimeoutMs))) : DEFAULT_JUDGE_TIMEOUT_MS;
	const controller = new AbortController();
	const timer = setTimeout(() => controller.abort(), timeoutMs);
	const merged = opts.signal ? AbortSignal.any([opts.signal, controller.signal]) : controller.signal;
	try {
		const user = {
			role: "user",
			content: `${EVALUATE_CHECKLIST_SYSTEM}\n\n${userBody}` + judgeResponseLanguageNote(locale),
			timestamp: Date.now()
		};
		const apiKey = await resolveGoalJudgeApiKey(model);
		const result = await complete(model, { messages: [user] }, {
			apiKey,
			maxTokens: 1500,
			temperature: 0,
			signal: merged
		});
		if (getAssistantMessageErrorReason(result)) return {
			parsed: {
				updates: [],
				newItems: [],
				reason: judgeReasonText("judge_call_failed", locale)
			},
			parseFailed: false
		};
		const data = extractJsonObject(extractAssistantText(result.content));
		if (!data) return {
			parsed: {
				updates: [],
				newItems: [],
				reason: judgeReasonText("judge_reply_not_json", locale)
			},
			parseFailed: true
		};
		const updatesRaw = data.updates;
		const newRaw = data.new_items ?? data.newItems;
		const reason = localizeJudgeReasonText((typeof data.reason === "string" ? data.reason.trim() : "") || JUDGE_REASON_EN.no_reason_provided, locale);
		const updates = [];
		if (Array.isArray(updatesRaw)) for (const u of updatesRaw) {
			if (!u || typeof u !== "object" || Array.isArray(u)) continue;
			const rec = u;
			const idx1 = Number(rec.index);
			if (!Number.isFinite(idx1)) continue;
			const idx0 = Math.floor(idx1) - 1;
			const st = String(rec.status ?? "").trim().toLowerCase();
			if (st !== "completed" && st !== "impossible") continue;
			const evidence = typeof rec.evidence === "string" ? rec.evidence : null;
			updates.push({
				index: idx0,
				status: st,
				evidence
			});
		}
		const newItems = [];
		if (Array.isArray(newRaw)) {
			for (const row of newRaw) if (typeof row === "string") {
				const t = row.trim();
				if (t) newItems.push({ text: t });
			} else if (row && typeof row === "object" && !Array.isArray(row)) {
				const t = String(row.text ?? "").trim();
				if (t) newItems.push({ text: t });
			}
		}
		return {
			parsed: {
				updates,
				newItems,
				reason: reason || judgeReasonText("no_reason_provided", locale)
			},
			parseFailed: false
		};
	} catch {
		return {
			parsed: {
				updates: [],
				newItems: [],
				reason: judgeReasonText("judge_call_failed", locale)
			},
			parseFailed: false
		};
	} finally {
		clearTimeout(timer);
	}
}
//#endregion
export { decomposeGoalChecklist, evaluateGoalChecklistJudge };

//# sourceMappingURL=checklist-judge.js.map