/**
 * uok-task-state.test.mjs — Task state machine contract tests.
 *
 * Purpose: verify gate-outcome-to-task-state mapping, unit-runtime bridging,
 * state transition validity, and aggregate computation.
 */

import assert from "node:assert/strict";
import { test } from "vitest";
import {
	aggregateTaskStates,
	buildTaskRecord,
	canTransitionTaskState,
	gateOutcomesToTaskState,
	normalizeTaskState,
	TASK_STATES,
	TASK_TERMINAL_STATES,
	unitRuntimeToTaskState,
} from "../uok/task-state.js";

// ─── Constants ─────────────────────────────────────────────────────────────

test("TASK_STATES_has_all_orch_states", () => {
	assert.deepEqual(TASK_STATES, [
		"todo",
		"running",
		"verifying",
		"reviewing",
		"done",
		"blocked",
		"paused",
		"retrying",
		"failed",
		"cancelled",
	]);
});

test("TASK_TERMINAL_STATES_includes_done_failed_cancelled", () => {
	assert.equal(TASK_TERMINAL_STATES.has("done"), true);
	assert.equal(TASK_TERMINAL_STATES.has("failed"), true);
	assert.equal(TASK_TERMINAL_STATES.has("cancelled"), true);
	assert.equal(TASK_TERMINAL_STATES.has("todo"), false);
	assert.equal(TASK_TERMINAL_STATES.has("running"), false);
	assert.equal(TASK_TERMINAL_STATES.has("blocked"), false);
});

// ─── Gate outcomes → task state ────────────────────────────────────────────

test("gateOutcomesToTaskState_empty_returns_todo", () => {
	assert.equal(gateOutcomesToTaskState([]), "todo");
	assert.equal(gateOutcomesToTaskState(null), "todo");
	assert.equal(gateOutcomesToTaskState(undefined), "todo");
});

test("gateOutcomesToTaskState_all_pass_returns_done", () => {
	const results = [
		{ outcome: "pass", gateId: "security" },
		{ outcome: "pass", gateId: "cost" },
	];
	assert.equal(gateOutcomesToTaskState(results), "done");
});

test("gateOutcomesToTaskState_any_fail_returns_failed", () => {
	const results = [
		{ outcome: "pass", gateId: "security" },
		{ outcome: "fail", gateId: "cost", failureClass: "policy" },
	];
	assert.equal(gateOutcomesToTaskState(results), "failed");
});

test("gateOutcomesToTaskState_any_manual_attention_returns_reviewing", () => {
	const results = [
		{ outcome: "pass", gateId: "security" },
		{ outcome: "manual-attention", gateId: "verification" },
	];
	assert.equal(gateOutcomesToTaskState(results), "reviewing");
});

test("gateOutcomesToTaskState_any_retry_returns_retrying", () => {
	const results = [
		{ outcome: "pass", gateId: "security" },
		{ outcome: "retry", gateId: "execution" },
	];
	assert.equal(gateOutcomesToTaskState(results), "retrying");
});

test("gateOutcomesToTaskState_any_blocked_returns_blocked", () => {
	const results = [
		{ outcome: "pass", gateId: "security" },
		{ outcome: "blocked", gateId: "verification" },
	];
	assert.equal(gateOutcomesToTaskState(results), "blocked");
});

test("gateOutcomesToTaskState_mixed_nonterminal_returns_running", () => {
	const results = [
		{ outcome: "pass", gateId: "security" },
		{ outcome: "pass", gateId: "cost" },
		{ outcome: "pass", gateId: "verification" },
	];
	// All pass = done, not in_progress
	assert.equal(gateOutcomesToTaskState(results), "done");
});

// ─── Unit runtime → task state ─────────────────────────────────────────────

test("unitRuntimeToTaskState_null_returns_todo", () => {
	assert.equal(unitRuntimeToTaskState(null), "todo");
});

test("unitRuntimeToTaskState_queued_returns_todo", () => {
	assert.equal(unitRuntimeToTaskState({ status: "queued" }), "todo");
});

test("unitRuntimeToTaskState_claimed_returns_todo", () => {
	assert.equal(unitRuntimeToTaskState({ status: "claimed" }), "todo");
});

test("unitRuntimeToTaskState_running_returns_running", () => {
	assert.equal(unitRuntimeToTaskState({ status: "running" }), "running");
});

test("unitRuntimeToTaskState_progress_returns_running", () => {
	assert.equal(unitRuntimeToTaskState({ status: "progress" }), "running");
});

test("unitRuntimeToTaskState_validating_returns_verifying", () => {
	assert.equal(unitRuntimeToTaskState({ status: "validating" }), "verifying");
});

test("unitRuntimeToTaskState_completed_returns_done", () => {
	assert.equal(unitRuntimeToTaskState({ status: "completed" }), "done");
});

test("unitRuntimeToTaskState_failed_returns_failed", () => {
	assert.equal(unitRuntimeToTaskState({ status: "failed" }), "failed");
});

test("unitRuntimeToTaskState_blocked_returns_blocked", () => {
	assert.equal(unitRuntimeToTaskState({ status: "blocked" }), "blocked");
});

test("unitRuntimeToTaskState_review_returns_reviewing", () => {
	assert.equal(unitRuntimeToTaskState({ status: "review" }), "reviewing");
});

test("unitRuntimeToTaskState_cancelled_returns_cancelled", () => {
	assert.equal(unitRuntimeToTaskState({ status: "cancelled" }), "cancelled");
});

test("unitRuntimeToTaskState_stale_returns_retrying", () => {
	assert.equal(unitRuntimeToTaskState({ status: "stale" }), "retrying");
});

test("unitRuntimeToTaskState_runaway_recovered_returns_retrying", () => {
	assert.equal(
		unitRuntimeToTaskState({ status: "runaway-recovered" }),
		"retrying",
	);
});

// ─── State transitions ─────────────────────────────────────────────────────

test("canTransitionTaskState_valid_returns_true", () => {
	assert.equal(canTransitionTaskState("todo", "running"), true);
	assert.equal(canTransitionTaskState("running", "verifying"), true);
	assert.equal(canTransitionTaskState("verifying", "reviewing"), true);
	assert.equal(canTransitionTaskState("running", "done"), true);
	assert.equal(canTransitionTaskState("failed", "retrying"), true);
});

test("canTransitionTaskState_invalid_returns_false", () => {
	assert.equal(canTransitionTaskState("todo", "done"), false);
	assert.equal(canTransitionTaskState("done", "running"), false);
	assert.equal(canTransitionTaskState("cancelled", "todo"), false);
});

test("canTransitionTaskState_self_loop_returns_false", () => {
	assert.equal(canTransitionTaskState("todo", "todo"), false);
});

// ─── Aggregate ─────────────────────────────────────────────────────────────

test("aggregateTaskStates_empty_returns_zero_progress", () => {
	const agg = aggregateTaskStates([]);
	assert.equal(agg.total, 0);
	assert.equal(agg.terminal, 0);
	assert.equal(agg.progress, 0);
	assert.equal(agg.isComplete, false);
});

test("aggregateTaskStates_counts_correctly", () => {
	const agg = aggregateTaskStates([
		"todo",
		"in_progress",
		"review",
		"done",
		"done",
		"failed",
	]);
	assert.equal(agg.total, 6);
	assert.equal(agg.terminal, 3); // done(2) + failed(1)
	assert.equal(agg.progress, 50);
	assert.equal(agg.isComplete, false);
	assert.equal(agg.counts.todo, 1);
	assert.equal(agg.counts.running, 1);
	assert.equal(agg.counts.reviewing, 1);
	assert.equal(agg.counts.done, 2);
	assert.equal(agg.counts.failed, 1);
});

test("normalizeTaskState_maps_source_labels_to_canonical_names", () => {
	assert.equal(normalizeTaskState("in_progress"), "running");
	assert.equal(normalizeTaskState("review"), "reviewing");
	assert.equal(normalizeTaskState("queued"), "todo");
	assert.equal(normalizeTaskState("completed"), "done");
});

test("aggregateTaskStates_complete_when_all_terminal", () => {
	const agg = aggregateTaskStates(["done", "done", "cancelled"]);
	assert.equal(agg.progress, 100);
	assert.equal(agg.isComplete, true);
});

// ─── Build task record ─────────────────────────────────────────────────────

test("buildTaskRecord_with_gate_results_uses_gate_state", () => {
	const task = buildTaskRecord({
		nodeId: "execute-task:M001:S01:T01",
		unitType: "execute-task",
		unitId: "M001.S01.T01",
		milestoneId: "M001",
		sliceId: "S01",
		taskId: "T01",
		title: "Implement auth",
		gateResults: [
			{ outcome: "pass", gateId: "security" },
			{ outcome: "pass", gateId: "cost" },
		],
	});
	assert.equal(task.state, "done");
	assert.equal(task.unitId, "M001.S01.T01");
	assert.equal(task.title, "Implement auth");
	assert.ok(task.updatedAt);
});

test("buildTaskRecord_with_runtime_uses_runtime_state", () => {
	const task = buildTaskRecord({
		nodeId: "execute-task:M001:S01:T01",
		unitType: "execute-task",
		unitId: "M001.S01.T01",
		runtimeRecord: { status: "running" },
		gateResults: [],
	});
	assert.equal(task.state, "running");
});

test("buildTaskRecord_includes_worker_id", () => {
	const task = buildTaskRecord({
		nodeId: "n1",
		unitType: "execute-task",
		unitId: "M001.S01.T01",
		gateResults: [{ outcome: "pass", gateId: "g1" }],
		workerId: "w-3",
		durationMs: 1500,
	});
	assert.equal(task.workerId, "w-3");
	assert.equal(task.durationMs, 1500);
});
