import { afterEach, beforeEach, describe, expect, it } from "bun:test";
import { mkdir, rm } from "node:fs/promises";
import { join } from "node:path";
import { $ } from "bun";
import { Core } from "../core/backlog.ts";
import type { Task } from "../types/index.ts";
import { createUniqueTestDir, initializeTestProject, safeCleanup } from "./test-utils.ts";

let TEST_DIR: string;

describe("Cleanup functionality", () => {
	let core: Core;

	// Sample data
	const sampleTask: Task = {
		id: "task-1",
		title: "Test Task",
		status: "Done",
		assignee: [],
		createdDate: "2025-07-21",
		labels: [],
		dependencies: [],
		rawContent: "Test task description",
	};

	beforeEach(async () => {
		TEST_DIR = createUniqueTestDir("test-cleanup");
		try {
			await rm(TEST_DIR, { recursive: true, force: true });
		} catch {
			// Ignore cleanup errors
		}
		await mkdir(TEST_DIR, { recursive: true });

		// Initialize git repo
		await $`git init -b main`.cwd(TEST_DIR).quiet();
		await $`git config user.name "Test User"`.cwd(TEST_DIR).quiet();
		await $`git config user.email test@example.com`.cwd(TEST_DIR).quiet();

		// Initialize backlog project
		core = new Core(TEST_DIR);
		await initializeTestProject(core, "Cleanup Test Project");
	});

	afterEach(async () => {
		try {
			await safeCleanup(TEST_DIR);
		} catch {
			// Ignore cleanup errors - the unique directory names prevent conflicts
		}
	});

	describe("Core functionality", () => {
		it("should create completed directory in backlog structure", async () => {
			await core.filesystem.ensureBacklogStructure();
			expect(core.filesystem.completedDir).toBe(join(TEST_DIR, "backlog", "completed"));
		});

		it("should move Done task to completed folder", async () => {
			// Create a task
			await core.createTask(sampleTask, false);

			// Verify task exists in active tasks
			const activeTasks = await core.filesystem.listTasks();
			expect(activeTasks).toHaveLength(1);
			expect(activeTasks[0]?.id).toBe("TASK-1");

			// Move to completed
			const success = await core.completeTask("task-1", false);
			expect(success).toBe(true);

			// Verify task is no longer in active tasks
			const activeTasksAfter = await core.filesystem.listTasks();
			expect(activeTasksAfter).toHaveLength(0);

			// Verify task is in completed tasks
			const completedTasks = await core.filesystem.listCompletedTasks();
			expect(completedTasks).toHaveLength(1);
			expect(completedTasks[0]?.id).toBe("TASK-1");
			expect(completedTasks[0]?.title).toBe("Test Task");
		});

		it("allows core completion callers to move active tasks into completed", async () => {
			await core.createTask(
				{
					...sampleTask,
					id: "task-2",
					title: "Not Done Task",
					status: "Not Done",
				},
				false,
			);

			const success = await core.completeTask("task-2", false);
			expect(success).toBe(true);
			expect(await core.filesystem.loadTask("task-2")).toBeNull();

			const completedTasks = await core.filesystem.listCompletedTasks();
			expect(completedTasks).toHaveLength(1);
			expect(completedTasks[0]?.id).toBe("TASK-2");
			expect(completedTasks[0]?.status).toBe("Not Done");
		});
	});

	describe("getTerminalStatusTasksByAge", () => {
		it("should filter Done tasks by age", async () => {
			// Create old Done task (7 days ago)
			const oldDate = new Date();
			oldDate.setDate(oldDate.getDate() - 7);
			const oldTask: Task = {
				...sampleTask,
				title: "Old Done Task",
				createdDate: oldDate.toISOString().split("T")[0] as string,
				updatedDate: oldDate.toISOString().split("T")[0] as string,
				rawContent: "Old task description",
			};
			await core.createTask(oldTask, false);

			// Create recent Done task (1 day ago)
			const recentDate = new Date();
			recentDate.setDate(recentDate.getDate() - 1);
			const recentTask: Task = {
				...sampleTask,
				id: "task-2",
				title: "Recent Done Task",
				createdDate: recentDate.toISOString().split("T")[0] as string,
				updatedDate: recentDate.toISOString().split("T")[0] as string,
				rawContent: "Recent task description",
			};
			await core.createTask(recentTask, false);

			// Create In Progress task
			const activeTask: Task = {
				...sampleTask,
				id: "task-3",
				title: "Active Task",
				status: "In Progress",
				createdDate: oldDate.toISOString().split("T")[0] as string,
				rawContent: "Active task description",
			};
			await core.createTask(activeTask, false);

			// Get tasks older than 3 days
			const oldTasks = await core.getTerminalStatusTasksByAge(3);
			expect(oldTasks).toHaveLength(1);
			expect(oldTasks[0]?.id).toBe("TASK-1");

			// Get tasks older than 0 days (should include recent task too)
			const allDoneTasks = await core.getTerminalStatusTasksByAge(0);
			expect(allDoneTasks).toHaveLength(2);
		});

		it("uses the final configured status as the cleanup status", async () => {
			const config = await core.filesystem.loadConfig();
			if (!config) {
				throw new Error("Expected test project config to exist");
			}
			await core.filesystem.saveConfig({
				...config,
				statuses: ["To Do", "Review", "Closed"],
				defaultStatus: "To Do",
			});

			const oldDate = new Date();
			oldDate.setDate(oldDate.getDate() - 7);
			const recentDate = new Date();
			recentDate.setDate(recentDate.getDate() - 1);
			const oldDateValue = oldDate.toISOString().split("T")[0] as string;
			const recentDateValue = recentDate.toISOString().split("T")[0] as string;

			await core.createTask(
				{
					...sampleTask,
					id: "task-1",
					title: "Old Closed Task",
					status: "Closed",
					createdDate: oldDateValue,
					updatedDate: oldDateValue,
				},
				false,
			);
			await core.createTask(
				{
					...sampleTask,
					id: "task-2",
					title: "Recent Closed Task",
					status: "Closed",
					createdDate: recentDateValue,
					updatedDate: recentDateValue,
				},
				false,
			);
			await core.createTask(
				{
					...sampleTask,
					id: "task-3",
					title: "Old Review Task",
					status: "Review",
					createdDate: oldDateValue,
					updatedDate: oldDateValue,
				},
				false,
			);
			await core.createTask(
				{
					...sampleTask,
					id: "task-4",
					title: "Old Literal Done Task",
					status: "Done",
					createdDate: oldDateValue,
					updatedDate: oldDateValue,
				},
				false,
			);

			const oldTasks = await core.getTerminalStatusTasksByAge(3);
			expect(oldTasks.map((task) => task.id)).toEqual(["TASK-1"]);

			const allTerminalTasks = await core.getTerminalStatusTasksByAge(0);
			expect(allTerminalTasks.map((task) => task.id).sort()).toEqual(["TASK-1", "TASK-2"]);
		});

		it("does not collapse internal spaces when matching the cleanup status", async () => {
			const config = await core.filesystem.loadConfig();
			if (!config) {
				throw new Error("Expected test project config to exist");
			}
			await core.filesystem.saveConfig({
				...config,
				statuses: ["To Do", "In Progress", "InProgress"],
				defaultStatus: "To Do",
			});

			const oldDate = new Date();
			oldDate.setDate(oldDate.getDate() - 7);
			const oldDateValue = oldDate.toISOString().split("T")[0] as string;

			await core.createTask(
				{
					...sampleTask,
					id: "task-1",
					title: "Old Terminal Task",
					status: "InProgress",
					createdDate: oldDateValue,
					updatedDate: oldDateValue,
				},
				false,
			);
			await core.createTask(
				{
					...sampleTask,
					id: "task-2",
					title: "Old Non-Terminal Task",
					status: "In Progress",
					createdDate: oldDateValue,
					updatedDate: oldDateValue,
				},
				false,
			);

			const oldTasks = await core.getTerminalStatusTasksByAge(3);
			expect(oldTasks.map((task) => task.id)).toEqual(["TASK-1"]);
		});

		it("should handle tasks without dates", async () => {
			const task: Task = {
				...sampleTask,
				title: "Task Without Date",
				createdDate: "",
				rawContent: "Task description",
			};
			await core.createTask(task, false);

			const oldTasks = await core.getTerminalStatusTasksByAge(1);
			expect(oldTasks).toHaveLength(0); // Should not include tasks without valid dates
		});

		it("should use updatedDate over createdDate when available", async () => {
			const oldDate = new Date();
			oldDate.setDate(oldDate.getDate() - 10);
			const recentDate = new Date();
			recentDate.setDate(recentDate.getDate() - 1);

			const task: Task = {
				id: "task-1",
				title: "Task with Both Dates",
				status: "Done",
				assignee: [],
				createdDate: oldDate.toISOString().split("T")[0] as string,
				updatedDate: recentDate.toISOString().split("T")[0] as string,
				labels: [],
				dependencies: [],
				rawContent: "Task description",
			};
			await core.createTask(task, false);

			// Should use updatedDate (recent) not createdDate (old)
			const oldTasks = await core.getTerminalStatusTasksByAge(5);
			expect(oldTasks).toHaveLength(0); // updatedDate is recent, so not old enough

			const recentTasks = await core.getTerminalStatusTasksByAge(0);
			expect(recentTasks).toHaveLength(1); // updatedDate makes it recent
		});
	});

	describe("Error handling", () => {
		it("should handle non-existent task gracefully", async () => {
			const success = await core.completeTask("non-existent", false);
			expect(success).toBe(false);
		});

		it("should return empty array for listCompletedTasks when no completed tasks exist", async () => {
			const completedTasks = await core.filesystem.listCompletedTasks();
			expect(completedTasks).toHaveLength(0);
		});
	});
});
