import { type RefObject, useEffect } from 'react';
import type SignatureCanvas from 'react-signature-canvas';

/*
 * `react-signature-canvas` dibuja sobre un `<canvas>` con un buffer de pixeles
 * fijo (300x150 por defecto) que el CSS solo estira visualmente si no se
 * ajusta a mano -- eso distorsiona el trazo en cualquier contenedor que no
 * mida justo eso. Este helper sincroniza el buffer real del canvas con el
 * tamaño CSS de su contenedor (y el devicePixelRatio del dispositivo) cada
 * vez que ese contenedor cambia de tamaño, preservando el trazo ya dibujado.
 */

// Umbral en pixeles CSS (antes de escalar por devicePixelRatio) por debajo
// del cual un cambio de tamaño del contenedor se ignora por completo. Sin
// esto, el jitter de layout tipico de movil (la barra de direccion del
// navegador aparece/desaparece, o el viewport hace reflow al tocar la
// pantalla) dispara un resize "real" de 1-2px que de todos modos entra a
// cambiar canvas.width/height -- y eso BORRA el bitmap por especificacion de
// <canvas>, sin relacion alguna con que el usuario haya dibujado algo.
const RESIZE_TOLERANCE_PX = 3;

export function resizePadToContainer(
  ref: RefObject<SignatureCanvas | null>,
  containerRef: RefObject<HTMLDivElement | null>,
) {
  const current = ref.current;
  const container = containerRef.current;
  const canvas = current?.getCanvas();

  if (!current || !container || !canvas) return;

  const width = Math.max(1, Math.floor(container.clientWidth));
  const height = Math.max(1, Math.floor(container.clientHeight));
  const ratio = Math.max(window.devicePixelRatio || 1, 1);
  const targetWidth = Math.floor(width * ratio);
  const targetHeight = Math.floor(height * ratio);

  const widthDiff = Math.abs(canvas.width - targetWidth);
  const heightDiff = Math.abs(canvas.height - targetHeight);
  const toleranceRatio = RESIZE_TOLERANCE_PX * ratio;
  if (widthDiff <= toleranceRatio && heightDiff <= toleranceRatio) {
    return;
  }

  const currentData = current.isEmpty() ? [] : current.toData();

  canvas.width = targetWidth;
  canvas.height = targetHeight;
  canvas.style.width = `${width}px`;
  canvas.style.height = `${height}px`;
  canvas.getContext('2d')?.scale(ratio, ratio);
  current.clear();

  if (currentData.length) {
    current.fromData(currentData);
  }
}

// isDrawingRef en vez de estado de React a proposito: no necesita re-render,
// solo que el callback del ResizeObserver (que corre fuera del ciclo normal
// de React) pueda leer el valor mas reciente en el momento exacto en que se
// dispara -- un estado normal quedaria "stale" dentro del closure del
// observer si se leyera directo.
export function useResponsiveSignaturePad(
  ref: RefObject<SignatureCanvas | null>,
  containerRef: RefObject<HTMLDivElement | null>,
  isDrawingRef: RefObject<boolean>,
) {
  useEffect(() => {
    if (!containerRef.current) return;

    let debounceTimeout: number | undefined;
    let retryTimeout: number | undefined;

    // Mientras el usuario tiene el dedo/lapiz sobre el pad, CUALQUIER resize
    // (aunque sea uno real, no solo jitter) se pospone -- redimensionar a
    // mitad de un trazo pierde justo el trazo en curso, porque toData() en
    // ese instante todavia no incluye el punto que el usuario esta dibujando
    // ahora mismo. Se reintenta poco despues hasta que termine de dibujar.
    const syncSize = () => {
      if (isDrawingRef.current) {
        window.clearTimeout(retryTimeout);
        retryTimeout = window.setTimeout(syncSize, 200);
        return;
      }
      resizePadToContainer(ref, containerRef);
    };

    // Debounce: una rafaga de notificaciones del ResizeObserver (tipico de
    // movil, varios frames de reflow seguidos por un solo toque) colapsa en
    // una sola verificacion despues de que el layout se asiente, en vez de
    // reaccionar a cada frame intermedio.
    const debouncedSync = () => {
      window.clearTimeout(debounceTimeout);
      debounceTimeout = window.setTimeout(syncSize, 120);
    };

    const observer = new ResizeObserver(debouncedSync);
    observer.observe(containerRef.current);
    const initialTimeout = window.setTimeout(syncSize, 0);

    return () => {
      window.clearTimeout(initialTimeout);
      window.clearTimeout(debounceTimeout);
      window.clearTimeout(retryTimeout);
      observer.disconnect();
    };
  }, [containerRef, ref, isDrawingRef]);
}
