/**
 * Dispatch Memory Enhancement Tests
 *
 * Verify that dispatch rules can use memory to enhance unit ranking.
 */

import { beforeEach, describe, expect, it, vi } from "vitest";
import { enhanceUnitRankingWithMemory } from "../uok/auto-dispatch.js";

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

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

describe("Dispatch Memory Enhancement", () => {
	beforeEach(() => {
		vi.clearAllMocks();
		memoryStore.isDbAvailable.mockReturnValue(true);
	});

	describe("enhanceUnitRankingWithMemory", () => {
		it("boosts_units_with_matching_memory_patterns", async () => {
			memoryStore.getRelevantMemoriesRanked.mockResolvedValueOnce([
				{ confidence: 0.9, content: "execute-task succeeded" },
			]);

			const units = [
				{ id: "task1", type: "execute-task" },
				{ id: "plan1", type: "plan-milestone" },
			];

			const result = await enhanceUnitRankingWithMemory(units, {
				task1: 0.5,
				plan1: 0.5,
			});

			// Task1 should have higher score due to memory boost
			expect(result[0].id).toBe("task1");
			expect(result[0].score).toBeGreaterThan(0.5);
			expect(result[0].memoryBoost).toBeGreaterThan(0);
		});

		it("returns_units_in_score_order", async () => {
			memoryStore.getRelevantMemoriesRanked.mockResolvedValue([
				{ confidence: 0.8 },
			]);

			const units = [
				{ id: "u1", type: "type1" },
				{ id: "u2", type: "type2" },
				{ id: "u3", type: "type3" },
			];

			const result = await enhanceUnitRankingWithMemory(units, {
				u1: 0.3,
				u2: 0.6,
				u3: 0.4,
			});

			// Units should be sorted by final score
			const scores = result.map((u) => u.score);
			expect(scores[0]).toBeGreaterThanOrEqual(scores[1]);
			expect(scores[1]).toBeGreaterThanOrEqual(scores[2]);
		});

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

			const units = [
				{ id: "u1", type: "type1" },
				{ id: "u2", type: "type2" },
			];

			const result = await enhanceUnitRankingWithMemory(units);

			// Should return original units unchanged
			expect(result).toEqual(units);
			expect(memoryStore.getRelevantMemoriesRanked).not.toHaveBeenCalled();
		});

		it("uses_base_scores_for_initial_ranking", async () => {
			memoryStore.getRelevantMemoriesRanked.mockResolvedValue([]);

			const units = [{ id: "u1", type: "type1" }];

			const result = await enhanceUnitRankingWithMemory(units, {
				u1: 0.7,
			});

			expect(result[0].score).toBeGreaterThanOrEqual(0.7);
		});

		it("memory_boost_is_bounded", async () => {
			memoryStore.getRelevantMemoriesRanked.mockResolvedValueOnce([
				{ confidence: 1.0 }, // Perfect confidence
			]);

			const units = [{ id: "u1", type: "type1" }];

			const result = await enhanceUnitRankingWithMemory(units, {
				u1: 0.5,
			});

			// Boost should be max 0.15 (15% of confidence)
			expect(result[0].memoryBoost).toBeLessThanOrEqual(0.15);
		});

		it("handles_empty_unit_list", async () => {
			const result = await enhanceUnitRankingWithMemory([], {});
			expect(result).toEqual([]);
		});

		it("assigns_zero_boost_when_no_memories_found", async () => {
			memoryStore.getRelevantMemoriesRanked.mockResolvedValue([]);

			const units = [{ id: "u1", type: "type1" }];

			const result = await enhanceUnitRankingWithMemory(units, {
				u1: 0.5,
			});

			expect(result[0].memoryBoost).toBe(0);
			expect(result[0].score).toBe(0.5);
		});

		it("queries_for_dispatch_pattern_memory", async () => {
			memoryStore.getRelevantMemoriesRanked.mockResolvedValue([]);

			const units = [{ id: "u1", type: "research-task" }];

			await enhanceUnitRankingWithMemory(units);

			const callArgs = memoryStore.getRelevantMemoriesRanked.mock.calls[0];
			expect(callArgs[0]).toBe("dispatch pattern research-task");
		});

		it("requests_top_3_memories", async () => {
			memoryStore.getRelevantMemoriesRanked.mockResolvedValue([]);

			const units = [{ id: "u1", type: "type1" }];

			await enhanceUnitRankingWithMemory(units);

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

		it("preserves_original_unit_properties", async () => {
			memoryStore.getRelevantMemoriesRanked.mockResolvedValue([]);

			const units = [
				{
					id: "u1",
					type: "type1",
					customProp: "value",
					nested: { field: 123 },
				},
			];

			const result = await enhanceUnitRankingWithMemory(units);

			expect(result[0].id).toBe("u1");
			expect(result[0].type).toBe("type1");
			expect(result[0].customProp).toBe("value");
			expect(result[0].nested.field).toBe(123);
		});

		it("handles_missing_base_scores", async () => {
			memoryStore.getRelevantMemoriesRanked.mockResolvedValue([]);

			const units = [{ id: "u1", type: "type1" }];

			// Call without baseScores
			const result = await enhanceUnitRankingWithMemory(units);

			// Should default to 0.5
			expect(result[0].score).toBe(0.5);
		});

		it("degrades_gracefully_on_memory_lookup_error", async () => {
			memoryStore.getRelevantMemoriesRanked.mockRejectedValueOnce(
				new Error("DB connection failed"),
			);

			const units = [{ id: "u1", type: "type1" }];

			// Should not throw
			const result = await enhanceUnitRankingWithMemory(units, {
				u1: 0.5,
			});

			// Should return units (with some processing attempt)
			expect(result).toHaveLength(1);
		});

		it("handles_missing_unit_type", async () => {
			memoryStore.getRelevantMemoriesRanked.mockResolvedValue([]);

			const units = [{ id: "u1" }]; // No type

			const result = await enhanceUnitRankingWithMemory(units);

			expect(result[0].id).toBe("u1");
			expect(result).toHaveLength(1);
		});

		it("boosts_multiple_units_independently", async () => {
			memoryStore.getRelevantMemoriesRanked
				.mockResolvedValueOnce([{ confidence: 0.8 }]) // First unit
				.mockResolvedValueOnce([]); // Second unit (no match)

			const units = [
				{ id: "u1", type: "successful-type" },
				{ id: "u2", type: "new-type" },
			];

			const result = await enhanceUnitRankingWithMemory(units, {
				u1: 0.5,
				u2: 0.5,
			});

			// u1 should be boosted, u2 should not
			const u1Result = result.find((u) => u.id === "u1");
			const u2Result = result.find((u) => u.id === "u2");

			expect(u1Result.memoryBoost).toBeGreaterThan(0);
			expect(u2Result.memoryBoost).toBe(0);
		});

		it("high_confidence_memory_produces_larger_boost", async () => {
			// First call with low confidence
			memoryStore.getRelevantMemoriesRanked
				.mockResolvedValueOnce([{ confidence: 0.3 }])
				// Second call with high confidence
				.mockResolvedValueOnce([{ confidence: 0.95 }]);

			const units1 = [{ id: "u1", type: "type1" }];
			const units2 = [{ id: "u2", type: "type2" }];

			const result1 = await enhanceUnitRankingWithMemory(units1);
			const result2 = await enhanceUnitRankingWithMemory(units2);

			expect(result2[0].memoryBoost).toBeGreaterThan(result1[0].memoryBoost);
		});
	});

	describe("Integration with dispatch rules", () => {
		it("can_enhance_typical_dispatch_candidates", async () => {
			memoryStore.getRelevantMemoriesRanked.mockResolvedValue([
				{ confidence: 0.85 },
			]);

			const candidates = [
				{ id: "plan-m001", type: "plan-milestone", unit: "milestone" },
				{ id: "plan-s001", type: "plan-slice", unit: "slice" },
				{
					id: "exec-t001",
					type: "execute-task",
					unit: "task",
				},
			];

			const result = await enhanceUnitRankingWithMemory(candidates, {
				"plan-m001": 0.8,
				"plan-s001": 0.7,
				"exec-t001": 0.6,
			});

			expect(result).toHaveLength(3);
			expect(result.every((u) => u.score !== undefined)).toBe(true);
		});

		it("maintains_deterministic_ordering_for_same_inputs", async () => {
			memoryStore.getRelevantMemoriesRanked.mockResolvedValue([]);

			const units = [
				{ id: "u1", type: "type1" },
				{ id: "u2", type: "type2" },
			];
			const scores = { u1: 0.5, u2: 0.5 };

			const result1 = await enhanceUnitRankingWithMemory(units, scores);
			const result2 = await enhanceUnitRankingWithMemory(units, scores);

			expect(result1.map((u) => u.id)).toEqual(result2.map((u) => u.id));
		});
	});
});
