'use client';

/**
 * PanZoomStage — 휠 줌 + 드래그 팬 + 드롭 핸들러
 * v3 rich-ui.jsx 의 PanZoomStage 포팅
 */

import { useEffect, useRef, useState, type ReactNode } from 'react';
import { ZoomIn, ZoomOut, Maximize, Hand } from 'lucide-react';

interface PanZoomStageProps {
  width: number;
  height: number;
  initialFit?: boolean;
  minScale?: number;
  maxScale?: number;
  onCanvasDrop?: (drop: { x: number; y: number; type: string }) => void;
  children: ReactNode | ((ctx: { scale: number }) => ReactNode);
}

export function PanZoomStage({
  width, height,
  initialFit = true,
  minScale = 0.25, maxScale = 1.6,
  onCanvasDrop,
  children,
}: PanZoomStageProps) {
  const ref = useRef<HTMLDivElement>(null);
  const innerRef = useRef<HTMLDivElement>(null);
  const [pan, setPan] = useState({ x: 0, y: 0 });
  const [scale, setScale] = useState(1);
  const [dragging, setDragging] = useState(false);
  const [dropHover, setDropHover] = useState(false);
  const dragStart = useRef<{ x: number; y: number } | null>(null);
  const panStart = useRef<{ x: number; y: number } | null>(null);
  const scaleRef = useRef(1);
  const panRef = useRef({ x: 0, y: 0 });

  useEffect(() => { scaleRef.current = scale; }, [scale]);
  useEffect(() => { panRef.current = pan; }, [pan]);

  // initial fit + recompute on resize
  useEffect(() => {
    if (!ref.current) return;
    const fit = () => {
      const el = ref.current; if (!el) return;
      const pw = el.clientWidth, ph = el.clientHeight;
      if (initialFit) {
        const s = Math.min(pw / width, ph / height, 1);
        setScale(s);
        setPan({ x: (pw - width * s) / 2, y: (ph - height * s) / 2 });
      } else {
        setPan({ x: (pw - width) / 2, y: (ph - height) / 2 });
      }
    };
    fit();
    const ro = new ResizeObserver(fit);
    ro.observe(ref.current);
    return () => ro.disconnect();
  }, [width, height, initialFit]);

  const screenToCanvas = (clientX: number, clientY: number) => {
    if (!ref.current) return { x: 0, y: 0 };
    const rect = ref.current.getBoundingClientRect();
    return {
      x: (clientX - rect.left - panRef.current.x) / scaleRef.current,
      y: (clientY - rect.top  - panRef.current.y) / scaleRef.current,
    };
  };

  const onMouseDown = (e: React.MouseEvent) => {
    if (e.button !== 0) return;
    const target = e.target as HTMLElement;
    if (target.closest('[data-pz-node], button, input, select, textarea, a')) return;
    setDragging(true);
    dragStart.current = { x: e.clientX, y: e.clientY };
    panStart.current = { ...pan };
    e.preventDefault();
  };

  useEffect(() => {
    if (!dragging) return;
    const onMove = (e: MouseEvent) => {
      if (!dragStart.current || !panStart.current) return;
      const dx = e.clientX - dragStart.current.x;
      const dy = e.clientY - dragStart.current.y;
      setPan({ x: panStart.current.x + dx, y: panStart.current.y + dy });
    };
    const onUp = () => setDragging(false);
    window.addEventListener('mousemove', onMove);
    window.addEventListener('mouseup', onUp);
    return () => {
      window.removeEventListener('mousemove', onMove);
      window.removeEventListener('mouseup', onUp);
    };
  }, [dragging]);

  const onWheel = (e: React.WheelEvent) => {
    if (!ref.current) return;
    e.preventDefault();
    const delta = -e.deltaY * 0.0015;
    const next = Math.max(minScale, Math.min(maxScale, scale * (1 + delta)));
    const rect = ref.current.getBoundingClientRect();
    const cx = e.clientX - rect.left;
    const cy = e.clientY - rect.top;
    const k = next / scale;
    setPan({ x: cx - (cx - pan.x) * k, y: cy - (cy - pan.y) * k });
    setScale(next);
  };

  const onDragOver = (e: React.DragEvent) => {
    if (!onCanvasDrop) return;
    e.preventDefault();
    e.dataTransfer.dropEffect = 'copy';
    setDropHover(true);
  };
  const onDragLeave = () => setDropHover(false);
  const onDrop = (e: React.DragEvent) => {
    if (!onCanvasDrop) return;
    e.preventDefault();
    setDropHover(false);
    const type = e.dataTransfer.getData('application/x-ctrl-node-type')
              || e.dataTransfer.getData('text/plain');
    if (!type) return;
    const { x, y } = screenToCanvas(e.clientX, e.clientY);
    onCanvasDrop({ x, y, type });
  };

  const zoomIn  = () => setScale((s) => Math.min(maxScale, s * 1.15));
  const zoomOut = () => setScale((s) => Math.max(minScale, s / 1.15));
  const reset   = () => {
    if (!ref.current) return;
    const pw = ref.current.clientWidth, ph = ref.current.clientHeight;
    const s = Math.min(pw / width, ph / height, 1);
    setScale(s);
    setPan({ x: (pw - width * s) / 2, y: (ph - height * s) / 2 });
  };

  const childOut = typeof children === 'function' ? children({ scale }) : children;

  return (
    <>
      <div
        ref={ref}
        onMouseDown={onMouseDown}
        onWheel={onWheel}
        onDragOver={onDragOver}
        onDragLeave={onDragLeave}
        onDrop={onDrop}
        className="ctrl-pz-stage"
        style={{
          cursor: dragging ? 'grabbing' : 'grab',
          background: dropHover ? 'rgba(var(--v5-cyan-rgb), .04)' : 'transparent',
          boxShadow: dropHover ? 'inset 0 0 0 2px rgba(var(--v5-cyan-rgb), .4)' : 'none',
          userSelect: dragging ? 'none' : 'auto',
        }}
      >
        <div
          ref={innerRef}
          style={{
            position: 'absolute', left: 0, top: 0,
            width, height,
            transform: `translate(${pan.x}px, ${pan.y}px) scale(${scale})`,
            transformOrigin: '0 0',
          }}
        >
          {childOut}
        </div>
      </div>
      <div className="ctrl-pz-zoom">
        <button onClick={zoomOut} title="축소"><ZoomOut size={11} /></button>
        <button onClick={reset}   title="맞춤"><Maximize size={11} /></button>
        <button onClick={zoomIn}  title="확대"><ZoomIn size={11} /></button>
        <span className="ctrl-pz-pct">{Math.round(scale * 100)}%</span>
      </div>
      <div className="ctrl-pz-hint">
        <Hand size={10} />
        {onCanvasDrop ? '드래그로 이동 · 휠로 확대/축소 · 팔레트 드롭으로 노드 추가' : '드래그로 이동 · 휠로 확대/축소'}
      </div>
    </>
  );
}
