/**
 * Gate Context Enrichment Tests (Phase 3)
 *
 * Verify that gate failures can be enriched with memory context.
 * Note: This is diagnostic only and never changes gate logic.
 */

import { beforeEach, describe, expect, it, vi } from "vitest";
import { enrichGateResultWithMemory } from "../uok/gate-runner.js";

// Mock memory store
vi.mock("../memory-store.js", () => ({
	getRelevantMemoriesRanked: vi.fn().mockResolvedValue([]),
}));

vi.mock("../sf-db.js", () => ({
	isDbAvailable: vi.fn().mockReturnValue(true),
	getGateCircuitBreaker: vi.fn(),
	getGateRunStats: vi.fn(),
	insertGateRun: vi.fn(),
	updateGateCircuitBreaker: vi.fn(),
}));

import * as memoryStore from "../memory-store.js";
import * as sfDb from "../sf-db.js";

describe("Gate Context Enrichment (Phase 3)", () => {
	beforeEach(() => {
		vi.clearAllMocks();
		sfDb.isDbAvailable.mockReturnValue(true);
	});

	describe("enrichGateResultWithMemory", () => {
		it("passes_unchanged_when_gate_passes", async () => {
			const result = { outcome: "pass", gateId: "test-gate" };

			const enriched = await enrichGateResultWithMemory(result, "test-gate");

			expect(enriched).toEqual(result);
			expect(memoryStore.getRelevantMemoriesRanked).not.toHaveBeenCalled();
		});

		it("adds_memory_context_when_gate_fails", async () => {
			memoryStore.getRelevantMemoriesRanked.mockResolvedValueOnce([
				{ id: "m1", confidence: 0.8, content: "Similar failure occurred" },
			]);

			const result = { outcome: "fail", gateId: "test-gate" };

			const enriched = await enrichGateResultWithMemory(result, "test-gate");

			expect(enriched.memoryContext).toBeDefined();
			expect(enriched.memoryContext.hasHistoricalPattern).toBe(true);
			expect(enriched.memoryContext.similarFailures).toHaveLength(1);
		});

		it("preserves_gate_result_properties", async () => {
			memoryStore.getRelevantMemoriesRanked.mockResolvedValueOnce([
				{ id: "m1", confidence: 0.8, content: "test" },
			]);

			const result = {
				outcome: "fail",
				gateId: "test",
				rationale: "original reason",
				failureClass: "policy",
			};

			const enriched = await enrichGateResultWithMemory(result, "test");

			expect(enriched.outcome).toBe("fail");
			expect(enriched.rationale).toBe("original reason");
			expect(enriched.failureClass).toBe("policy");
		});

		it("does_not_change_gate_decision", async () => {
			memoryStore.getRelevantMemoriesRanked.mockResolvedValueOnce([
				{ id: "m1", confidence: 0.9, content: "Known failure" },
			]);

			const originalOutcome = "fail";
			const result = { outcome: originalOutcome };

			const enriched = await enrichGateResultWithMemory(result, "gate");

			// Outcome should never change
			expect(enriched.outcome).toBe(originalOutcome);
		});

		it("truncates_memory_content_to_100_chars", async () => {
			const longContent = "a".repeat(200); // 200 char string
			memoryStore.getRelevantMemoriesRanked.mockResolvedValueOnce([
				{ id: "m1", confidence: 0.8, content: longContent },
			]);

			const result = { outcome: "fail", gateId: "test" };

			const enriched = await enrichGateResultWithMemory(result, "test");

			const similarFailure = enriched.memoryContext.similarFailures[0];
			expect(similarFailure.content.length).toBeLessThanOrEqual(100);
		});

		it("queries_with_gate_failure_context", async () => {
			memoryStore.getRelevantMemoriesRanked.mockResolvedValueOnce([]);

			const result = {
				outcome: "fail",
				failureClass: "timeout",
				rationale: "Command exceeded limit",
			};

			await enrichGateResultWithMemory(result, "test-gate");

			const callArgs = memoryStore.getRelevantMemoriesRanked.mock.calls[0];
			expect(callArgs[0]).toContain("uok gate failure test-gate");
			expect(callArgs[0]).toContain("timeout");
			expect(callArgs[0]).toContain("Command exceeded limit");
		});

		it("limits_similar_failures_to_2", async () => {
			memoryStore.getRelevantMemoriesRanked.mockResolvedValueOnce([]);

			const result = { outcome: "fail" };

			await enrichGateResultWithMemory(result, "test-gate");

			const callArgs = memoryStore.getRelevantMemoriesRanked.mock.calls[0];
			expect(callArgs[1]).toBe(2); // limit: 2
		});

		it("degrades_gracefully_when_db_unavailable", async () => {
			sfDb.isDbAvailable.mockReturnValue(false);

			const result = { outcome: "fail", gateId: "test" };

			const enriched = await enrichGateResultWithMemory(result, "test");

			expect(enriched).toEqual(result);
			expect(memoryStore.getRelevantMemoriesRanked).not.toHaveBeenCalled();
		});

		it("degrades_gracefully_on_memory_error", async () => {
			memoryStore.getRelevantMemoriesRanked.mockRejectedValueOnce(
				new Error("DB error"),
			);

			const result = { outcome: "fail" };

			// Should not throw
			const enriched = await enrichGateResultWithMemory(result, "test");

			// Should return original result
			expect(enriched).toEqual(result);
		});

		it("handles_missing_gate_id", async () => {
			memoryStore.getRelevantMemoriesRanked.mockResolvedValueOnce([]);

			const result = { outcome: "fail" };

			// Should not throw
			const enriched = await enrichGateResultWithMemory(result);

			expect(enriched.outcome).toBe("fail");
		});

		it("includes_confidence_in_similar_failures", async () => {
			memoryStore.getRelevantMemoriesRanked.mockResolvedValueOnce([
				{ id: "m1", confidence: 0.75, content: "test" },
				{ id: "m2", confidence: 0.92, content: "test2" },
			]);

			const result = { outcome: "fail" };

			const enriched = await enrichGateResultWithMemory(result, "test");

			expect(enriched.memoryContext.similarFailures[0].confidence).toBe(0.75);
			expect(enriched.memoryContext.similarFailures[1].confidence).toBe(0.92);
		});

		it("no_memory_context_when_no_similar_failures_found", async () => {
			memoryStore.getRelevantMemoriesRanked.mockResolvedValueOnce([]);

			const result = { outcome: "fail" };

			const enriched = await enrichGateResultWithMemory(result, "test");

			expect(enriched.memoryContext).toBeUndefined();
			expect(enriched.outcome).toBe("fail"); // Decision unchanged
		});

		it("handles_gate_result_with_many_properties", async () => {
			memoryStore.getRelevantMemoriesRanked.mockResolvedValueOnce([
				{ id: "m1", confidence: 0.8, content: "test" },
			]);

			const result = {
				gateId: "complex-gate",
				gateType: "safety",
				outcome: "fail",
				failureClass: "execution",
				rationale: "timeout",
				attempt: 2,
				maxAttempts: 3,
				retryable: true,
				customField: "custom",
			};

			const enriched = await enrichGateResultWithMemory(result, "complex-gate");

			// All properties should remain
			expect(enriched.gateId).toBe("complex-gate");
			expect(enriched.attempt).toBe(2);
			expect(enriched.customField).toBe("custom");
			// Memory context added
			expect(enriched.memoryContext).toBeDefined();
		});

		it("multiple_enrichments_independent", async () => {
			memoryStore.getRelevantMemoriesRanked.mockResolvedValueOnce([
				{ id: "m1", confidence: 0.8, content: "failure1" },
			]);

			memoryStore.getRelevantMemoriesRanked.mockResolvedValueOnce([]);

			const result1 = { outcome: "fail" };
			const result2 = { outcome: "fail" };

			const enriched1 = await enrichGateResultWithMemory(result1, "gate1");
			const enriched2 = await enrichGateResultWithMemory(result2, "gate2");

			expect(enriched1.memoryContext).toBeDefined();
			expect(enriched2.memoryContext).toBeUndefined();
		});

		it("handles_high_confidence_memories", async () => {
			memoryStore.getRelevantMemoriesRanked.mockResolvedValueOnce([
				{
					id: "critical",
					confidence: 0.99,
					content: "Critical known issue",
				},
			]);

			const result = { outcome: "fail" };

			const enriched = await enrichGateResultWithMemory(result, "test");

			expect(enriched.memoryContext.hasHistoricalPattern).toBe(true);
			expect(enriched.memoryContext.similarFailures[0].confidence).toBe(0.99);
		});
	});

	describe("Diagnostic use cases", () => {
		it("enables_operator_understanding_of_recurring_failures", async () => {
			memoryStore.getRelevantMemoriesRanked.mockResolvedValueOnce([
				{
					id: "known_issue_123",
					confidence: 0.95,
					content: "Network timeout during deployment",
				},
			]);

			const failedGate = {
				outcome: "fail",
				failureClass: "timeout",
				rationale: "Gate timed out",
			};

			const enriched = await enrichGateResultWithMemory(
				failedGate,
				"deployment-gate",
			);

			// Operator can now see this is a known pattern
			expect(enriched.memoryContext.hasHistoricalPattern).toBe(true);
			expect(enriched.outcome).toBe("fail"); // Decision unchanged
		});

		it("supports_gate_debugging_without_changing_logic", async () => {
			memoryStore.getRelevantMemoriesRanked.mockResolvedValueOnce([
				{ id: "m1", confidence: 0.7, content: "Similar issue" },
			]);

			const gateFail = { outcome: "fail", gateId: "security-gate" };

			const enriched = await enrichGateResultWithMemory(
				gateFail,
				"security-gate",
			);

			// Gate decision is unchanged
			expect(enriched.outcome).toBe("fail");
			// But diagnostic context is added
			expect(enriched.memoryContext).toBeDefined();
		});
	});
});
