import { useCallback, useEffect, useRef } from "react";

interface CameraViewport3DProps {
  horizontal: number;
  vertical: number;
  roll: number;
  zoom: number;
  onOrbit: (horizontal: number, vertical: number) => void;
  onZoom: (zoom: number) => void;
  onReset: () => void;
  referenceImage?: string | null;
}

interface Vec3 {
  x: number;
  y: number;
  z: number;
}

const DEG = Math.PI / 180;

function rotateY(p: Vec3, angle: number): Vec3 {
  const c = Math.cos(angle);
  const s = Math.sin(angle);
  return { x: p.x * c + p.z * s, y: p.y, z: -p.x * s + p.z * c };
}

function rotateX(p: Vec3, angle: number): Vec3 {
  const c = Math.cos(angle);
  const s = Math.sin(angle);
  return { x: p.x, y: p.y * c - p.z * s, z: p.y * s + p.z * c };
}

function rotateZ(p: Vec3, angle: number): Vec3 {
  const c = Math.cos(angle);
  const s = Math.sin(angle);
  return { x: p.x * c - p.y * s, y: p.x * s + p.y * c, z: p.z };
}

function project(p: Vec3, fov: number, w: number, h: number): { x: number; y: number; depth: number } | null {
  if (p.z <= 0.1) return null;
  const scale = fov / p.z;
  return { x: w / 2 + p.x * scale, y: h / 2 - p.y * scale, depth: p.z };
}

function transformPoint(p: Vec3, azimuth: number, elevation: number, rollAngle: number, dist: number): Vec3 {
  let pt = rotateY(p, -azimuth * DEG);
  pt = rotateX(pt, elevation * DEG);
  pt = rotateZ(pt, -rollAngle * DEG);
  pt = { x: pt.x, y: pt.y, z: pt.z + dist };
  return pt;
}

const HUMANOID: Vec3[][] = [
  [{ x: 0, y: 1.7, z: 0 }, { x: 0, y: 1.5, z: 0 }],
  [{ x: -0.15, y: 1.7, z: 0 }, { x: 0.15, y: 1.7, z: 0 }],
  [{ x: 0, y: 1.5, z: 0 }, { x: 0, y: 0.9, z: 0 }],
  [{ x: -0.25, y: 1.5, z: 0 }, { x: 0, y: 1.5, z: 0 }],
  [{ x: 0, y: 1.5, z: 0 }, { x: 0.25, y: 1.5, z: 0 }],
  [{ x: -0.25, y: 1.5, z: 0 }, { x: -0.3, y: 1.0, z: 0 }],
  [{ x: 0.25, y: 1.5, z: 0 }, { x: 0.3, y: 1.0, z: 0 }],
  [{ x: 0, y: 0.9, z: 0 }, { x: -0.15, y: 0, z: 0 }],
  [{ x: 0, y: 0.9, z: 0 }, { x: 0.15, y: 0, z: 0 }],
];

function renderScene(
  ctx: CanvasRenderingContext2D,
  w: number,
  h: number,
  azimuth: number,
  elevation: number,
  rollAngle: number,
  focalLen: number,
) {
  const dist = 5.5 - (focalLen - 24) * 0.02;
  const fov = w * 0.8;

  ctx.clearRect(0, 0, w, h);

  // Background gradient
  const bg = ctx.createLinearGradient(0, 0, 0, h);
  bg.addColorStop(0, "#1a1f2e");
  bg.addColorStop(1, "#0d1117");
  ctx.fillStyle = bg;
  ctx.fillRect(0, 0, w, h);

  // Ground grid
  const gridLines: { p1: { x: number; y: number; depth: number }; p2: { x: number; y: number; depth: number }; alpha: number }[] = [];
  const gridSize = 4;
  const gridStep = 0.5;
  for (let i = -gridSize; i <= gridSize; i += gridStep) {
    const a = transformPoint({ x: i, y: 0, z: -gridSize }, azimuth, elevation, rollAngle, dist);
    const b = transformPoint({ x: i, y: 0, z: gridSize }, azimuth, elevation, rollAngle, dist);
    const pa = project(a, fov, w, h);
    const pb = project(b, fov, w, h);
    if (pa && pb) gridLines.push({ p1: pa, p2: pb, alpha: i === 0 ? 0.4 : 0.15 });

    const c = transformPoint({ x: -gridSize, y: 0, z: i }, azimuth, elevation, rollAngle, dist);
    const d = transformPoint({ x: gridSize, y: 0, z: i }, azimuth, elevation, rollAngle, dist);
    const pc = project(c, fov, w, h);
    const pd = project(d, fov, w, h);
    if (pc && pd) gridLines.push({ p1: pc, p2: pd, alpha: i === 0 ? 0.4 : 0.15 });
  }

  for (const line of gridLines) {
    ctx.beginPath();
    ctx.moveTo(line.p1.x, line.p1.y);
    ctx.lineTo(line.p2.x, line.p2.y);
    ctx.strokeStyle = `rgba(45, 212, 191, ${line.alpha})`;
    ctx.lineWidth = line.alpha > 0.3 ? 1.2 : 0.6;
    ctx.stroke();
  }

  // Humanoid figure
  for (const seg of HUMANOID) {
    const a = transformPoint(seg[0], azimuth, elevation, rollAngle, dist);
    const b = transformPoint(seg[1], azimuth, elevation, rollAngle, dist);
    const pa = project(a, fov, w, h);
    const pb = project(b, fov, w, h);
    if (pa && pb) {
      ctx.beginPath();
      ctx.moveTo(pa.x, pa.y);
      ctx.lineTo(pb.x, pb.y);
      ctx.strokeStyle = "rgba(255, 255, 255, 0.85)";
      ctx.lineWidth = 2.5;
      ctx.lineCap = "round";
      ctx.stroke();
    }
  }
  // Head circle
  const headCenter = transformPoint({ x: 0, y: 1.8, z: 0 }, azimuth, elevation, rollAngle, dist);
  const headEdge = transformPoint({ x: 0.12, y: 1.8, z: 0 }, azimuth, elevation, rollAngle, dist);
  const ph = project(headCenter, fov, w, h);
  const pe = project(headEdge, fov, w, h);
  if (ph && pe) {
    const r = Math.hypot(pe.x - ph.x, pe.y - ph.y);
    ctx.beginPath();
    ctx.arc(ph.x, ph.y, r, 0, Math.PI * 2);
    ctx.strokeStyle = "rgba(255, 255, 255, 0.85)";
    ctx.lineWidth = 2.5;
    ctx.stroke();
  }

  // Axis indicators (bottom-left)
  const axisOrigin = { x: 44, y: h - 44 };
  const axisLen = 28;
  const axes: { dir: Vec3; color: string; label: string }[] = [
    { dir: { x: 1, y: 0, z: 0 }, color: "#ef4444", label: "X" },
    { dir: { x: 0, y: 1, z: 0 }, color: "#22c55e", label: "Y" },
    { dir: { x: 0, y: 0, z: 1 }, color: "#3b82f6", label: "Z" },
  ];
  for (const axis of axes) {
    let d = rotateY(axis.dir, -azimuth * DEG);
    d = rotateX(d, elevation * DEG);
    d = rotateZ(d, -rollAngle * DEG);
    const ex = axisOrigin.x + d.x * axisLen;
    const ey = axisOrigin.y - d.y * axisLen;
    ctx.beginPath();
    ctx.moveTo(axisOrigin.x, axisOrigin.y);
    ctx.lineTo(ex, ey);
    ctx.strokeStyle = axis.color;
    ctx.lineWidth = 2;
    ctx.stroke();
    ctx.fillStyle = axis.color;
    ctx.font = "bold 10px sans-serif";
    ctx.fillText(axis.label, ex + 3, ey - 3);
  }

  // HUD info (top-left)
  ctx.fillStyle = "rgba(255,255,255,0.6)";
  ctx.font = "11px monospace";
  ctx.fillText(`方位 ${azimuth.toFixed(0)}°  俯仰 ${elevation.toFixed(0)}°  倾斜 ${rollAngle.toFixed(0)}°`, 12, 20);
  ctx.fillText(`焦距 ${focalLen}mm`, 12, 36);
}

export default function CameraViewport3D({
  horizontal,
  vertical,
  roll,
  zoom,
  onOrbit,
  onZoom,
  onReset,
  referenceImage,
}: CameraViewport3DProps) {
  const canvasRef = useRef<HTMLCanvasElement>(null);
  const dragRef = useRef<{ startX: number; startY: number; startH: number; startV: number } | null>(null);
  const rafRef = useRef(0);
  const refImgRef = useRef<HTMLImageElement | null>(null);

  const draw = useCallback(() => {
    const canvas = canvasRef.current;
    if (!canvas) return;
    const ctx = canvas.getContext("2d");
    if (!ctx) return;
    const rect = canvas.getBoundingClientRect();
    const dpr = window.devicePixelRatio || 1;
    const w = rect.width * dpr;
    const h = rect.height * dpr;
    if (canvas.width !== w || canvas.height !== h) {
      canvas.width = w;
      canvas.height = h;
    }
    ctx.setTransform(dpr, 0, 0, dpr, 0, 0);
    renderScene(ctx, rect.width, rect.height, horizontal, vertical, roll, zoom);

    if (refImgRef.current && refImgRef.current.complete && refImgRef.current.naturalWidth) {
      const img = refImgRef.current;
      const thumbSize = 64;
      const aspect = img.naturalWidth / img.naturalHeight;
      const tw = aspect >= 1 ? thumbSize : thumbSize * aspect;
      const th = aspect >= 1 ? thumbSize / aspect : thumbSize;
      const tx = rect.width - tw - 10;
      const ty = 10;
      ctx.globalAlpha = 0.85;
      ctx.drawImage(img, tx, ty, tw, th);
      ctx.globalAlpha = 1;
      ctx.strokeStyle = "rgba(45, 212, 191, 0.6)";
      ctx.lineWidth = 1;
      ctx.strokeRect(tx, ty, tw, th);
    }
  }, [horizontal, vertical, roll, zoom]);

  useEffect(() => {
    rafRef.current = requestAnimationFrame(draw);
    return () => cancelAnimationFrame(rafRef.current);
  }, [draw]);

  useEffect(() => {
    if (!referenceImage) { refImgRef.current = null; return; }
    const img = new Image();
    img.src = referenceImage;
    img.onload = () => { refImgRef.current = img; draw(); };
    refImgRef.current = img;
  }, [referenceImage, draw]);

  const handlePointerDown = useCallback((e: React.PointerEvent) => {
    const canvas = canvasRef.current;
    if (!canvas) return;
    canvas.setPointerCapture(e.pointerId);
    dragRef.current = { startX: e.clientX, startY: e.clientY, startH: horizontal, startV: vertical };
  }, [horizontal, vertical]);

  const handlePointerMove = useCallback((e: React.PointerEvent) => {
    if (!dragRef.current) return;
    const dx = e.clientX - dragRef.current.startX;
    const dy = e.clientY - dragRef.current.startY;
    const newH = Math.max(-180, Math.min(180, dragRef.current.startH - dx * 0.5));
    const newV = Math.max(-85, Math.min(65, dragRef.current.startV + dy * 0.5));
    onOrbit(newH, newV);
  }, [onOrbit]);

  const handlePointerUp = useCallback((e: React.PointerEvent) => {
    const canvas = canvasRef.current;
    if (canvas) canvas.releasePointerCapture(e.pointerId);
    dragRef.current = null;
  }, []);

  const handleWheel = useCallback((e: React.WheelEvent) => {
    e.preventDefault();
    const delta = e.deltaY > 0 ? 4 : -4;
    onZoom(Math.max(24, Math.min(100, zoom + delta)));
  }, [zoom, onZoom]);

  const handleDoubleClick = useCallback(() => {
    onReset();
  }, [onReset]);

  return (
    <canvas
      ref={canvasRef}
      className="camera-viewport-3d"
      style={{ width: "100%", height: "100%", cursor: dragRef.current ? "grabbing" : "grab", touchAction: "none" }}
      onPointerDown={handlePointerDown}
      onPointerMove={handlePointerMove}
      onPointerUp={handlePointerUp}
      onWheel={handleWheel}
      onDoubleClick={handleDoubleClick}
    />
  );
}


