import assert from "node:assert/strict";
import { mkdirSync, mkdtempSync, rmSync } from "node:fs";
import { tmpdir } from "node:os";
import { join } from "node:path";
import { afterEach, test } from "vitest";
import {
	closeDatabase,
	getGateRunStats,
	getLlmTaskOutcomeStats,
	getLlmTaskOutcomesByModel,
	getLlmTaskOutcomesByUnit,
	getRecentLlmTaskOutcomes,
	insertLlmTaskOutcome,
	openDatabase,
} from "../sf-db.js";
import { appendTraceEvent } from "../uok/trace-writer.js";

const tmpRoots = [];

afterEach(() => {
	closeDatabase();
	for (const dir of tmpRoots.splice(0)) {
		rmSync(dir, { recursive: true, force: true });
	}
});

function makeProject() {
	const root = mkdtempSync(join(tmpdir(), "sf-outcome-ledger-"));
	mkdirSync(join(root, ".sf"), { recursive: true });
	tmpRoots.push(root);
	return root;
}

test("llm_task_outcome_queries_when_records_exist_return_ordered_contracts", () => {
	openDatabase(":memory:");
	const now = Date.now();

	insertLlmTaskOutcome({
		modelId: "provider/expensive",
		provider: "provider",
		unitType: "execute-task",
		unitId: "M001/S01/T01",
		succeeded: false,
		retries: 1,
		escalated: true,
		verification_passed: false,
		blocker_discovered: true,
		duration_ms: 1200,
		tokens_total: 4000,
		cost_usd: 0.42,
		recorded_at: now - 1_000,
	});
	insertLlmTaskOutcome({
		modelId: "provider/cheap",
		provider: "provider",
		unitType: "execute-task",
		unitId: "M001/S01/T01",
		succeeded: true,
		retries: 0,
		escalated: false,
		verification_passed: true,
		blocker_discovered: false,
		duration_ms: 800,
		tokens_total: 1000,
		cost_usd: 0.03,
		recorded_at: now,
	});

	const byUnit = getLlmTaskOutcomesByUnit("execute-task", "M001/S01/T01", 10);
	assert.equal(byUnit.length, 2);
	assert.equal(byUnit[0].model_id, "provider/cheap");
	assert.equal(byUnit[1].model_id, "provider/expensive");

	const byModel = getLlmTaskOutcomesByModel("provider/expensive", 10);
	assert.equal(byModel.length, 1);
	assert.equal(byModel[0].cost_usd, 0.42);

	const recent = getRecentLlmTaskOutcomes(1, 10);
	assert.equal(recent.length, 2);

	const stats = getLlmTaskOutcomeStats("provider/expensive", 1);
	assert.equal(stats.total, 1);
	assert.equal(stats.failed, 1);
	assert.equal(stats.succeeded, 0);
	assert.equal(stats.totalCostUsd, 0.42);
	assert.equal(stats.totalTokens, 4000);
	assert.equal(stats.avgDurationMs, 1200);
});

test("gate_run_stats_when_gate_runs_exist_aggregates_by_gate_and_window", () => {
	const basePath = makeProject();
	openDatabase(join(basePath, ".sf", "sf.db"));
	const now = new Date().toISOString();

	for (const outcome of ["pass", "fail", "retry", "manual-attention"]) {
		appendTraceEvent(basePath, `trace-${outcome}`, {
			type: "gate_run",
			traceId: `trace-${outcome}`,
			turnId: `turn-${outcome}`,
			gateId: "cost-guard",
			gateType: "policy",
			outcome,
			failureClass: outcome === "pass" ? "none" : "policy",
			rationale: outcome,
			findings: "",
			attempt: 1,
			maxAttempts: 1,
			retryable: outcome === "retry",
			evaluatedAt: now,
		});
	}
	appendTraceEvent(basePath, "trace-other", {
		type: "gate_run",
		traceId: "trace-other",
		turnId: "turn-other",
		gateId: "other-gate",
		gateType: "policy",
		outcome: "fail",
		failureClass: "policy",
		rationale: "other",
		findings: "",
		attempt: 1,
		maxAttempts: 1,
		retryable: false,
		evaluatedAt: now,
	});

	const stats = getGateRunStats("cost-guard", 1);
	assert.equal(stats.total, 4);
	assert.equal(stats.pass, 1);
	assert.equal(stats.fail, 1);
	assert.equal(stats.retry, 1);
	assert.equal(stats.manualAttention, 1);
	assert.ok(
		stats.lastEvaluatedAt === now || stats.lastEvaluatedAt != null,
		`expected lastEvaluatedAt to be set`,
	);
});
