'use client';

import { useMemo } from 'react';
import { motion } from 'framer-motion';
import { cn } from '@/lib/utils';
import type { BinaryTreeState, BinaryTreeNodeState } from '@/lib/visualizations/types';
import { TreeNode } from '../primitives/tree-node';

interface BinaryTreeViewProps {
  tree: BinaryTreeState;
  currentNodeId?: string;
  className?: string;
}

const NODE_RADIUS = 24;
const LEVEL_HEIGHT = 72;
const MIN_NODE_SPACING = 56;
const SVG_PADDING = 32;

interface NodePosition {
  node: BinaryTreeNodeState;
  x: number;
  y: number;
  parentX?: number;
  parentY?: number;
}

function buildNodeMap(nodes: BinaryTreeNodeState[]): Map<string, BinaryTreeNodeState> {
  const map = new Map<string, BinaryTreeNodeState>();
  for (const node of nodes) {
    map.set(node.id, node);
  }
  return map;
}

function calculateTreeDepth(
  nodeId: string | null,
  nodeMap: Map<string, BinaryTreeNodeState>
): number {
  if (!nodeId) return 0;
  const node = nodeMap.get(nodeId);
  if (!node) return 0;
  const leftDepth = calculateTreeDepth(node.left, nodeMap);
  const rightDepth = calculateTreeDepth(node.right, nodeMap);
  return 1 + Math.max(leftDepth, rightDepth);
}

function calculatePositions(
  tree: BinaryTreeState,
  totalWidth: number
): NodePosition[] {
  const nodeMap = buildNodeMap(tree.nodes);
  const positions: NodePosition[] = [];

  function traverse(
    nodeId: string | null,
    level: number,
    left: number,
    right: number,
    parentX?: number,
    parentY?: number
  ) {
    if (!nodeId) return;
    const node = nodeMap.get(nodeId);
    if (!node) return;

    const x = (left + right) / 2;
    const y = SVG_PADDING + level * LEVEL_HEIGHT + NODE_RADIUS;

    positions.push({ node, x, y, parentX, parentY });

    const childWidth = (right - left) / 2;
    traverse(node.left, level + 1, left, left + childWidth, x, y);
    traverse(node.right, level + 1, left + childWidth, right, x, y);
  }

  traverse(tree.rootId, 0, 0, totalWidth);
  return positions;
}

export function BinaryTreeView({
  tree,
  currentNodeId,
  className,
}: BinaryTreeViewProps) {
  const { positions, svgWidth, svgHeight } = useMemo(() => {
    const nodeMap = buildNodeMap(tree.nodes);
    const depth = calculateTreeDepth(tree.rootId, nodeMap);
    const maxNodesAtBottom = Math.pow(2, depth - 1);
    const width = Math.max(
      maxNodesAtBottom * MIN_NODE_SPACING + SVG_PADDING * 2,
      200
    );
    const height = depth * LEVEL_HEIGHT + SVG_PADDING * 2;
    const pos = calculatePositions(tree, width);
    return { positions: pos, svgWidth: width, svgHeight: height };
  }, [tree]);

  return (
    <div className={cn('flex flex-col items-center', className)}>
      {tree.label && (
        <span className="mb-2 text-sm font-medium text-[var(--muted-foreground)]">
          {tree.label}
        </span>
      )}
      <svg
        width={svgWidth}
        height={svgHeight}
        viewBox={`0 0 ${svgWidth} ${svgHeight}`}
        className="overflow-visible"
      >
        {/* Draw edges first (behind nodes) */}
        {positions.map(({ node, x, y, parentX, parentY }) =>
          parentX !== undefined && parentY !== undefined ? (
            <motion.line
              key={`edge-${node.id}`}
              x1={parentX}
              y1={parentY + NODE_RADIUS}
              x2={x}
              y2={y - NODE_RADIUS}
              className="stroke-[var(--border)]"
              strokeWidth={2}
              initial={false}
              animate={{
                opacity: node.state === 'visited' ? 0.5 : 1,
              }}
              transition={{ duration: 0.2 }}
            />
          ) : null
        )}

        {/* Draw nodes */}
        {positions.map(({ node, x, y }) => (
          <TreeNode
            key={node.id}
            node={node}
            x={x}
            y={y}
            radius={NODE_RADIUS}
          />
        ))}

        {/* Current node indicator arrow */}
        {currentNodeId && (() => {
          const currentPos = positions.find((p) => p.node.id === currentNodeId);
          if (!currentPos) return null;
          const { x, y } = currentPos;
          const arrowY = y + NODE_RADIUS + 12;
          return (
            <g key={`current-indicator-${currentNodeId}`}>
              <motion.polygon
                points={`${x},${arrowY} ${x - 6},${arrowY + 10} ${x + 6},${arrowY + 10}`}
                className="fill-[var(--primary)]"
                initial={{ opacity: 0 }}
                animate={{ opacity: 1 }}
                transition={{ duration: 0.2 }}
              />
              <motion.text
                x={x}
                y={arrowY + 24}
                textAnchor="middle"
                className="fill-[var(--primary)] text-xs font-medium"
                initial={{ opacity: 0 }}
                animate={{ opacity: 1 }}
                transition={{ duration: 0.2 }}
              >
                curr
              </motion.text>
            </g>
          );
        })()}
      </svg>
    </div>
  );
}
