// Rôle : fournit un calque canvas réutilisable pour les annotations dessinées.
import { useEffect, useRef, useState } from "react";

function getCanvasPoint(event, canvas, coordinateMode = "pixel") {
  const rect = canvas.getBoundingClientRect();
  const x = event.clientX - rect.left;
  const y = event.clientY - rect.top;
  if (coordinateMode === "percent") return { x: rect.width ? (x / rect.width) * 100 : 0, y: rect.height ? (y / rect.height) * 100 : 0 };
  return { x, y };
}

function pointDistance(a, b) {
  return Math.hypot(a.x - b.x, a.y - b.y);
}

function toCanvasPoint(point, size, coordinateMode) {
  if (coordinateMode === "percent") return { x: (point.x / 100) * size.width, y: (point.y / 100) * size.height };
  return point;
}

function strokeHitsPoint(stroke, point, size, coordinateMode) {
  const canvasPoint = toCanvasPoint(point, size, coordinateMode);
  const threshold = Math.max(10, (Number(stroke.width) || 4) * 2.2);
  return stroke.points.some((strokePoint) => pointDistance(toCanvasPoint(strokePoint, size, coordinateMode), canvasPoint) <= threshold);
}

function drawStroke(context, stroke, size, coordinateMode) {
  if (!stroke.points.length) return;
  context.strokeStyle = stroke.color;
  context.lineWidth = stroke.width;
  context.lineCap = "round";
  context.lineJoin = "round";
  context.beginPath();
  stroke.points.forEach((point, index) => {
    const canvasPoint = toCanvasPoint(point, size, coordinateMode);
    if (index === 0) context.moveTo(canvasPoint.x, canvasPoint.y);
    else context.lineTo(canvasPoint.x, canvasPoint.y);
  });
  context.stroke();
}

export function DrawingOverlay({ active, erase, strokes, color, width, onChange, getSurfaceSize, createStrokeId, coordinateMode = "pixel", className = "" }) {
  const canvasRef = useRef(null);
  const drawingRef = useRef(null);
  const [size, setSize] = useState({ width: 0, height: 0 });

  useEffect(() => {
    function syncSize() {
      const nextSize = getSurfaceSize?.() || { width: 0, height: 0 };
      setSize({
        width: Math.max(0, Math.ceil(nextSize.width || 0)),
        height: Math.max(0, Math.ceil(nextSize.height || 0))
      });
    }

    syncSize();
    const observer = typeof ResizeObserver !== "undefined" ? new ResizeObserver(syncSize) : null;
    const canvas = canvasRef.current;
    if (observer && canvas?.parentElement) observer.observe(canvas.parentElement);
    window.addEventListener("resize", syncSize);
    return () => {
      observer?.disconnect();
      window.removeEventListener("resize", syncSize);
    };
  }, [getSurfaceSize]);

  useEffect(() => {
    const canvas = canvasRef.current;
    if (!canvas || !size.width || !size.height) return;
    const ratio = window.devicePixelRatio || 1;
    canvas.width = Math.max(1, Math.floor(size.width * ratio));
    canvas.height = Math.max(1, Math.floor(size.height * ratio));
    canvas.style.width = `${size.width}px`;
    canvas.style.height = `${size.height}px`;
    const context = canvas.getContext("2d");
    context.setTransform(ratio, 0, 0, ratio, 0, 0);
    context.clearRect(0, 0, size.width, size.height);
    strokes.forEach((stroke) => drawStroke(context, stroke, size, coordinateMode));
  }, [coordinateMode, size, strokes]);

  function commitStroke(points) {
    if (points.length < 2) return;
    onChange([
      ...strokes,
      {
        id: createStrokeId?.() || `stroke_${Date.now().toString(36)}`,
        color,
        width,
        points
      }
    ]);
  }

  function handlePointerDown(event) {
    if (!active || event.button > 0) return;
    event.preventDefault();
    const canvas = canvasRef.current;
    const point = getCanvasPoint(event, canvas, coordinateMode);
    if (erase) {
      const nextStrokes = strokes.filter((stroke) => !strokeHitsPoint(stroke, point, size, coordinateMode));
      if (nextStrokes.length !== strokes.length) onChange(nextStrokes);
      return;
    }
    canvas.setPointerCapture?.(event.pointerId);
    drawingRef.current = { pointerId: event.pointerId, points: [point] };
  }

  function handlePointerMove(event) {
    const drawing = drawingRef.current;
    if (!drawing || drawing.pointerId !== event.pointerId) return;
    event.preventDefault();
    const point = getCanvasPoint(event, canvasRef.current, coordinateMode);
    const lastPoint = drawing.points[drawing.points.length - 1];
    if (pointDistance(toCanvasPoint(lastPoint, size, coordinateMode), toCanvasPoint(point, size, coordinateMode)) < 1.5) return;
    drawing.points.push(point);

    const canvas = canvasRef.current;
    const context = canvas.getContext("2d");
    drawStroke(context, { color, width, points: [lastPoint, point] }, size, coordinateMode);
  }

  function handlePointerUp(event) {
    const drawing = drawingRef.current;
    if (!drawing || drawing.pointerId !== event.pointerId) return;
    event.preventDefault();
    canvasRef.current.releasePointerCapture?.(event.pointerId);
    drawingRef.current = null;
    commitStroke(drawing.points);
  }

  return (
    <canvas
      ref={canvasRef}
      className={`drawing-overlay ${active ? "is-active" : ""} ${erase ? "is-erase" : ""} ${className}`.trim()}
      aria-hidden="true"
      onPointerDown={handlePointerDown}
      onPointerMove={handlePointerMove}
      onPointerUp={handlePointerUp}
      onPointerCancel={handlePointerUp}
    />
  );
}
