import { useCallback, useEffect, useRef, useState } from 'preact/hooks';
import { Info } from 'lucide-preact';
import GLightbox from 'glightbox';
import 'glightbox/dist/css/glightbox.min.css';
/**
 * Primera isla Preact del sitio (item 09). Grid de inspiración de la galería:
 * al montarse hace fetch al endpoint JSON del item 08 (gallery.images) y arma
 * un masonry con el envelope { category, count, images }.
 *
 * El contrato del 08 se consume TAL CUAL: `src` ya es la URL pública resuelta
 * (no se reconstruye), el ORDEN del array ya viene alfabético (no se reordena)
 * y width/height sólo existen cuando type === 'image' (los videos NO traen esas
 * claves → aspect 16/9 por defecto). Categoría vacía → count:0/images:[] y el
 * empty-state VISUAL lo pone esta isla (no el endpoint).
 *
 * Masonry sin layout-shift: CSS `columns` (cero JS de medición) + width/height
 * en cada <img> para reservar el aspect-ratio antes de cargar. Matiz conocido y
 * revisable (ver task doc): `columns` llena columna-por-columna, así que el
 * orden VISUAL no es lineal izq→der aunque el array no se reordene.
 *
 * Lightbox (mismo recurso que Apex — GLightbox, vendored ahí, acá instalado vía
 * npm): cada card es un <a data-gallery="gallery-grid" href={fullSrc}> alrededor
 * de la imagen/video; GLightbox se re-inicializa en cada cambio de `images`
 * (columns nuevas = links nuevos, hay que re-escanear el selector). Import
 * dentro de esta isla → mismo chunk lazy que ya solo carga en /gallery/*, el
 * bundle global no crece.
 */

// Íconos Lucide inline (mismo patrón que hero-slider.js: paths sobre un <svg>
// con currentColor). Evita sumar una dep de íconos a la isla.
const ICON_ATTRS = {
  xmlns: 'http://www.w3.org/2000/svg',
  viewBox: '0 0 24 24',
  fill: 'none',
  stroke: 'currentColor',
  'stroke-width': '1.5',
  'stroke-linecap': 'round',
  'stroke-linejoin': 'round',
};

function AlertIcon({ class: className }) {
  return (
    <svg {...ICON_ATTRS} class={className} aria-hidden="true">
      <path d="m21.73 18-8-14a2 2 0 0 0-3.48 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-3Z" />
      <path d="M12 9v4" />
      <path d="M12 17h.01" />
    </svg>
  );
}

function RefreshIcon({ class: className }) {
  return (
    <svg {...ICON_ATTRS} class={className} aria-hidden="true">
      <path d="M3 12a9 9 0 0 1 9-9 9.75 9.75 0 0 1 6.74 2.74L21 8" />
      <path d="M21 3v5h-5" />
      <path d="M21 12a9 9 0 0 1-9 9 9.75 9.75 0 0 1-6.74-2.74L3 16" />
      <path d="M8 16H3v5" />
    </svg>
  );
}

/**
 * Imagen con fade-in al cargar (decisión 4-B) + zoom on-hover (mismo patrón
 * `group-hover:scale-*` que .service-highlight-card.blade.php — `group` vive en
 * el <a> que envuelve la card). Arranca transparente y suma `is-loaded` cuando
 * el asset resuelve. Chequea `complete` en el ref porque una imagen cacheada
 * puede no disparar onLoad tras el montaje.
 */
function GalleryImage({ image }) {
  const [loaded, setLoaded] = useState(false);

  const onRef = useCallback((node) => {
    if (node && node.complete) setLoaded(true);
  }, []);

  return (
    <img
      ref={onRef}
      src={image.src}
      alt={image.alt}
      width={image.width}
      height={image.height}
      loading="lazy"
      decoding="async"
      onLoad={() => setLoaded(true)}
      class={`gallery-img w-full group-hover:scale-105${loaded ? ' is-loaded' : ''}`}
    />
  );
}

/**
 * Card de un item del grid — interactiva (mismo recurso que Apex: GLightbox).
 * El <a> es el trigger: href a la imagen/video en resolución completa,
 * data-gallery agrupa TODAS las cards en un único lightbox navegable (flechas
 * prev/next entre fotos), data-type distingue imagen de video para que
 * GLightbox monte el reproductor en vez de una imagen. `group` habilita el
 * zoom-on-hover de GalleryImage.
 */
function GalleryCard({ image, index }) {
  const isVideo = image.type === 'video';

  return (
    <figure
      class="gallery-card mb-4 break-inside-avoid overflow-hidden rounded-lg bg-surface-2"
      style={`--i:${index}`}
    >
      <a
        href={image.src}
        class="group block cursor-zoom-in"
        data-gallery="gallery-grid"
        data-type={isVideo ? 'video' : 'image'}
        aria-label={image.alt}
      >
        {isVideo ? (
          <video
            src={image.src}
            class="gallery-img is-loaded w-full group-hover:scale-105"
            style="aspect-ratio:16/9"
            muted
            loop
            playsInline
            preload="metadata"
          />
        ) : (
          <GalleryImage image={image} />
        )}
      </a>
    </figure>
  );
}

/** Placeholders del estado loading: respetan el layout de columnas con alturas
 *  variadas para insinuar el masonry (decisión 4). */
function GallerySkeleton() {
  const heights = [220, 300, 180, 260, 200, 320, 240, 190, 280];

  return (
    <div class="gallery-columns" aria-hidden="true">
      {heights.map((h, i) => (
        <div
          key={i}
          class="mb-4 animate-pulse break-inside-avoid rounded-lg bg-surface-2"
          style={`height:${h}px`}
        />
      ))}
    </div>
  );
}

export default function GalleryGrid({ category, endpoint }) {
  // status: 'loading' | 'ready' | 'empty' | 'error'
  const [status, setStatus] = useState('loading');
  const [images, setImages] = useState([]);
  const lightboxRef = useRef(null);

  const load = useCallback(() => {
    let cancelled = false;
    setStatus('loading');

    fetch(endpoint, { headers: { Accept: 'application/json' } })
      .then((res) => {
        if (!res.ok) throw new Error(`HTTP ${res.status}`);
        return res.json();
      })
      .then((data) => {
        if (cancelled) return;
        const list = Array.isArray(data.images) ? data.images : [];
        setImages(list);
        setStatus(list.length === 0 ? 'empty' : 'ready');
      })
      .catch(() => {
        if (!cancelled) setStatus('error');
      });

    return () => {
      cancelled = true;
    };
  }, [endpoint]);

  useEffect(() => load(), [load]);

  // GLightbox se re-crea cada vez que `images` cambia (columns nuevas = <a
  // data-gallery> nuevos en el DOM; la instancia vieja no los conoce). Destruye
  // la anterior antes de crear la próxima — evita listeners duplicados.
  useEffect(() => {
    if (status !== 'ready') return undefined;

    lightboxRef.current?.destroy();
    lightboxRef.current = GLightbox({ selector: '[data-gallery="gallery-grid"]' });

    return () => lightboxRef.current?.destroy();
  }, [status, images]);

  if (status === 'loading') {
    return (
      <section class="gallery-grid container py-12" aria-busy="true">
        <GallerySkeleton />
      </section>
    );
  }

  if (status === 'error') {
    return (
      <section class="gallery-grid container py-16">
        <div class="mx-auto flex max-w-md flex-col items-center text-center">
          <AlertIcon class="mb-4 h-14 w-14 text-muted" />
          <h3 class="mb-2 font-serif text-2xl text-fg">Something went wrong</h3>
          <p class="mb-6 text-base font-light text-fg/70">
            We couldn't load the gallery. Please check your connection and try again.
          </p>
          <button
            type="button"
            onClick={load}
            class="btn btn-primary inline-flex items-center gap-2"
          >
            <RefreshIcon class="h-5 w-5" />
            Retry
          </button>
        </div>
      </section>
    );
  }

  if (status === 'empty') {
    return (
      <section class="gallery-grid container py-16">
        <div class="mx-auto flex max-w-md flex-col items-center text-center">
          <Info class="mb-4 h-14 w-14 text-muted" />
          <h3 class="mb-2 text-2xl text-fg">Gallery under construction</h3>
          <p class="text-base font-light text-fg/70">
            We're working to bring you inspiring {category} projects — check back soon!
          </p>
        </div>
      </section>
    );
  }

  return (
    <section class="gallery-grid container py-12">
      <div class="gallery-columns">
        {images.map((image, index) => (
          <GalleryCard key={image.src} image={image} index={index} />
        ))}
      </div>
    </section>
  );
}
