/**
 * Minimap Component
 *
 * A small overview panel showing all nodes with a draggable viewport rectangle.
 * Uses <canvas> element for performance with large graphs (1000+ nodes).
 */

'use client';

import React, { useRef, useEffect } from 'react';
import { useAtomValue, useSetAtom } from 'jotai';
import { uiNodesAtom } from '../core/graph-derived';
import { graphAtom } from '../core/graph-store';
import { panAtom, zoomAtom, viewportRectAtom } from '../core/viewport-store';
import { selectedNodeIdsAtom } from '../core/selection-store';
import { computeGraphBounds, getMinimapTransform, minimapToWorld } from './minimap-utils';

// Re-export utilities
export { computeGraphBounds, getMinimapTransform, minimapToWorld } from './minimap-utils';
export type { NodeLike, GraphBounds, MinimapTransform } from './minimap-utils';

export interface MinimapProps {
	/** Width of the minimap in pixels */
	width?: number;
	/** Height of the minimap in pixels */
	height?: number;
	/** Background color */
	backgroundColor?: string;
	/** Default node color */
	nodeColor?: string;
	/** Selected node color */
	selectedNodeColor?: string;
	/** Viewport rectangle color */
	viewportColor?: string;
	/** Border color */
	borderColor?: string;
	/** Edge line color */
	edgeColor?: string;
	/** Position: which corner to place the minimap */
	position?: 'top-left' | 'top-right' | 'bottom-left' | 'bottom-right';
	/** Offset from the corner in pixels */
	offset?: number;
	/** CSS class for the container */
	className?: string;
}

/**
 * Minimap overview of the entire graph.
 *
 * Shows all nodes as small rectangles with a viewport rectangle overlay.
 * Click or drag on the minimap to pan the viewport.
 *
 * @example
 * ```tsx
 * <Canvas>
 *   <Minimap position="bottom-right" />
 * </Canvas>
 * ```
 */
export function Minimap({
	width = 200,
	height = 150,
	backgroundColor = 'rgba(0, 0, 0, 0.7)',
	nodeColor = 'rgba(148, 163, 184, 0.6)',
	selectedNodeColor = 'rgba(99, 102, 241, 0.8)',
	viewportColor = 'rgba(255, 255, 255, 0.3)',
	borderColor = 'rgba(255, 255, 255, 0.2)',
	edgeColor = 'rgba(148, 163, 184, 0.3)',
	position = 'bottom-right',
	offset = 12,
	className,
}: MinimapProps) {
	const canvasRef = useRef<HTMLCanvasElement>(null);
	const nodes = useAtomValue(uiNodesAtom);
	const graph = useAtomValue(graphAtom);
	const pan = useAtomValue(panAtom);
	const zoom = useAtomValue(zoomAtom);
	const viewportRect = useAtomValue(viewportRectAtom);
	const selectedNodeIds = useAtomValue(selectedNodeIdsAtom);
	const setPan = useSetAtom(panAtom);

	const isDragging = useRef(false);

	// Compute graph bounds (all nodes)
	const graphBounds = computeGraphBounds(nodes);

	// Convert minimap pixel coords → world coords
	const minimapToWorldCoords = (mx: number, my: number): { x: number; y: number } | null => {
		if (!graphBounds) return null;
		const t = getMinimapTransform(graphBounds, width, height);
		if (!t) return null;
		return minimapToWorld(mx, my, t);
	};

	// Pan viewport to center on a world coordinate
	const panToWorld = (worldX: number, worldY: number) => {
		if (!viewportRect) return;
		const newPanX = -worldX * zoom + viewportRect.width / 2;
		const newPanY = -worldY * zoom + viewportRect.height / 2;
		setPan({ x: newPanX, y: newPanY });
	};

	// Draw minimap
	useEffect(() => {
		const canvas = canvasRef.current;
		if (!canvas) return;

		const ctx = canvas.getContext('2d');
		if (!ctx) return;

		const dpr = window.devicePixelRatio || 1;
		canvas.width = width * dpr;
		canvas.height = height * dpr;
		ctx.scale(dpr, dpr);

		// Background
		ctx.fillStyle = backgroundColor;
		ctx.fillRect(0, 0, width, height);

		const t = graphBounds ? getMinimapTransform(graphBounds, width, height) : null;
		if (!t) return;

		// Build node position lookup for edge drawing
		const nodePositions = new Map<string, { cx: number; cy: number }>();
		for (const node of nodes) {
			const x = (node.position.x - t.minX) * t.scale + t.offsetX;
			const y = (node.position.y - t.minY) * t.scale + t.offsetY;
			const w = Math.max((node.width ?? 200) * t.scale, 2);
			const h = Math.max((node.height ?? 100) * t.scale, 2);
			nodePositions.set(node.id, { cx: x + w / 2, cy: y + h / 2 });
		}

		// Draw edges (before nodes so nodes sit on top)
		ctx.strokeStyle = edgeColor;
		ctx.lineWidth = 1;
		graph.forEachEdge((_key, _attrs, source, target) => {
			const from = nodePositions.get(source);
			const to = nodePositions.get(target);
			if (!from || !to) return;
			ctx.beginPath();
			ctx.moveTo(from.cx, from.cy);
			ctx.lineTo(to.cx, to.cy);
			ctx.stroke();
		});

		// Draw nodes
		for (const node of nodes) {
			const pos = nodePositions.get(node.id);
			if (!pos) continue;
			const w = Math.max((node.width ?? 200) * t.scale, 2);
			const h = Math.max((node.height ?? 100) * t.scale, 2);

			ctx.fillStyle = selectedNodeIds.has(node.id) ? selectedNodeColor : nodeColor;
			ctx.fillRect(pos.cx - w / 2, pos.cy - h / 2, w, h);
		}

		// Draw viewport rectangle
		if (viewportRect && viewportRect.width > 0) {
			const vpWorldMinX = -pan.x / zoom;
			const vpWorldMinY = -pan.y / zoom;
			const vpWorldW = viewportRect.width / zoom;
			const vpWorldH = viewportRect.height / zoom;

			const vpX = (vpWorldMinX - t.minX) * t.scale + t.offsetX;
			const vpY = (vpWorldMinY - t.minY) * t.scale + t.offsetY;
			const vpW = vpWorldW * t.scale;
			const vpH = vpWorldH * t.scale;

			ctx.strokeStyle = viewportColor;
			ctx.lineWidth = 1.5;
			ctx.strokeRect(vpX, vpY, vpW, vpH);

			// Light fill
			ctx.fillStyle = viewportColor.replace(/[\d.]+\)$/, '0.1)');
			ctx.fillRect(vpX, vpY, vpW, vpH);
		}

		// Border
		ctx.strokeStyle = borderColor;
		ctx.lineWidth = 1;
		ctx.strokeRect(0, 0, width, height);
	}, [
		nodes,
		graph,
		pan,
		zoom,
		viewportRect,
		selectedNodeIds,
		width,
		height,
		backgroundColor,
		nodeColor,
		selectedNodeColor,
		viewportColor,
		borderColor,
		edgeColor,
		getMinimapTransform,
	]);

	// Handle click/drag on minimap
	const handlePointerDown = (e: React.PointerEvent) => {
		e.stopPropagation();
		e.preventDefault();
		isDragging.current = true;
		(e.target as HTMLElement).setPointerCapture(e.pointerId);

		const rect = canvasRef.current?.getBoundingClientRect();
		if (!rect) return;
		const mx = e.clientX - rect.left;
		const my = e.clientY - rect.top;
		const worldPos = minimapToWorldCoords(mx, my);
		if (worldPos) panToWorld(worldPos.x, worldPos.y);
	};

	const handlePointerMove = (e: React.PointerEvent) => {
		if (!isDragging.current) return;
		e.stopPropagation();

		const rect = canvasRef.current?.getBoundingClientRect();
		if (!rect) return;
		const mx = e.clientX - rect.left;
		const my = e.clientY - rect.top;
		const worldPos = minimapToWorldCoords(mx, my);
		if (worldPos) panToWorld(worldPos.x, worldPos.y);
	};

	const handlePointerUp = (e: React.PointerEvent) => {
		isDragging.current = false;
		(e.target as HTMLElement).releasePointerCapture(e.pointerId);
	};

	// Position styles
	const positionStyles: React.CSSProperties = {
		position: 'absolute',
		zIndex: 9999,
		...(position.includes('top') ? { top: offset } : { bottom: offset }),
		...(position.includes('left') ? { left: offset } : { right: offset }),
	};

	return (
		<canvas
			ref={canvasRef}
			className={className}
			style={{
				...positionStyles,
				width,
				height,
				borderRadius: 6,
				cursor: 'pointer',
				touchAction: 'none',
			}}
			onPointerDown={handlePointerDown}
			onPointerMove={handlePointerMove}
			onPointerUp={handlePointerUp}
		/>
	);
}
