import { useEffect, useRef, useState, useCallback } from "react";
import type { Card, Viewport } from "./types";
import { NoteCardView } from "./cards/NoteCardView";
import "./canvas.css";

const MIN_SCALE = 0.1;
const MAX_SCALE = 4;
const ZOOM_SENSITIVITY = 0.0015;

interface CanvasProps {
  initialCards: Card[];
}

export function Canvas({ initialCards }: CanvasProps) {
  const [cards, setCards] = useState<Card[]>(initialCards);
  const [vp, setVp] = useState<Viewport>({ x: 0, y: 0, scale: 1 });
  const [spaceHeld, setSpaceHeld] = useState(false);
  const containerRef = useRef<HTMLDivElement>(null);
  const panState = useRef<{ startX: number; startY: number; vpX: number; vpY: number } | null>(
    null,
  );

  useEffect(() => {
    const onKeyDown = (e: KeyboardEvent) => {
      if (e.code === "Space" && !e.repeat) setSpaceHeld(true);
    };
    const onKeyUp = (e: KeyboardEvent) => {
      if (e.code === "Space") setSpaceHeld(false);
    };
    window.addEventListener("keydown", onKeyDown);
    window.addEventListener("keyup", onKeyUp);
    return () => {
      window.removeEventListener("keydown", onKeyDown);
      window.removeEventListener("keyup", onKeyUp);
    };
  }, []);

  const onWheel = useCallback(
    (e: React.WheelEvent) => {
      if (!e.ctrlKey && !e.metaKey) return;
      e.preventDefault();
      const rect = containerRef.current!.getBoundingClientRect();
      const mx = e.clientX - rect.left;
      const my = e.clientY - rect.top;
      setVp((prev) => {
        const factor = Math.exp(-e.deltaY * ZOOM_SENSITIVITY);
        const next = Math.max(MIN_SCALE, Math.min(MAX_SCALE, prev.scale * factor));
        const k = next / prev.scale;
        return { x: mx - (mx - prev.x) * k, y: my - (my - prev.y) * k, scale: next };
      });
    },
    [],
  );

  const onPointerDown = (e: React.PointerEvent) => {
    const isPan = e.button === 1 || (e.button === 0 && spaceHeld);
    if (!isPan) return;
    e.preventDefault();
    (e.target as Element).setPointerCapture(e.pointerId);
    panState.current = { startX: e.clientX, startY: e.clientY, vpX: vp.x, vpY: vp.y };
  };

  const onPointerMove = (e: React.PointerEvent) => {
    if (!panState.current) return;
    const dx = e.clientX - panState.current.startX;
    const dy = e.clientY - panState.current.startY;
    setVp((prev) => ({ ...prev, x: panState.current!.vpX + dx, y: panState.current!.vpY + dy }));
  };

  const onPointerUp = (e: React.PointerEvent) => {
    if (panState.current) {
      (e.target as Element).releasePointerCapture(e.pointerId);
      panState.current = null;
    }
  };

  const updateCard = (id: string, patch: Partial<Card>) => {
    setCards((cs) => cs.map((c) => (c.id === id ? ({ ...c, ...patch } as Card) : c)));
  };

  return (
    <div
      ref={containerRef}
      className={`canvas-container ${spaceHeld ? "pan-mode" : ""}`}
      onWheel={onWheel}
      onPointerDown={onPointerDown}
      onPointerMove={onPointerMove}
      onPointerUp={onPointerUp}
      onPointerCancel={onPointerUp}
    >
      <div
        className="canvas-grid"
        style={{
          backgroundPosition: `${vp.x}px ${vp.y}px`,
          backgroundSize: `${40 * vp.scale}px ${40 * vp.scale}px`,
        }}
      />
      <div
        className="canvas-world"
        style={{ transform: `translate(${vp.x}px, ${vp.y}px) scale(${vp.scale})` }}
      >
        {cards.map((c) => {
          if (c.kind === "note") {
            return <NoteCardView key={c.id} card={c} onUpdate={(p) => updateCard(c.id, p)} />;
          }
          return null;
        })}
      </div>
      <div className="canvas-hud">
        <span>x {vp.x.toFixed(0)}</span>
        <span>y {vp.y.toFixed(0)}</span>
        <span>{(vp.scale * 100).toFixed(0)}%</span>
      </div>
    </div>
  );
}
