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

type Point = { x: number; y: number; t: number; pressure?: number };
type Stroke = { points: Point[]; color: string; width: number };

export default function SignaturePad({
  className = "",
  strokeColor = "#111827", // default gray-900
  strokeWidth = 2.5,
  backgroundColor = "transparent",
  saveFileName = "signature.png",
}: {
  className?: string;
  strokeColor?: string;
  strokeWidth?: number;
  backgroundColor?: string;
  saveFileName?: string;
}) {
  const canvasRef = useRef<HTMLCanvasElement | null>(null);
  const containerRef = useRef<HTMLDivElement | null>(null);
  const [isDrawing, setIsDrawing] = useState(false);
  const [strokes, setStrokes] = useState<Stroke[]>([]);
  const [currentStroke, setCurrentStroke] = useState<Stroke | null>(null);
  const [color, setColor] = useState(strokeColor);
  const [width, setWidth] = useState(strokeWidth);

  // Resize canvas to device pixel ratio and container size
  const resizeCanvas = useCallback(() => {
    const canvas = canvasRef.current;
    const container = containerRef.current;
    if (!canvas || !container) return;
    const rect = container.getBoundingClientRect();
    const dpr = Math.max(1, window.devicePixelRatio || 1);
    canvas.width = rect.width * dpr;
    canvas.height = rect.height * dpr;
    canvas.style.width = `${rect.width}px`;
    canvas.style.height = `${rect.height}px`;
    const ctx = canvas.getContext("2d");
    if (!ctx) return;
    ctx.scale(dpr, dpr);
    // redraw existing strokes
    redrawAll(ctx, strokes, backgroundColor, rect.width, rect.height);
  }, [strokes, backgroundColor]);

  useEffect(() => {
    resizeCanvas();
    const ro = new ResizeObserver(resizeCanvas);
    if (containerRef.current) ro.observe(containerRef.current);
    return () => ro.disconnect();
  }, [resizeCanvas]);

  // helper to draw a single stroke
  function drawStroke(ctx: CanvasRenderingContext2D, stroke: Stroke) {
    const { points, color, width } = stroke;
    if (points.length < 2) return;

    ctx.lineJoin = "round";
    ctx.lineCap = "round";
    ctx.strokeStyle = color;

    const minW = 0.8;
    const maxW = width * 2.2;
    const smoothing = 0.3;

    let lastWidth = width;

    ctx.beginPath();
    ctx.moveTo(points[0].x, points[0].y);

    for (let i = 1; i < points.length - 1; i++) {
      const p0 = points[i - 1];
      const p1 = points[i];
      const p2 = points[i + 1];

      // suaviza a curva: ponto médio entre atual e próximo
      const cx = (p1.x + p2.x) / 2;
      const cy = (p1.y + p2.y) / 2;

      const dx = p2.x - p1.x;
      const dy = p2.y - p1.y;
      const dt = p2.t - p1.t || 1;
      const speed = Math.sqrt(dx * dx + dy * dy) / dt;

      const targetWidth = Math.max(
        minW,
        maxW - Math.min(speed * 50, maxW - minW)
      );
      const lw = lastWidth + (targetWidth - lastWidth) * smoothing;
      lastWidth = lw;

      // aplica espessura incremental
      ctx.lineWidth = lw;
      ctx.quadraticCurveTo(p1.x, p1.y, cx, cy);
    }

    ctx.stroke();

    // cobre o último ponto
    const last = points[points.length - 1];
    ctx.beginPath();
    ctx.arc(last.x, last.y, lastWidth / 2, 0, Math.PI * 2);
    ctx.fillStyle = color;
    ctx.fill();
  }

  function handlePointerMove(e: React.PointerEvent) {
    if (!isDrawing || !currentStroke) return;
    const p = getPointerPos(e);

    setCurrentStroke((prev) => {
      if (!prev) return prev;
      const next = { ...prev, points: [...prev.points, p] };
      const ctx = canvasRef.current?.getContext("2d");
      if (ctx) drawStroke(ctx, next);
      return next;
    });
  }

  // redraws full canvas
  function redrawAll(
    ctx: CanvasRenderingContext2D,
    allStrokes: Stroke[],
    bg: string,
    w: number,
    h: number
  ) {
    // clear using CSS pixel size
    ctx.clearRect(0, 0, w, h);
    if (bg !== "transparent") {
      ctx.fillStyle = bg;
      ctx.fillRect(0, 0, w, h);
    }
    for (const s of allStrokes) drawStroke(ctx, s);
  }

  // pointer event handlers
  function getPointerPos(e: PointerEvent | React.PointerEvent) {
    const canvas = canvasRef.current!;
    const rect = canvas.getBoundingClientRect();
    return {
      x: (e as any).clientX - rect.left,
      y: (e as any).clientY - rect.top,
      t: Date.now(),
      pressure: (e as any).pressure ?? (e as any).force ?? 0.5,
    };
  }

  function handlePointerDown(e: React.PointerEvent) {
    (e.target as Element).setPointerCapture((e as any).pointerId);
    const p = getPointerPos(e);
    const stroke: Stroke = { points: [p], color, width };
    setCurrentStroke(stroke);
    setIsDrawing(true);
  }

  function drawSmoothLine(
    ctx: CanvasRenderingContext2D,
    points: Point[],
    color: string,
    width: number
  ) {
    if (points.length < 2) return;

    ctx.beginPath();
    ctx.lineJoin = "round";
    ctx.lineCap = "round";
    ctx.strokeStyle = color;
    ctx.lineWidth = width;

    // Começa do primeiro ponto
    ctx.moveTo(points[0].x, points[0].y);

    // Interpola entre os pontos
    for (let i = 1; i < points.length - 1; i++) {
      const midX = (points[i].x + points[i + 1].x) / 2;
      const midY = (points[i].y + points[i + 1].y) / 2;
      ctx.quadraticCurveTo(points[i].x, points[i].y, midX, midY);
    }

    // Último ponto
    const last = points[points.length - 1];
    ctx.lineTo(last.x, last.y);
    ctx.stroke();
  }

  // function handlePointerMove(e: React.PointerEvent) {
  //   if (!isDrawing || !currentStroke) return;
  //   const p = getPointerPos(e);

  //   setCurrentStroke((prev) => {
  //     if (!prev) return prev;
  //     const next = { ...prev, points: [...prev.points, p] };

  //     // redesenha o último traço suavizado
  //     const ctx = canvasRef.current?.getContext("2d");
  //     if (ctx) {
  //       // limpa só a área pequena do último trecho
  //       drawSmoothLine(ctx, next.points.slice(-3), next.color, next.width);
  //     }
  //     return next;
  //   });
  // }

  function handlePointerUp(e: React.PointerEvent) {
    try {
      (e.target as Element).releasePointerCapture((e as any).pointerId);
    } catch {}
    if (!currentStroke) {
      setIsDrawing(false);
      return;
    }
    setStrokes((s) => [...s, currentStroke]);
    setCurrentStroke(null);
    setIsDrawing(false);
  }

  // undo last stroke
  function undo() {
    setStrokes((prev) => {
      const next = prev.slice(0, -1);
      const canvas = canvasRef.current;
      if (!canvas) return next;
      const ctx = canvas.getContext("2d");
      if (!ctx) return next;
      redrawAll(
        ctx,
        next,
        backgroundColor,
        canvas.width / (window.devicePixelRatio || 1),
        canvas.height / (window.devicePixelRatio || 1)
      );
      return next;
    });
  }

  // clear
  function clear() {
    setStrokes([]);
    const canvas = canvasRef.current;
    if (!canvas) return;
    const ctx = canvas.getContext("2d");
    if (!ctx) return;
    redrawAll(
      ctx,
      [],
      backgroundColor,
      canvas.width / (window.devicePixelRatio || 1),
      canvas.height / (window.devicePixelRatio || 1)
    );
  }

  // export as data URL
  function toDataURL(mime = "image/png") {
    const canvas = canvasRef.current;
    if (!canvas) return null;
    // create a temporary canvas with the desired background
    if (backgroundColor === "transparent") return canvas.toDataURL(mime);
    const dpr = window.devicePixelRatio || 1;
    const tmp = document.createElement("canvas");
    tmp.width = canvas.width;
    tmp.height = canvas.height;
    const ctx = tmp.getContext("2d");
    if (!ctx) return null;
    ctx.scale(dpr, dpr);
    if (backgroundColor) {
      ctx.fillStyle = backgroundColor;
      ctx.fillRect(0, 0, tmp.width / dpr, tmp.height / dpr);
    }
    ctx.drawImage(canvas, 0, 0, tmp.width / dpr, tmp.height / dpr);
    return tmp.toDataURL(mime);
  }

  function download() {
    const data = toDataURL("image/png");
    if (!data) return;
    const a = document.createElement("a");
    a.href = data;
    a.download = saveFileName;
    document.body.appendChild(a);
    a.click();
    a.remove();
  }

  // keep canvas in sync when strokes change (e.g., undo/clear or external change)
  useEffect(() => {
    const canvas = canvasRef.current;
    if (!canvas) return;
    const ctx = canvas.getContext("2d");
    if (!ctx) return;
    redrawAll(
      ctx,
      strokes,
      backgroundColor,
      canvas.width / (window.devicePixelRatio || 1),
      canvas.height / (window.devicePixelRatio || 1)
    );
  }, [strokes, backgroundColor]);

  return (
    <div className={`space-y-2 ${className}`}>
      <div className="flex items-center gap-2">
        <label className="flex items-center gap-2">
          <span className="text-sm">Cor</span>
          <input
            aria-label="Cor do traço"
            type="color"
            value={color}
            onChange={(e) => setColor(e.target.value)}
            className="w-10 h-8 p-0 m-0"
          />
        </label>
        <label className="flex items-center gap-2">
          <span className="text-sm">Espessura</span>
          <input
            aria-label="Espessura do traço"
            type="range"
            min={1}
            max={24}
            value={width}
            onChange={(e) => setWidth(Number(e.target.value))}
          />
        </label>
        <button
          onClick={undo}
          className="px-3 py-1 rounded-md border hover:shadow-sm text-sm"
          aria-label="Desfazer"
        >
          Desfazer
        </button>
        <button
          onClick={clear}
          className="px-3 py-1 rounded-md border hover:shadow-sm text-sm"
          aria-label="Limpar"
        >
          Limpar
        </button>
        <button
          onClick={download}
          className="px-3 py-1 rounded-md border hover:shadow-sm text-sm"
          aria-label="Salvar"
        >
          Salvar PNG
        </button>
      </div>

      <div
        ref={containerRef}
        className="w-full h-48 md:h-64 rounded-lg border overflow-hidden touch-none"
        style={{
          background:
            backgroundColor === "transparent" ? undefined : backgroundColor,
        }}
      >
        <canvas
          ref={canvasRef}
          onPointerDown={handlePointerDown}
          onPointerMove={handlePointerMove}
          onPointerUp={handlePointerUp}
          onPointerCancel={handlePointerUp}
          onPointerLeave={handlePointerUp}
          style={{
            width: "100%",
            height: "100%",
            display: "block",
            touchAction: "none",
            border: "rgb(255, 0, 0)",
          }}
          className="border-2"
        />
      </div>

      <div className="text-xs text-gray-500">
        Dica: utilize mouse, caneta ou o dedo em telas sensíveis ao toque.
        Ajuste a espessura e cor antes de assinar.
      </div>
    </div>
  );
}
