import { describe, expect, test } from "bun:test";
import type { Task } from "../types";
import {
	buildDependencyGraph,
	extractTaskIdFromNodeId,
	filterGraph,
	generateMermaidFlowchart,
} from "../web/utils/dependency-graph";

// Helper to create minimal tasks for testing
function createTask(id: string, overrides: Partial<Task> = {}): Task {
	return {
		id,
		title: `Task ${id}`,
		status: "To Do",
		assignee: [],
		createdDate: "2025-01-01",
		labels: [],
		dependencies: [],
		...overrides,
	};
}

describe("buildDependencyGraph", () => {
	test("creates nodes and edges from tasks", () => {
		const tasks = [createTask("task-001"), createTask("task-002", { dependencies: ["task-001"] })];

		const graph = buildDependencyGraph(tasks);

		expect(graph.nodes).toHaveLength(2);
		expect(graph.edges).toHaveLength(1);
		expect(graph.edges[0]).toEqual({ source: "task-001", target: "task-002" });
	});

	test("calculates dependents correctly", () => {
		const tasks = [
			createTask("task-001"),
			createTask("task-002", { dependencies: ["task-001"] }),
			createTask("task-003", { dependencies: ["task-001"] }),
		];

		const graph = buildDependencyGraph(tasks);
		const task001 = graph.nodes.find((n) => n.id === "task-001");

		expect(task001?.dependents).toEqual(["task-002", "task-003"]);
	});

	test("annotates nodes with sequence index", () => {
		const tasks = [
			createTask("task-001", { ordinal: 0 }),
			createTask("task-002", { dependencies: ["task-001"] }),
			createTask("task-003", { dependencies: ["task-002"] }),
		];

		const graph = buildDependencyGraph(tasks);

		// First task should be in sequence 1
		const task001 = graph.nodes.find((n) => n.id === "task-001");
		expect(task001?.sequenceIndex).toBe(1);

		// Second task depends on first, should be in sequence 2
		const task002 = graph.nodes.find((n) => n.id === "task-002");
		expect(task002?.sequenceIndex).toBe(2);

		// Third task depends on second, should be in sequence 3
		const task003 = graph.nodes.find((n) => n.id === "task-003");
		expect(task003?.sequenceIndex).toBe(3);
	});

	test("handles tasks with no dependencies as unsequenced", () => {
		const tasks = [
			createTask("task-001"), // No ordinal, no deps, no dependents
			createTask("task-002"), // No ordinal, no deps, no dependents
		];

		const graph = buildDependencyGraph(tasks);

		// Isolated tasks should go to unsequenced bucket
		expect(graph.unsequenced).toHaveLength(2);
	});
});

describe("filterGraph", () => {
	test("filters by status", () => {
		const graph = buildDependencyGraph([
			createTask("task-001", { status: "Done" }),
			createTask("task-002", { status: "To Do" }),
		]);

		const filtered = filterGraph(graph, { showCompleted: false });

		expect(filtered.nodes).toHaveLength(1);
		expect(filtered.nodes[0]?.id).toBe("task-002");
	});

	test("includes completed tasks when showCompleted is true", () => {
		const graph = buildDependencyGraph([
			createTask("task-001", { status: "Done" }),
			createTask("task-002", { status: "To Do" }),
		]);

		const filtered = filterGraph(graph, { showCompleted: true });

		expect(filtered.nodes).toHaveLength(2);
	});

	test("filters by priority", () => {
		const graph = buildDependencyGraph([
			createTask("task-001", { priority: "high" }),
			createTask("task-002", { priority: "low" }),
		]);

		const filtered = filterGraph(graph, { priority: ["high"], showCompleted: true });

		expect(filtered.nodes).toHaveLength(1);
		expect(filtered.nodes[0]?.id).toBe("task-001");
	});

	test("filters edges to match filtered nodes", () => {
		const graph = buildDependencyGraph([
			createTask("task-001", { status: "Done" }),
			createTask("task-002", { status: "To Do", dependencies: ["task-001"] }),
		]);

		// When we hide completed tasks, the edge should be removed too
		const filtered = filterGraph(graph, { showCompleted: false });

		expect(filtered.edges).toHaveLength(0);
	});
});

describe("generateMermaidFlowchart", () => {
	test("generates valid Mermaid syntax", () => {
		const graph = buildDependencyGraph([
			createTask("task-001", { status: "Done", ordinal: 0 }),
			createTask("task-002", { status: "In Progress", dependencies: ["task-001"] }),
		]);

		const mermaid = generateMermaidFlowchart(graph);

		expect(mermaid).toContain("flowchart TB");
		expect(mermaid).toContain("task-001 --> task-002");
		expect(mermaid).toContain("classDef done");
		expect(mermaid).toContain("classDef inProgress");
		expect(mermaid).toContain("class task-001 done");
		expect(mermaid).toContain("class task-002 inProgress");
	});

	test("generates subgraphs for sequences", () => {
		const graph = buildDependencyGraph([
			createTask("task-001", { ordinal: 0 }),
			createTask("task-002", { dependencies: ["task-001"] }),
		]);

		const mermaid = generateMermaidFlowchart(graph);

		expect(mermaid).toContain('subgraph Seq1["Sequence 1"]');
		expect(mermaid).toContain('subgraph Seq2["Sequence 2"]');
	});

	test("applies high priority styling", () => {
		const graph = buildDependencyGraph([createTask("task-001", { priority: "high", ordinal: 0 })]);

		const mermaid = generateMermaidFlowchart(graph);

		expect(mermaid).toContain("class task-001 highPriority");
	});

	test("escapes special characters in labels", () => {
		const graph = buildDependencyGraph([
			createTask("task-001", { title: 'Task with "quotes" and [brackets]', ordinal: 0 }),
		]);

		const mermaid = generateMermaidFlowchart(graph);

		// Should escape quotes and remove brackets
		expect(mermaid).not.toContain('"quotes"');
		expect(mermaid).not.toContain("[brackets]");
	});
});

describe("extractTaskIdFromNodeId", () => {
	test("extracts task ID from Mermaid node ID", () => {
		expect(extractTaskIdFromNodeId("flowchart-task-001-123")).toBe("task-001");
		expect(extractTaskIdFromNodeId("flowchart-task-123-456")).toBe("task-123");
	});

	test("returns null for invalid node IDs", () => {
		expect(extractTaskIdFromNodeId("invalid")).toBeNull();
		expect(extractTaskIdFromNodeId("")).toBeNull();
	});
});
