import * as d3 from "d3";
import React, { useCallback, useEffect, useMemo, useRef, useState } from "react";
import type { Task } from "../../types";
import type { GraphFilters } from "../types/graph";
import { buildDependencyGraph, filterGraph } from "../utils/dependency-graph";

interface DependencyViewD3Props {
	tasks: Task[];
	onEditTask: (task: Task) => void;
	filters?: GraphFilters;
}

interface D3Node extends d3.SimulationNodeDatum {
	id: string;
	title: string;
	status: string;
	priority?: "high" | "medium" | "low";
	labels: string[];
	dependencies: string[];
	dependents: string[];
	sequenceIndex?: number;
}

interface D3Edge extends d3.SimulationLinkDatum<D3Node> {
	source: string | D3Node;
	target: string | D3Node;
}

// Color utilities
function getNodeColor(status: string): string {
	const lower = status.toLowerCase();
	if (lower.includes("done") || lower.includes("complete")) return "#22c55e";
	if (lower.includes("progress")) return "#3b82f6";
	return "#6b7280";
}

function getNodeStrokeColor(status: string): string {
	const lower = status.toLowerCase();
	if (lower.includes("done") || lower.includes("complete")) return "#16a34a";
	if (lower.includes("progress")) return "#2563eb";
	return "#4b5563";
}

function isCompleted(status: string): boolean {
	const lower = status.toLowerCase();
	return lower.includes("done") || lower.includes("complete");
}

export default function DependencyViewD3({ tasks, onEditTask, filters }: DependencyViewD3Props) {
	const svgRef = useRef<SVGSVGElement>(null);
	const containerRef = useRef<HTMLDivElement>(null);
	const [dimensions, setDimensions] = useState({ width: 800, height: 600 });

	// Create task lookup map
	const taskMap = useMemo(() => new Map(tasks.map((t) => [t.id, t])), [tasks]);

	// Build and filter graph
	const graph = useMemo(() => {
		const fullGraph = buildDependencyGraph(tasks);
		return filters ? filterGraph(fullGraph, filters) : fullGraph;
	}, [tasks, filters]);

	// Convert to D3-compatible format
	const d3Data = useMemo(() => {
		const nodes: D3Node[] = graph.nodes.map((n) => ({
			...n,
			x: undefined,
			y: undefined,
		}));
		const edges: D3Edge[] = graph.edges.map((e) => ({
			source: e.source,
			target: e.target,
		}));
		return { nodes, edges };
	}, [graph]);

	// Handle resize
	useEffect(() => {
		const updateDimensions = () => {
			if (containerRef.current) {
				const rect = containerRef.current.getBoundingClientRect();
				setDimensions({
					width: Math.max(600, rect.width),
					height: Math.max(500, rect.height),
				});
			}
		};

		updateDimensions();
		window.addEventListener("resize", updateDimensions);
		return () => window.removeEventListener("resize", updateDimensions);
	}, []);

	// Handle node click
	const handleNodeClick = useCallback(
		(nodeId: string) => {
			const task = taskMap.get(nodeId);
			if (task) onEditTask(task);
		},
		[taskMap, onEditTask],
	);

	// D3 visualization
	useEffect(() => {
		if (!svgRef.current || d3Data.nodes.length === 0) return;

		const svg = d3.select(svgRef.current);
		svg.selectAll("*").remove();

		const { width, height } = dimensions;

		// Create main group for zoom/pan
		const g = svg.append("g").attr("class", "graph-container");

		// Zoom behavior
		const zoom = d3
			.zoom<SVGSVGElement, unknown>()
			.scaleExtent([0.1, 4])
			.on("zoom", (event) => {
				g.attr("transform", event.transform);
			});

		svg.call(zoom);

		// Double-click to reset zoom
		svg.on("dblclick.zoom", () => {
			svg.transition().duration(500).call(zoom.transform, d3.zoomIdentity);
		});

		// Arrow marker for edges
		const defs = svg.append("defs");

		defs
			.append("marker")
			.attr("id", "arrowhead")
			.attr("viewBox", "-10 -5 10 10")
			.attr("refX", 28)
			.attr("refY", 0)
			.attr("markerWidth", 6)
			.attr("markerHeight", 6)
			.attr("orient", "auto")
			.append("path")
			.attr("d", "M-10,-5L0,0L-10,5")
			.attr("fill", "#9ca3af");

		// Highlighted arrow marker
		defs
			.append("marker")
			.attr("id", "arrowhead-highlight")
			.attr("viewBox", "-10 -5 10 10")
			.attr("refX", 28)
			.attr("refY", 0)
			.attr("markerWidth", 6)
			.attr("markerHeight", 6)
			.attr("orient", "auto")
			.append("path")
			.attr("d", "M-10,-5L0,0L-10,5")
			.attr("fill", "#3b82f6");

		// Create copies for simulation (D3 mutates these)
		const nodesCopy: D3Node[] = d3Data.nodes.map((d) => ({ ...d }));
		const edgesCopy: D3Edge[] = d3Data.edges.map((d) => ({ ...d }));

		// Force simulation
		const simulation = d3
			.forceSimulation<D3Node>(nodesCopy)
			.force(
				"link",
				d3
					.forceLink<D3Node, D3Edge>(edgesCopy)
					.id((d) => d.id)
					.distance(150),
			)
			.force("charge", d3.forceManyBody().strength(-500))
			.force("center", d3.forceCenter(width / 2, height / 2))
			.force("x", d3.forceX(width / 2).strength(0.05))
			.force("y", d3.forceY(height / 2).strength(0.05))
			.force("collision", d3.forceCollide().radius(40));

		// Draw edges
		const linkGroup = g.append("g").attr("class", "links");

		const links = linkGroup
			.selectAll("line")
			.data(edgesCopy)
			.join("line")
			.attr("stroke", "#9ca3af")
			.attr("stroke-width", 2)
			.attr("marker-end", "url(#arrowhead)")
			.style("opacity", 0.6);

		// Draw nodes
		const nodeGroup = g.append("g").attr("class", "nodes");

		const nodes = nodeGroup
			.selectAll<SVGGElement, D3Node>("g")
			.data(nodesCopy)
			.join("g")
			.attr("class", "node")
			.attr("cursor", "pointer");

		// Background circle for progress animation
		nodes
			.append("circle")
			.attr("r", 28)
			.attr("fill", (d) => getNodeColor(d.status))
			.attr("stroke", (d) => (d.priority === "high" ? "#ef4444" : getNodeStrokeColor(d.status)))
			.attr("stroke-width", (d) => (d.priority === "high" ? 3 : 2))
			.attr("class", "node-bg");

		// Progress fill circle (for completed tasks)
		nodes
			.filter((d) => isCompleted(d.status))
			.append("circle")
			.attr("r", 0)
			.attr("fill", "#16a34a")
			.attr("class", "progress-fill")
			.transition()
			.duration(800)
			.ease(d3.easeElastic)
			.attr("r", 24);

		// Inner circle (white/dark bg)
		nodes
			.filter((d) => !isCompleted(d.status))
			.append("circle")
			.attr("r", 20)
			.attr("fill", (d) => getNodeColor(d.status))
			.attr("opacity", 0.9);

		// Task ID label
		nodes
			.append("text")
			.attr("text-anchor", "middle")
			.attr("dy", 4)
			.attr("fill", "white")
			.attr("font-size", "11px")
			.attr("font-weight", "bold")
			.attr("pointer-events", "none")
			.text((d) => d.id.replace("task-", ""));

		// Title tooltip (below node)
		nodes
			.append("text")
			.attr("text-anchor", "middle")
			.attr("dy", 45)
			.attr("fill", "currentColor")
			.attr("class", "text-gray-700 dark:text-gray-300")
			.attr("font-size", "10px")
			.attr("pointer-events", "none")
			.text((d) => (d.title.length > 20 ? `${d.title.substring(0, 20)}...` : d.title));

		// Sequence badge
		nodes
			.filter((d) => d.sequenceIndex !== undefined)
			.append("circle")
			.attr("cx", 20)
			.attr("cy", -20)
			.attr("r", 10)
			.attr("fill", "#6366f1")
			.attr("stroke", "white")
			.attr("stroke-width", 1);

		nodes
			.filter((d) => d.sequenceIndex !== undefined)
			.append("text")
			.attr("x", 20)
			.attr("y", -16)
			.attr("text-anchor", "middle")
			.attr("fill", "white")
			.attr("font-size", "9px")
			.attr("font-weight", "bold")
			.attr("pointer-events", "none")
			.text((d) => d.sequenceIndex?.toString() || "");

		// Click handler
		nodes.on("click", (event, d) => {
			event.stopPropagation();
			handleNodeClick(d.id);
		});

		// Hover effects
		nodes
			.on("mouseenter", function (event, d) {
				// Highlight this node
				d3.select(this).select(".node-bg").attr("stroke-width", 4);

				// Find connected nodes
				const connectedIds = new Set<string>([d.id]);
				for (const dep of d.dependencies) connectedIds.add(dep);
				for (const dep of d.dependents) connectedIds.add(dep);

				// Dim non-connected nodes
				nodes.style("opacity", (n) => (connectedIds.has(n.id) ? 1 : 0.3));

				// Highlight connected edges
				links.each(function (l) {
					const sourceId = typeof l.source === "string" ? l.source : l.source.id;
					const targetId = typeof l.target === "string" ? l.target : l.target.id;
					const isConnected = sourceId === d.id || targetId === d.id;

					d3.select(this)
						.style("opacity", isConnected ? 1 : 0.1)
						.attr("stroke", isConnected ? "#3b82f6" : "#9ca3af")
						.attr("stroke-width", isConnected ? 3 : 2)
						.attr("marker-end", isConnected ? "url(#arrowhead-highlight)" : "url(#arrowhead)");
				});
			})
			.on("mouseleave", function (_, d) {
				// Reset all
				nodes.style("opacity", 1);
				d3.select(this).select(".node-bg").attr("stroke-width", d.priority === "high" ? 3 : 2);
				links
					.style("opacity", 0.6)
					.attr("stroke", "#9ca3af")
					.attr("stroke-width", 2)
					.attr("marker-end", "url(#arrowhead)");
			});

		// Drag behavior
		const drag = d3
			.drag<SVGGElement, D3Node>()
			.on("start", (event, d) => {
				if (!event.active) simulation.alphaTarget(0.3).restart();
				d.fx = d.x;
				d.fy = d.y;
			})
			.on("drag", (event, d) => {
				d.fx = event.x;
				d.fy = event.y;
			})
			.on("end", (event, d) => {
				if (!event.active) simulation.alphaTarget(0);
				// Keep node pinned where user dropped it
				// To unpin, double-click (handled below)
			});

		nodes.call(drag);

		// Double-click to unpin node
		nodes.on("dblclick", (event, d) => {
			event.stopPropagation();
			d.fx = null;
			d.fy = null;
			simulation.alpha(0.3).restart();
		});

		// Tick function
		simulation.on("tick", () => {
			links
				.attr("x1", (d) => (d.source as D3Node).x || 0)
				.attr("y1", (d) => (d.source as D3Node).y || 0)
				.attr("x2", (d) => (d.target as D3Node).x || 0)
				.attr("y2", (d) => (d.target as D3Node).y || 0);

			nodes.attr("transform", (d) => `translate(${d.x || 0},${d.y || 0})`);
		});

		// Initial zoom to fit
		setTimeout(() => {
			const bounds = (g.node() as SVGGElement)?.getBBox();
			if (bounds) {
				const scale = Math.min(
					0.9,
					Math.min(width / (bounds.width + 100), height / (bounds.height + 100)),
				);
				const translateX = width / 2 - (bounds.x + bounds.width / 2) * scale;
				const translateY = height / 2 - (bounds.y + bounds.height / 2) * scale;

				svg
					.transition()
					.duration(500)
					.call(zoom.transform, d3.zoomIdentity.translate(translateX, translateY).scale(scale));
			}
		}, 500);

		return () => {
			simulation.stop();
		};
	}, [d3Data, dimensions, handleNodeClick]);

	// Empty state
	if (tasks.length === 0) {
		return (
			<div className="flex items-center justify-center h-96 bg-white dark:bg-gray-800 rounded-lg border border-gray-200 dark:border-gray-700">
				<div className="text-center text-gray-500 dark:text-gray-400">
					<svg
						className="w-16 h-16 mx-auto mb-4 opacity-50"
						fill="none"
						stroke="currentColor"
						viewBox="0 0 24 24"
					>
						<path
							strokeLinecap="round"
							strokeLinejoin="round"
							strokeWidth={1}
							d="M13.828 10.172a4 4 0 00-5.656 0l-4 4a4 4 0 105.656 5.656l1.102-1.101m-.758-4.899a4 4 0 005.656 0l4-4a4 4 0 00-5.656-5.656l-1.1 1.1"
						/>
					</svg>
					<p className="text-lg font-medium">No tasks to display</p>
					<p className="text-sm mt-1">Create some tasks to see the dependency graph</p>
				</div>
			</div>
		);
	}

	// No nodes after filtering
	if (graph.nodes.length === 0) {
		return (
			<div className="flex items-center justify-center h-96 bg-white dark:bg-gray-800 rounded-lg border border-gray-200 dark:border-gray-700">
				<div className="text-center text-gray-500 dark:text-gray-400">
					<svg
						className="w-16 h-16 mx-auto mb-4 opacity-50"
						fill="none"
						stroke="currentColor"
						viewBox="0 0 24 24"
					>
						<path
							strokeLinecap="round"
							strokeLinejoin="round"
							strokeWidth={1}
							d="M3 4a1 1 0 011-1h16a1 1 0 011 1v2.586a1 1 0 01-.293.707l-6.414 6.414a1 1 0 00-.293.707V17l-4 4v-6.586a1 1 0 00-.293-.707L3.293 7.293A1 1 0 013 6.586V4z"
						/>
					</svg>
					<p className="text-lg font-medium">No tasks match filters</p>
					<p className="text-sm mt-1">Try adjusting your filter criteria</p>
				</div>
			</div>
		);
	}

	return (
		<div className="relative">
			{/* Stats and controls bar */}
			<div className="flex items-center justify-between mb-2 text-sm text-gray-500 dark:text-gray-400">
				<span>
					{graph.nodes.length} task{graph.nodes.length !== 1 ? "s" : ""} • {graph.edges.length}{" "}
					dependenc{graph.edges.length !== 1 ? "ies" : "y"}
				</span>
				<div className="flex items-center gap-4">
					<span className="text-xs">
						Drag to move • Scroll to zoom • Double-click node to unpin • Double-click background to
						reset
					</span>
				</div>
			</div>

			{/* Graph container */}
			<div
				ref={containerRef}
				className="w-full bg-white dark:bg-gray-800 rounded-lg border border-gray-200 dark:border-gray-700 overflow-hidden"
				style={{ height: "600px" }}
			>
				<svg
					ref={svgRef}
					width={dimensions.width}
					height={dimensions.height}
					className="dependency-graph-d3"
					style={{ display: "block" }}
				/>
			</div>

			{/* Legend */}
			<div className="flex flex-wrap items-center gap-4 mt-4 text-sm">
				<span className="text-gray-500 dark:text-gray-400">Legend:</span>
				<div className="flex items-center gap-2">
					<span className="w-4 h-4 rounded-full" style={{ backgroundColor: "#22c55e" }} />
					<span className="text-gray-600 dark:text-gray-300">Done</span>
				</div>
				<div className="flex items-center gap-2">
					<span className="w-4 h-4 rounded-full" style={{ backgroundColor: "#3b82f6" }} />
					<span className="text-gray-600 dark:text-gray-300">In Progress</span>
				</div>
				<div className="flex items-center gap-2">
					<span className="w-4 h-4 rounded-full" style={{ backgroundColor: "#6b7280" }} />
					<span className="text-gray-600 dark:text-gray-300">To Do</span>
				</div>
				<div className="flex items-center gap-2">
					<span
						className="w-4 h-4 rounded-full border-2"
						style={{ borderColor: "#ef4444", backgroundColor: "#6b7280" }}
					/>
					<span className="text-gray-600 dark:text-gray-300">High Priority</span>
				</div>
				<div className="flex items-center gap-2">
					<span
						className="w-4 h-4 rounded-full text-white text-xs flex items-center justify-center font-bold"
						style={{ backgroundColor: "#6366f1" }}
					>
						1
					</span>
					<span className="text-gray-600 dark:text-gray-300">Sequence #</span>
				</div>
			</div>
		</div>
	);
}
