/**
 * Repo Supervisor — skeleton behaviour contracts.
 *
 * Purpose: verify that the per-repo supervisor boundary keeps lifecycle, guard,
 * and dispatch-placeholder state isolated before any real dispatch is wired.
 *
 * Consumer: M053 supervisor implementation and future `sf supervisor` CLI work.
 */
import assert from "node:assert/strict";
import { mkdtempSync, rmSync } from "node:fs";
import { tmpdir } from "node:os";
import { join } from "node:path";
import { afterEach, describe, it } from "vitest";
import { RepoSupervisor } from "../supervisor/repo-supervisor.js";

const tempRoots = [];

function makeRepo(name) {
	const root = mkdtempSync(join(tmpdir(), `sf-${name}-`));
	tempRoots.push(root);
	return root;
}

function makeLogger() {
	const lines = [];
	return {
		lines,
		info(message) {
			lines.push(message);
		},
	};
}

afterEach(() => {
	while (tempRoots.length > 0) {
		const root = tempRoots.pop();
		rmSync(root, { force: true, recursive: true });
	}
});

describe("RepoSupervisor", () => {
	it("isolates_state_between_two_repo_supervisors", async () => {
		const loggerA = makeLogger();
		const loggerB = makeLogger();
		const repoA = makeRepo("repo-a");
		const repoB = makeRepo("repo-b");
		const supervisorA = new RepoSupervisor({
			repoPath: repoA,
			supervisorId: "supervisor-a",
			options: {
				dispatchQueue: [{ id: "A-001" }],
				logger: loggerA,
			},
		});
		const supervisorB = new RepoSupervisor({
			repoPath: repoB,
			supervisorId: "supervisor-b",
			options: {
				dispatchQueue: [{ id: "B-001" }],
				logger: loggerB,
			},
		});

		const resultA = await supervisorA.tick();

		assert.equal(resultA.status, "ok");
		assert.equal(resultA.unitId, "A-001");
		assert.equal(supervisorA.getStatus().ticksTotal, 1);
		assert.equal(supervisorB.getStatus().ticksTotal, 0);
		assert.equal(supervisorA.getStatus().errorsTotal, 0);
		assert.equal(supervisorB.getStatus().errorsTotal, 0);
		assert.match(loggerA.lines[0], /would dispatch unit A-001/);
		assert.equal(loggerB.lines.length, 0);

		const resultB = await supervisorB.tick();

		assert.equal(resultB.status, "ok");
		assert.equal(resultB.unitId, "B-001");
		assert.equal(supervisorA.getStatus().ticksTotal, 1);
		assert.equal(supervisorB.getStatus().ticksTotal, 1);
		assert.notEqual(supervisorA.dbPath, supervisorB.dbPath);
	});

	it("tick_returns_result_object_and_catches_simulated_errors", async () => {
		const supervisor = new RepoSupervisor({
			repoPath: makeRepo("error-repo"),
			supervisorId: "supervisor-error",
			options: {
				logger: makeLogger(),
				shouldFailTick() {
					throw new Error("simulated failure");
				},
			},
		});

		const result = await supervisor.tick();

		assert.deepEqual(result, {
			dispatched: false,
			error: "simulated failure",
			errors: ["simulated failure"],
			status: "error",
		});
		assert.equal(supervisor.getStatus().errorsTotal, 1);
		assert.equal(supervisor.getStatus().lastTickResult.status, "error");
	});

	it("pause_resume_toggles_tick_dispatch_behaviour", async () => {
		const logger = makeLogger();
		const supervisor = new RepoSupervisor({
			repoPath: makeRepo("paused-repo"),
			supervisorId: "supervisor-paused",
			options: {
				dispatchQueue: [{ id: "P-001" }],
				logger,
			},
		});

		await supervisor.pause();
		const pausedResult = await supervisor.tick();

		assert.deepEqual(pausedResult, {
			dispatched: false,
			errors: [],
			status: "paused",
		});
		assert.equal(supervisor.getStatus().paused, true);
		assert.equal(logger.lines.length, 0);

		await supervisor.resume();
		const resumedResult = await supervisor.tick();

		assert.equal(supervisor.getStatus().paused, false);
		assert.equal(resumedResult.status, "ok");
		assert.equal(resumedResult.unitId, "P-001");
		assert.match(logger.lines[0], /would dispatch unit P-001/);
	});

	it("getStatus_shape_matches_design_contract", async () => {
		const supervisor = new RepoSupervisor({
			repoPath: makeRepo("status-repo"),
			supervisorId: "supervisor-status",
			options: {
				dispatchQueue: [{ id: "S-001" }],
				logger: makeLogger(),
				repoId: "status-repo",
			},
		});

		assert.deepEqual(Object.keys(supervisor.getStatus()), [
			"repoId",
			"lastTickAt",
			"lastTickResult",
			"paused",
			"ticksTotal",
			"errorsTotal",
		]);

		await supervisor.tick();
		const status = supervisor.getStatus();

		assert.equal(status.repoId, "status-repo");
		assert.equal(typeof status.lastTickAt, "string");
		assert.equal(status.lastTickResult.status, "ok");
		assert.equal(status.paused, false);
		assert.equal(status.ticksTotal, 1);
		assert.equal(status.errorsTotal, 0);
	});
});
