import { useEffect, useLayoutEffect, useRef, useState } from 'preact/hooks';
import { animate, FADE_IN, prefersReducedMotion } from './motion.js';

/**
 * El loader sobre la foto, port del de Apex (`resources/js/ai/loader.jsx` +
 * `.three-body*` de ai-survey.css): la foto queda de fondo, una capa grisácea con
 * `backdrop-filter: blur` la deja ver a medias, y encima el indicador «three-body»
 * con un texto que cambia cada ~6-7 s con slide + fade, para que el visitante no
 * se desespere. El último mensaje de la secuencia se queda fijo.
 *
 * A diferencia de Apex, NO promete tiempo («about a minute»): no hay una medición
 * publicable.
 *
 * `override` reemplaza la secuencia (ej. «Downloading your design» entre el
 * COMPLETED y la imagen ya en memoria). Al desactivarse se va con un fade-out
 * simple y recién ahí se desmonta (`onHidden`).
 *
 * @param {{
 *   active: boolean,
 *   messages: string[],
 *   override?: string | null,
 *   onHidden?: () => void,
 * }} props
 */
export default function Loader({ active, messages, override = null, onHidden }) {
  const [mounted, setMounted] = useState(active);
  const [index, setIndex] = useState(0);
  const containerRef = useRef(/** @type {HTMLDivElement | null} */ (null));
  const textRef = useRef(/** @type {HTMLParagraphElement | null} */ (null));
  const hidden = useRef(onHidden);
  hidden.current = onHidden;

  // Encender: arranca la secuencia desde el principio.
  useEffect(() => {
    if (active) {
      // Si se estaba yendo, el fade-out (con `fill: forwards`) no puede quedar.
      containerRef.current?.getAnimations().forEach((animation) => animation.cancel());
      setMounted(true);
      setIndex(0);
      return undefined;
    }
    if (!mounted) return undefined;
    // Apagar: fade-out simple y recién después desmontar.
    let cancelled = false;
    const duration = prefersReducedMotion() ? 150 : 1000;
    const animation = containerRef.current?.animate([{ opacity: 1 }, { opacity: 0 }], {
      duration,
      easing: 'ease',
      fill: 'forwards',
    });
    const done = () => {
      if (cancelled) return;
      setMounted(false);
      hidden.current?.();
    };
    if (animation) animation.finished.then(done, done);
    else done();
    return () => {
      cancelled = true;
    };
  }, [active]);

  // Rotar los mensajes mientras está activo y sin texto propio.
  useEffect(() => {
    if (!active || override || index >= messages.length - 1) return undefined;
    const timer = setTimeout(() => setIndex((current) => current + 1), ROTATE_MS);
    return () => clearTimeout(timer);
  }, [active, override, index, messages.length]);

  const text = override ?? messages[Math.min(index, messages.length - 1)] ?? '';

  // Cada texto nuevo entra desde abajo (slide + fade); con menos movimiento, solo fade.
  useLayoutEffect(() => {
    animate(
      textRef.current,
      [
        { opacity: 0, transform: 'translateY(60%)' },
        { opacity: 1, transform: 'none' },
      ],
      { duration: 700, reduced: FADE_IN },
    );
  }, [text]);

  if (!mounted) return null;

  return (
    <div ref={containerRef} class="ai-loader" role="status" aria-live="polite">
      <div class="ai-three-body" aria-hidden="true">
        <div class="ai-three-body__dot" />
        <div class="ai-three-body__dot" />
        <div class="ai-three-body__dot" />
      </div>
      <div class="mt-8 overflow-hidden">
        <p ref={textRef} key={text} class="font-serif text-xl text-cream sm:text-2xl">
          {text}
        </p>
      </div>
    </div>
  );
}

/** Cada cuánto cambia el mensaje (Apex: 6-7 s). */
const ROTATE_MS = 6500;
