/**
 * ArtworkNavigator
 *
 * Behavior-only: prev/next navigation WITHOUT page reload.
 * Features: fetch + history.pushState, Image() preloading, keyboard (← →/F), touch swipe.
 * UI arrows are rendered by ArtworkHero via onReady callback.
 */
import { useState, useEffect, useCallback, useRef } from 'react';
import { useNavContext } from '../../lib/useNavContext';

const preloadCache = new Set();

function preloadImage(src) {
  if (!src || preloadCache.has(src)) return;
  preloadCache.add(src);
  const img = new Image();
  img.src = src;
}

export default function ArtworkNavigator({ artworkId, onNavigate, onOpenViewer, onReady }) {
  const { getNeighbors } = useNavContext(artworkId);
  const [neighbors, setNeighbors] = useState({ prevId: null, nextId: null, prevUrl: null, nextUrl: null });

  // Refs so navigate/keyboard/swipe callbacks are stable (no dep on state values)
  const navigatingRef = useRef(false);
  const neighborsRef = useRef(neighbors);
  const onNavigateRef = useRef(onNavigate);
  const onOpenViewerRef = useRef(onOpenViewer);
  const onReadyRef = useRef(onReady);

  // Keep refs in sync with latest props/state
  useEffect(() => { neighborsRef.current = neighbors; }, [neighbors]);
  useEffect(() => { onNavigateRef.current = onNavigate; }, [onNavigate]);
  useEffect(() => { onOpenViewerRef.current = onOpenViewer; }, [onOpenViewer]);
  useEffect(() => { onReadyRef.current = onReady; }, [onReady]);

  const touchStartX = useRef(null);
  const touchStartY = useRef(null);

  // Resolve neighbors on mount / artworkId change
  useEffect(() => {
    let cancelled = false;
    getNeighbors().then((n) => {
      if (cancelled) return;
      setNeighbors(n);
      [n.prevId, n.nextId].forEach((id) => {
        if (!id) return;
        fetch(`/api/artworks/${id}/page`, { headers: { Accept: 'application/json' } })
          .then((r) => r.ok ? r.json() : null)
          .then((data) => {
            if (!data) return;
            const imgUrl = data.thumbs?.lg?.url || data.thumbs?.md?.url;
            if (imgUrl) preloadImage(imgUrl);
          })
          .catch(() => {});
      });
    });
    return () => { cancelled = true; };
  }, [artworkId, getNeighbors]);

  // Stable navigate — reads state via refs, never recreated
  const navigate = useCallback(async (targetId, targetUrl) => {
    if (!targetId && !targetUrl) return;
    if (navigatingRef.current) return;

    const fallbackUrl = targetUrl || `/art/${targetId}`;
    const currentOnNavigate = onNavigateRef.current;

    if (!currentOnNavigate || !targetId) {
      window.location.href = fallbackUrl;
      return;
    }

    navigatingRef.current = true;
    try {
      const res = await fetch(`/api/artworks/${targetId}/page`, {
        headers: { Accept: 'application/json' },
      });
      if (!res.ok) throw new Error(`HTTP ${res.status}`);
      const data = await res.json();

      const canonicalSlug =
        (data.slug || data.title || String(data.id))
          .toString()
          .toLowerCase()
          .replace(/[^a-z0-9]+/g, '-')
          .replace(/^-|-$/g, '') || String(data.id);

      history.pushState({ artworkId: data.id }, '', `/art/${data.id}/${canonicalSlug}`);
      document.title = `${data.title} | Skinbase`;

      currentOnNavigate(data);
    } catch {
      window.location.href = fallbackUrl;
    } finally {
      navigatingRef.current = false;
    }
  }, []); // stable — accesses everything via refs

  // Notify parent whenever neighbors change
  useEffect(() => {
    const hasPrev = Boolean(neighbors.prevId || neighbors.prevUrl);
    const hasNext = Boolean(neighbors.nextId || neighbors.nextUrl);
    onReadyRef.current?.({
      hasPrev,
      hasNext,
      navigatePrev: hasPrev ? () => navigate(neighbors.prevId, neighbors.prevUrl) : null,
      navigateNext: hasNext ? () => navigate(neighbors.nextId, neighbors.nextUrl) : null,
    });
  }, [neighbors, navigate]);

  // Sync browser back/forward
  useEffect(() => {
    function onPop() { window.location.reload(); }
    window.addEventListener('popstate', onPop);
    return () => window.removeEventListener('popstate', onPop);
  }, []);

  // Keyboard: ← → navigate, F fullscreen
  useEffect(() => {
    function onKey(e) {
      const tag = e.target?.tagName?.toLowerCase?.() ?? '';
      if (['input', 'textarea', 'select'].includes(tag) || e.target?.isContentEditable) return;
      const n = neighborsRef.current;
      if (e.key === 'ArrowLeft') { e.preventDefault(); navigate(n.prevId, n.prevUrl); }
      else if (e.key === 'ArrowRight') { e.preventDefault(); navigate(n.nextId, n.nextUrl); }
      else if ((e.key === 'f' || e.key === 'F') && !e.ctrlKey && !e.metaKey) { onOpenViewerRef.current?.(); }
    }
    window.addEventListener('keydown', onKey);
    return () => window.removeEventListener('keydown', onKey);
  }, [navigate]); // navigate is stable so this only runs once

  // Touch swipe
  useEffect(() => {
    function onTouchStart(e) {
      touchStartX.current = e.touches[0].clientX;
      touchStartY.current = e.touches[0].clientY;
    }
    function onTouchEnd(e) {
      if (touchStartX.current === null) return;
      const dx = e.changedTouches[0].clientX - touchStartX.current;
      const dy = e.changedTouches[0].clientY - touchStartY.current;
      touchStartX.current = null;
      if (Math.abs(dx) > 50 && Math.abs(dy) < 80) {
        const n = neighborsRef.current;
        if (dx > 0) navigate(n.prevId, n.prevUrl);
        else navigate(n.nextId, n.nextUrl);
      }
    }
    window.addEventListener('touchstart', onTouchStart, { passive: true });
    window.addEventListener('touchend', onTouchEnd, { passive: true });
    return () => {
      window.removeEventListener('touchstart', onTouchStart);
      window.removeEventListener('touchend', onTouchEnd);
    };
  }, [navigate]); // stable

  return null;
}
