/**
 * Lifecycle Hooks Tests (Tier 1.2 Phase 3b)
 *
 * Validates that lifecycle hooks properly flush memory syncs at unit/session completion.
 */

import { afterEach, beforeEach, describe, expect, it } from "vitest";
import {
	flushAllProjectsMemorySync,
	flushProjectMemorySync,
	onSessionEnd,
	onUnitTerminal,
} from "../lifecycle-hooks.js";
import {
	getSyncStatus,
	queueMemorySync,
	resetAllSchedulers,
} from "../sync-scheduler.js";

describe("Lifecycle Hooks (Tier 1.2 Phase 3b)", () => {
	const TEST_PROJECT = "test-project-123";

	beforeEach(() => {
		resetAllSchedulers();
		process.env.SM_ENABLED = "false";
	});

	afterEach(() => {
		resetAllSchedulers();
	});

	describe("flushProjectMemorySync", () => {
		it("flushes_queued_memories_for_project", async () => {
			for (let i = 0; i < 5; i++) {
				queueMemorySync(TEST_PROJECT, `mem-${i}`, {
					type: "note",
					content: `note ${i}`,
					projectId: TEST_PROJECT,
				});
			}

			const result = await flushProjectMemorySync(TEST_PROJECT);

			expect(result.synced).toBe(5);
			expect(result.failed).toBe(0);
			expect(result.status).toBe("ok");
			expect(result.elapsed).toBeGreaterThanOrEqual(0);
		});

		it("returns_empty_when_no_queued_items", async () => {
			const result = await flushProjectMemorySync(TEST_PROJECT);

			expect(result.synced).toBe(0);
			expect(result.failed).toBe(0);
			expect(result.status).toBe("ok");
		});

		it("includes_elapsed_time", async () => {
			queueMemorySync(TEST_PROJECT, "mem-1", {
				type: "note",
				content: "test",
				projectId: TEST_PROJECT,
			});

			const result = await flushProjectMemorySync(TEST_PROJECT);

			expect(typeof result.elapsed).toBe("number");
			expect(result.elapsed).toBeGreaterThanOrEqual(0);
		});

		it("clears_project_queue", async () => {
			for (let i = 0; i < 3; i++) {
				queueMemorySync(TEST_PROJECT, `mem-${i}`, {
					type: "note",
					content: `note ${i}`,
					projectId: TEST_PROJECT,
				});
			}

			await flushProjectMemorySync(TEST_PROJECT);

			const status = getSyncStatus(TEST_PROJECT);
			expect(status.queued).toBe(0);
		});

		it("does_not_throw_on_sm_unavailable", async () => {
			process.env.SM_ENABLED = "true";
			process.env.SINGULARITY_MEMORY_ADDR = "http://localhost:19999";

			for (let i = 0; i < 3; i++) {
				queueMemorySync(TEST_PROJECT, `mem-${i}`, {
					type: "note",
					content: `note ${i}`,
					projectId: TEST_PROJECT,
				});
			}

			// Should complete without throwing
			const result = await flushProjectMemorySync(TEST_PROJECT);

			expect(result).toHaveProperty("synced");
			expect(result).toHaveProperty("failed");
		});
	});

	describe("flushAllProjectsMemorySync", () => {
		it("flushes_multiple_projects", async () => {
			const proj1 = "project-1";
			const proj2 = "project-2";

			for (let i = 0; i < 3; i++) {
				queueMemorySync(proj1, `mem-1-${i}`, {
					type: "note",
					content: `proj1-${i}`,
					projectId: proj1,
				});
				queueMemorySync(proj2, `mem-2-${i}`, {
					type: "note",
					content: `proj2-${i}`,
					projectId: proj2,
				});
			}

			const result = await flushAllProjectsMemorySync([proj1, proj2]);

			expect(result.totalSynced).toBe(6);
			expect(result.totalFailed).toBe(0);
			expect(result.results).toHaveLength(2);
		});

		it("tracks_per_project_results", async () => {
			const proj1 = "project-1";
			const proj2 = "project-2";

			for (let i = 0; i < 2; i++) {
				queueMemorySync(proj1, `mem-1-${i}`, {
					type: "note",
					content: `proj1-${i}`,
					projectId: proj1,
				});
			}

			for (let i = 0; i < 3; i++) {
				queueMemorySync(proj2, `mem-2-${i}`, {
					type: "note",
					content: `proj2-${i}`,
					projectId: proj2,
				});
			}

			const result = await flushAllProjectsMemorySync([proj1, proj2]);

			expect(result.results[0].projectId).toBe(proj1);
			expect(result.results[0].synced).toBe(2);
			expect(result.results[1].projectId).toBe(proj2);
			expect(result.results[1].synced).toBe(3);
		});

		it("returns_totals_across_projects", async () => {
			const projects = ["proj-1", "proj-2", "proj-3"];

			for (const proj of projects) {
				for (let i = 0; i < 2; i++) {
					queueMemorySync(proj, `mem-${i}`, {
						type: "note",
						content: `test`,
						projectId: proj,
					});
				}
			}

			const result = await flushAllProjectsMemorySync(projects);

			expect(result.totalSynced).toBe(6);
			expect(result.results).toHaveLength(3);
		});
	});

	describe("onUnitTerminal", () => {
		it("flushes_queue_on_unit_completion", async () => {
			for (let i = 0; i < 3; i++) {
				queueMemorySync(TEST_PROJECT, `mem-${i}`, {
					type: "note",
					content: `note ${i}`,
					projectId: TEST_PROJECT,
				});
			}

			const result = await onUnitTerminal("unit-1", TEST_PROJECT, "completed");

			expect(result.unitId).toBe("unit-1");
			expect(result.finalStatus).toBe("completed");
			expect(result.syncResult.synced).toBe(3);
		});

		it("returns_sync_result_on_failure", async () => {
			process.env.SM_ENABLED = "true";
			process.env.SINGULARITY_MEMORY_ADDR = "http://localhost:19999";

			for (let i = 0; i < 2; i++) {
				queueMemorySync(TEST_PROJECT, `mem-${i}`, {
					type: "note",
					content: `note ${i}`,
					projectId: TEST_PROJECT,
				});
			}

			const result = await onUnitTerminal("unit-1", TEST_PROJECT, "failed");

			expect(result.finalStatus).toBe("failed");
			expect(result.syncResult).toHaveProperty("synced");
			expect(result.syncResult).toHaveProperty("failed");
		});

		it("includes_all_terminal_statuses", async () => {
			const statuses = ["completed", "failed", "cancelled", "blocked"];

			for (const status of statuses) {
				const result = await onUnitTerminal("unit-1", TEST_PROJECT, status);
				expect(result.finalStatus).toBe(status);
			}
		});
	});

	describe("onSessionEnd", () => {
		it("flushes_default_project", async () => {
			// Queue for default project (process.cwd())
			queueMemorySync(process.cwd(), "mem-1", {
				type: "note",
				content: "test",
				projectId: process.cwd(),
			});

			const result = await onSessionEnd();

			expect(result.status).toBe("ok");
			expect(result.flushedProjects).toBe(1);
		});

		it("flushes_specified_projects", async () => {
			const projects = ["proj-1", "proj-2"];

			for (const proj of projects) {
				queueMemorySync(proj, "mem-1", {
					type: "note",
					content: "test",
					projectId: proj,
				});
			}

			const result = await onSessionEnd(projects);

			expect(result.flushedProjects).toBe(2);
			expect(result.totalSynced).toBe(2);
		});

		it("returns_aggregated_results", async () => {
			const projects = ["proj-1", "proj-2", "proj-3"];

			for (const proj of projects) {
				for (let i = 0; i < 2; i++) {
					queueMemorySync(proj, `mem-${i}`, {
						type: "note",
						content: "test",
						projectId: proj,
					});
				}
			}

			const result = await onSessionEnd(projects);

			expect(result.totalSynced).toBe(6);
			expect(result.totalFailed).toBe(0);
		});
	});

	describe("Lifecycle Integration", () => {
		it("unit_terminal_then_session_end_flushes_all", async () => {
			const proj1 = "project-1";
			const proj2 = "project-2";

			// Unit 1 in project 1
			for (let i = 0; i < 2; i++) {
				queueMemorySync(proj1, `mem-1-${i}`, {
					type: "note",
					content: `proj1-${i}`,
					projectId: proj1,
				});
			}

			await onUnitTerminal("unit-1", proj1, "completed");

			// Unit 2 in project 2
			for (let i = 0; i < 3; i++) {
				queueMemorySync(proj2, `mem-2-${i}`, {
					type: "note",
					content: `proj2-${i}`,
					projectId: proj2,
				});
			}

			// Session end flushes remaining
			const result = await onSessionEnd([proj1, proj2]);

			expect(result.totalSynced).toBeGreaterThanOrEqual(3); // proj2 memories
		});

		it("multiple_units_in_same_project", async () => {
			// Unit 1
			for (let i = 0; i < 2; i++) {
				queueMemorySync(TEST_PROJECT, `mem-u1-${i}`, {
					type: "note",
					content: `unit1-${i}`,
					projectId: TEST_PROJECT,
				});
			}

			await onUnitTerminal("unit-1", TEST_PROJECT, "completed");

			// Unit 2
			for (let i = 0; i < 3; i++) {
				queueMemorySync(TEST_PROJECT, `mem-u2-${i}`, {
					type: "note",
					content: `unit2-${i}`,
					projectId: TEST_PROJECT,
				});
			}

			await onUnitTerminal("unit-2", TEST_PROJECT, "completed");

			// All should be flushed
			const status = getSyncStatus(TEST_PROJECT);
			expect(status.queued).toBe(0);
		});
	});

	describe("Error Handling", () => {
		it("gracefully_handles_scheduler_errors", async () => {
			// Even if scheduler has issues, hooks should complete
			const result = await flushProjectMemorySync(TEST_PROJECT);

			expect(result).toHaveProperty("status");
			expect(["ok", "error"]).toContain(result.status);
		});

		it("continues_on_partial_failures", async () => {
			const projects = ["proj-1", "proj-2"];

			for (const proj of projects) {
				queueMemorySync(proj, "mem-1", {
					type: "note",
					content: "test",
					projectId: proj,
				});
			}

			// Should complete even if one project has issues
			const result = await flushAllProjectsMemorySync(projects);

			expect(result).toHaveProperty("totalSynced");
			expect(result.results).toHaveLength(2);
		});
	});
});
