'use client';

import { useCallback, useEffect, useRef, useState } from 'react';
import Cropper from 'react-easy-crop';
import { getCroppedWebp, type CropArea } from '@/lib/image/crop';

type Props = {
  name: string;
  maxDimension: number;
  accept: string;
  buttonLabel?: string;
  previewClassName?: string;
  previewImgClassName?: string;
  allowSvgPassthrough?: boolean;
  quality?: number;
  disabled?: boolean;
};

// Campo de imagen con recorte (zoom + pan) y reescalado a WebP en el navegador.
// El archivo optimizado se inyecta en un <input type="file"> oculto con `name`,
// de modo que el formulario padre lo envía como si fuera la selección original.
export default function ImageCropperField({
  name,
  maxDimension,
  accept,
  buttonLabel = 'Elegir imagen',
  previewClassName = 'h-24 w-24 rounded-lg',
  previewImgClassName = 'h-full w-full object-cover',
  allowSvgPassthrough = false,
  quality = 0.82,
  disabled = false,
}: Props) {
  const pickerRef = useRef<HTMLInputElement>(null);
  const outputRef = useRef<HTMLInputElement>(null);
  const objectUrls = useRef<Set<string>>(new Set());

  const [modalSrc, setModalSrc] = useState<string | null>(null);
  const [aspect, setAspect] = useState(1);
  const [crop, setCrop] = useState({ x: 0, y: 0 });
  const [zoom, setZoom] = useState(1);
  const [area, setArea] = useState<CropArea | null>(null);
  const [previewUrl, setPreviewUrl] = useState<string | null>(null);
  const [working, setWorking] = useState(false);
  const [error, setError] = useState<string | null>(null);

  const track = useCallback((url: string) => {
    objectUrls.current.add(url);
    return url;
  }, []);

  const revoke = useCallback((url: string | null) => {
    if (url && objectUrls.current.has(url)) {
      URL.revokeObjectURL(url);
      objectUrls.current.delete(url);
    }
  }, []);

  useEffect(() => {
    const urls = objectUrls.current;
    return () => {
      for (const url of urls) URL.revokeObjectURL(url);
      urls.clear();
    };
  }, []);

  function setOutputFile(file: File) {
    const dt = new DataTransfer();
    dt.items.add(file);
    if (outputRef.current) outputRef.current.files = dt.files;
  }

  async function onPick(e: React.ChangeEvent<HTMLInputElement>) {
    setError(null);
    const file = e.target.files?.[0];
    e.target.value = ''; // permite re-elegir el mismo archivo
    if (!file) return;

    if (allowSvgPassthrough && file.type === 'image/svg+xml') {
      setOutputFile(file);
      revoke(previewUrl);
      setPreviewUrl(track(URL.createObjectURL(file)));
      return;
    }

    const src = track(URL.createObjectURL(file));
    try {
      const dims = await loadDims(src);
      setAspect(dims.width / dims.height || 1);
      setCrop({ x: 0, y: 0 });
      setZoom(1);
      setArea(null);
      setModalSrc(src);
    } catch {
      revoke(src);
      setError('No se pudo abrir la imagen.');
    }
  }

  function cerrarModal() {
    revoke(modalSrc);
    setModalSrc(null);
  }

  async function aplicar() {
    if (!modalSrc || !area) return;
    setWorking(true);
    setError(null);
    try {
      const blob = await getCroppedWebp(modalSrc, area, maxDimension, quality);
      setOutputFile(new File([blob], `${name}.webp`, { type: 'image/webp' }));
      revoke(previewUrl);
      setPreviewUrl(track(URL.createObjectURL(blob)));
      cerrarModal();
    } catch {
      setError('No se pudo procesar la imagen. Prueba con otra.');
    } finally {
      setWorking(false);
    }
  }

  return (
    <div className="flex flex-col gap-2">
      <input ref={outputRef} type="file" name={name} accept="image/webp,image/svg+xml" className="hidden" />
      <input
        ref={pickerRef}
        type="file"
        accept={accept}
        onChange={onPick}
        disabled={disabled}
        className="hidden"
      />

      <div className="flex items-center gap-3">
        {previewUrl && (
          <span className={`overflow-hidden border border-gray-200 bg-gray-50 ${previewClassName}`}>
            {/* eslint-disable-next-line @next/next/no-img-element */}
            <img src={previewUrl} alt="Previsualización" className={previewImgClassName} />
          </span>
        )}
        <button
          type="button"
          onClick={() => pickerRef.current?.click()}
          disabled={disabled}
          className="rounded-lg border border-gray-300 px-4 py-1.5 text-sm font-medium text-gray-700 transition hover:border-primary-700 hover:text-primary-700 disabled:opacity-50"
        >
          {previewUrl ? 'Cambiar imagen' : buttonLabel}
        </button>
      </div>

      {error && <p className="text-sm text-red-600">{error}</p>}

      {modalSrc && (
        <div className="fixed inset-0 z-50 flex items-center justify-center bg-black/60 p-4">
          <div className="flex w-full max-w-lg flex-col gap-4 rounded-2xl bg-white p-5 shadow-xl">
            <h3 className="font-display text-2xl tracking-tight text-black">Ajusta el encuadre</h3>
            <div className="relative h-[55vh] w-full overflow-hidden rounded-xl bg-gray-900">
              <Cropper
                image={modalSrc}
                crop={crop}
                zoom={zoom}
                aspect={aspect}
                onCropChange={setCrop}
                onZoomChange={setZoom}
                onCropComplete={(_area, areaPixels) => setArea(areaPixels)}
              />
            </div>
            <label className="flex items-center gap-3 text-sm text-gray-600">
              Zoom
              <input
                type="range"
                min={1}
                max={3}
                step={0.01}
                value={zoom}
                onChange={(e) => setZoom(Number(e.target.value))}
                className="flex-1 accent-[#2f5c46]"
              />
            </label>
            <div className="flex justify-end gap-2">
              <button
                type="button"
                onClick={cerrarModal}
                disabled={working}
                className="rounded-lg border border-gray-300 px-4 py-2 text-sm font-medium text-gray-700 transition hover:border-gray-500 disabled:opacity-50"
              >
                Cancelar
              </button>
              <button
                type="button"
                onClick={aplicar}
                disabled={working || !area}
                className="rounded-lg bg-primary-700 px-4 py-2 text-sm font-semibold text-white transition hover:bg-primary-900 disabled:opacity-50"
              >
                {working ? 'Procesando…' : 'Aplicar'}
              </button>
            </div>
          </div>
        </div>
      )}
    </div>
  );
}

function loadDims(src: string): Promise<{ width: number; height: number }> {
  return new Promise((resolve, reject) => {
    const img = new Image();
    img.onload = () => resolve({ width: img.naturalWidth, height: img.naturalHeight });
    img.onerror = () => reject(new Error('No se pudo cargar la imagen.'));
    img.src = src;
  });
}
