/**
 * r016-bus-deliver-verify.test.mjs — R016 MessageBus dispatch delivery verification.
 *
 * Purpose: prove `_busDispatch()` does not acknowledge delivery until the target
 * PersistentAgent inbox can read the just-sent message from SQLite.
 *
 * Consumer: CI unit-test suite (`npm run test:unit`).
 */

import { mkdtempSync, rmSync } from "node:fs";
import { tmpdir } from "node:os";
import { join } from "node:path";
import { afterEach, beforeEach, describe, expect, test, vi } from "vitest";
import { _clearSfRootCache } from "../paths.js";
import { closeDatabase } from "../sf-db.js";
import { runAgentTurn } from "../uok/agent-runner.js";
import {
	BUS_DELIVERY_VERIFY_TIMEOUT_MS,
	SwarmDispatchLayer,
} from "../uok/swarm-dispatch.js";

vi.mock("@singularity-forge/coding-agent", () => ({
	runSubagent: vi.fn(async () => ({
		ok: true,
		output: "mock runner output",
		exitCode: 0,
		stderr: "",
	})),
}));

const tmpRoots = [];

beforeEach(() => {
	vi.clearAllMocks();
});

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

function makeProject() {
	const root = mkdtempSync(join(tmpdir(), "sf-r016-bus-"));
	tmpRoots.push(root);
	return root;
}

function buildEnvelope(unitId, payload = "work payload") {
	return {
		unitId,
		unitType: "task",
		workMode: "build",
		payload,
		priority: 5,
		scope: "r016",
	};
}

describe("R016 bus dispatch delivery verification", () => {
	test("dispatch_when_agent_inbox_cache_warm_forces_refresh_before_ack", async () => {
		const root = makeProject();
		const firstLayer = new SwarmDispatchLayer(root);
		const secondLayer = new SwarmDispatchLayer(root);

		const first = await firstLayer.dispatch(
			buildEnvelope("r016-first", "first"),
		);
		const secondSwarm = await secondLayer.getOrCreateSwarm();
		const warmAgent = secondSwarm.get(first.targetAgent);

		const firstTurn = await runAgentTurn(warmAgent);
		expect(firstTurn.turnsProcessed).toBe(1);
		expect(warmAgent.receive(true)).toHaveLength(0);

		const second = await secondLayer.dispatch(
			buildEnvelope("r016-second", "second"),
		);
		expect(second.ok).toBe(true);
		expect(second.messageId).toBeTruthy();

		const secondTurn = await runAgentTurn(warmAgent);
		expect(secondTurn.turnsProcessed).toBe(1);
		expect(secondTurn.response).toBe("mock runner output");
	});

	test("busDispatch_when_refresh_does_not_expose_sent_message_returns_delivery_verification_error", async () => {
		const root = makeProject();
		const layer = new SwarmDispatchLayer(root);
		const swarm = await layer.getOrCreateSwarm();
		const target = swarm.route(buildEnvelope("r016-failure-target"));
		const refreshSpy = vi
			.spyOn(target._inbox, "refresh")
			.mockImplementation(() => {
				target._inbox._messages = [];
				target._inbox._lastRefresh = Date.now();
			});
		vi.spyOn(target, "receive").mockReturnValue([]);

		const result = await layer._busDispatch(buildEnvelope("r016-failure"));

		expect(refreshSpy).toHaveBeenCalled();
		expect(result.ok).toBe(false);
		expect(result.error).toMatchObject({
			code: "BUS_DELIVERY_VERIFICATION_FAILED",
			messageId: expect.stringMatching(/^msg-/),
		});
		expect(result.error.reason).toContain("not visible");
	});

	test("busDispatch_when_message_visible_returns_success_shape", async () => {
		const root = makeProject();
		const layer = new SwarmDispatchLayer(root);

		const result = await layer._busDispatch(buildEnvelope("r016-happy"));

		expect(BUS_DELIVERY_VERIFY_TIMEOUT_MS).toBe(2000);
		expect(result.ok).toBe(true);
		expect(result.messageId).toMatch(/^msg-/);
		expect(result.targetAgent).toBeTruthy();
		expect(result.swarmName).toBe("default");
		expect(result.envelope.unitId).toBe("r016-happy");
		expect(result.error).toBeUndefined();
	});
});
