import { useRef, useEffect, useState, useCallback, type FC, type MouseEvent as ReactMouseEvent } from 'react';
import { treemap, hierarchy, treemapSquarify, type HierarchyRectangularNode } from 'd3-hierarchy';

/* ------------------------------------------------------------------ */
/*  Types                                                              */
/* ------------------------------------------------------------------ */

export interface TreemapNode {
  name: string;
  path?: string;
  value?: number;
  children?: TreemapNode[];
}

export interface TreemapCanvasProps {
  data: TreemapNode;
  onDrillDown?: (node: TreemapNode) => void;
  height?: number;
  className?: string;
}

/* ------------------------------------------------------------------ */
/*  Helpers                                                            */
/* ------------------------------------------------------------------ */

/**
 * Collapse single-child nodes so the treemap is not dominated by
 * long chains of directories that each have only one child.
 */
function skipSingleChild(node: TreemapNode): TreemapNode {
  if (node.children && node.children.length === 1) {
    const child = node.children[0]!;
    const merged: TreemapNode = {
      ...child,
      name: `${node.name}/${child.name}`,
      path: child.path ?? node.path,
    };
    return skipSingleChild(merged);
  }

  if (node.children) {
    return {
      ...node,
      children: node.children.map(skipSingleChild),
    };
  }

  return node;
}

/** Depth-based color intensity */
function blockColor(depth: number, maxDepth: number): string {
  const t = maxDepth > 0 ? depth / maxDepth : 0;
  const r = Math.round(30 + t * 29);   // 30 -> 59
  const g = Math.round(41 + t * 89);   // 41 -> 130
  const b = Math.round(59 + t * 187);  // 59 -> 246
  return `rgb(${r}, ${g}, ${b})`;
}

/* ------------------------------------------------------------------ */
/*  Component                                                          */
/* ------------------------------------------------------------------ */

const TreemapCanvas: FC<TreemapCanvasProps> = ({
  data,
  onDrillDown,
  height = 400,
  className = '',
}) => {
  const canvasRef = useRef<HTMLCanvasElement>(null);
  const containerRef = useRef<HTMLDivElement>(null);
  const [canvasWidth, setCanvasWidth] = useState(600);
  const [rootStack, setRootStack] = useState<TreemapNode[]>([]);
  const [tooltip, setTooltip] = useState<{ x: number; y: number; text: string } | null>(null);
  const layoutRef = useRef<HierarchyRectangularNode<TreemapNode>[]>([]);

  const currentRoot = rootStack.length > 0 ? rootStack[rootStack.length - 1]! : data;

  // Observe container width
  useEffect(() => {
    const el = containerRef.current;
    if (!el) return;

    const observer = new ResizeObserver((entries) => {
      for (const entry of entries) {
        setCanvasWidth(entry.contentRect.width);
      }
    });
    observer.observe(el);
    return () => observer.disconnect();
  }, []);

  // Reset root stack when data changes
  useEffect(() => {
    setRootStack([]);
  }, [data]);

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

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

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

    const collapsed = skipSingleChild(currentRoot);

    const root = hierarchy(collapsed)
      .sum((d) => d.value ?? 1)
      .sort((a, b) => (b.value ?? 0) - (a.value ?? 0));

    const layout = treemap<TreemapNode>()
      .size([canvasWidth, height])
      .paddingInner(2)
      .paddingOuter(2)
      .tile(treemapSquarify);

    const laidOut = layout(root);

    const leaves = laidOut.leaves();
    layoutRef.current = leaves;
    const maxDepth = root.height;

    // Clear
    ctx.fillStyle = '#0a0f1a';
    ctx.fillRect(0, 0, canvasWidth, height);

    // Draw blocks
    for (const leaf of leaves) {
      const x0 = leaf.x0 ?? 0;
      const y0 = leaf.y0 ?? 0;
      const w = (leaf.x1 ?? 0) - x0;
      const h = (leaf.y1 ?? 0) - y0;

      ctx.fillStyle = blockColor(leaf.depth, maxDepth);
      ctx.fillRect(x0, y0, w, h);

      // Border
      ctx.strokeStyle = '#1e293b';
      ctx.lineWidth = 1;
      ctx.strokeRect(x0, y0, w, h);

      // Label
      if (w > 60 && h > 18) {
        ctx.fillStyle = '#f1f5f9';
        ctx.font = '11px "JetBrains Mono", "Fira Code", monospace';
        ctx.textBaseline = 'top';
        const label = leaf.data.name;
        const maxChars = Math.floor((w - 8) / 7);
        const truncated = label.length > maxChars ? label.slice(0, maxChars - 1) + '\u2026' : label;
        ctx.fillText(truncated, x0 + 4, y0 + 4);

        // Value
        if (h > 32 && leaf.value != null) {
          ctx.fillStyle = '#94a3b8';
          ctx.font = '10px "JetBrains Mono", "Fira Code", monospace';
          ctx.fillText(String(leaf.value), x0 + 4, y0 + 18);
        }
      }
    }
  }, [currentRoot, canvasWidth, height]);

  // Mouse move for tooltip
  const handleMouseMove = useCallback(
    (e: ReactMouseEvent<HTMLCanvasElement>) => {
      const canvas = canvasRef.current;
      if (!canvas) return;
      const rect = canvas.getBoundingClientRect();
      const mx = e.clientX - rect.left;
      const my = e.clientY - rect.top;

      for (const leaf of layoutRef.current) {
        const x0 = leaf.x0 ?? 0;
        const y0 = leaf.y0 ?? 0;
        const x1 = leaf.x1 ?? 0;
        const y1 = leaf.y1 ?? 0;
        if (mx >= x0 && mx <= x1 && my >= y0 && my <= y1) {
          setTooltip({
            x: e.clientX - (containerRef.current?.getBoundingClientRect().left ?? 0) + 12,
            y: e.clientY - (containerRef.current?.getBoundingClientRect().top ?? 0) - 20,
            text: `${leaf.data.name} (${leaf.value ?? 0})`,
          });
          return;
        }
      }
      setTooltip(null);
    },
    [],
  );

  const handleMouseLeave = useCallback(() => setTooltip(null), []);

  // Click to drill down
  const handleClick = useCallback(
    (e: ReactMouseEvent<HTMLCanvasElement>) => {
      const canvas = canvasRef.current;
      if (!canvas) return;
      const rect = canvas.getBoundingClientRect();
      const mx = e.clientX - rect.left;
      const my = e.clientY - rect.top;

      for (const leaf of layoutRef.current) {
        const x0 = leaf.x0 ?? 0;
        const y0 = leaf.y0 ?? 0;
        const x1 = leaf.x1 ?? 0;
        const y1 = leaf.y1 ?? 0;
        if (mx >= x0 && mx <= x1 && my >= y0 && my <= y1) {
          // Find the first ancestor with children (the drillable parent)
          let target: HierarchyRectangularNode<TreemapNode> | null = leaf;
          while (target && (!target.data.children || target.data.children.length === 0)) {
            target = target.parent;
          }
          if (target && target.data.children && target.data !== currentRoot) {
            setRootStack((prev) => [...prev, target!.data]);
            onDrillDown?.(target.data);
          }
          return;
        }
      }
    },
    [currentRoot, onDrillDown],
  );

  // Breadcrumb navigation
  const breadcrumbs: { label: string; index: number }[] = [
    { label: data.name || 'root', index: -1 },
  ];
  rootStack.forEach((node, i) => {
    breadcrumbs.push({ label: node.name, index: i });
  });

  const handleBreadcrumbClick = useCallback(
    (index: number) => {
      if (index < 0) {
        setRootStack([]);
      } else {
        setRootStack((prev) => prev.slice(0, index + 1));
      }
    },
    [],
  );

  return (
    <div ref={containerRef} className={`relative ${className}`}>
      {/* Breadcrumb */}
      {rootStack.length > 0 && (
        <div className="flex items-center gap-1 text-xs text-text-secondary mb-2 font-mono">
          {breadcrumbs.map((crumb, i) => (
            <span key={i} className="flex items-center gap-1">
              {i > 0 && <span className="text-text-muted">&gt;</span>}
              <button
                onClick={() => handleBreadcrumbClick(crumb.index)}
                className="hover:text-text-primary transition-colors cursor-pointer"
              >
                {crumb.label}
              </button>
            </span>
          ))}
        </div>
      )}

      {/* Canvas */}
      <canvas
        ref={canvasRef}
        style={{ width: canvasWidth, height, display: 'block', cursor: 'pointer' }}
        className="rounded-lg"
        onMouseMove={handleMouseMove}
        onMouseLeave={handleMouseLeave}
        onClick={handleClick}
      />

      {/* Tooltip */}
      {tooltip && (
        <div
          className="absolute pointer-events-none z-50 px-2 py-1 rounded bg-surface-elevated border border-border text-text-primary text-xs font-mono whitespace-nowrap"
          style={{ left: tooltip.x, top: tooltip.y }}
        >
          {tooltip.text}
        </div>
      )}
    </div>
  );
};

export default TreemapCanvas;
