// App shell: router, nav, parallax bg, tweaks glue.

const { useState, useEffect, useRef, useMemo } = React;

// Color-grade presets — apply to CSS custom properties
const COLOR_GRADES = {
  golden: {
    overlay: 'linear-gradient(180deg, rgba(20,12,4,0.55) 0%, rgba(20,12,4,0.35) 40%, rgba(10,6,2,0.75) 100%)',
    tint:    'linear-gradient(135deg, rgba(212,140,60,0.10) 0%, rgba(90,40,10,0.00) 50%, rgba(30,10,2,0.20) 100%)',
    accent:  '#D4A547',
  },
  twilight: {
    overlay: 'linear-gradient(180deg, rgba(4,8,20,0.55) 0%, rgba(4,8,20,0.30) 40%, rgba(2,4,10,0.80) 100%)',
    tint:    'linear-gradient(135deg, rgba(60,100,200,0.14) 0%, rgba(10,20,60,0.00) 50%, rgba(4,6,20,0.22) 100%)',
    accent:  '#8FB4E0',
  },
  film: {
    overlay: 'linear-gradient(180deg, rgba(20,18,14,0.65) 0%, rgba(20,18,14,0.45) 40%, rgba(10,8,6,0.85) 100%)',
    tint:    'linear-gradient(135deg, rgba(160,140,110,0.06) 0%, rgba(40,32,22,0.00) 50%, rgba(20,16,10,0.22) 100%)',
    accent:  '#C0A878',
  },
  pastel: {
    overlay: 'linear-gradient(180deg, rgba(40,20,30,0.40) 0%, rgba(40,20,30,0.20) 40%, rgba(20,10,20,0.60) 100%)',
    tint:    'linear-gradient(135deg, rgba(232,180,188,0.12) 0%, rgba(120,80,120,0.00) 50%, rgba(80,40,60,0.18) 100%)',
    accent:  '#E8B4BC',
  },
};

// Parallax background with smooth rAF-driven lerp
const ParallaxBG = ({ behavior, gradeKey, photoIndex, onAccent }) => {
  const refA = useRef(null);
  const refB = useRef(null);

  // Active slot (0 = A showing, 1 = B showing)
  const [active, setActive] = useState(0);
  // Photo index showing in each slot
  const [slotA, setSlotA] = useState(photoIndex);
  const [slotB, setSlotB] = useState((photoIndex + 1) % PHOTOS.length);

  // Only cycle through photos that have a real image (not gradient placeholders)
  const realIndices = useMemo(
    () => PHOTOS.map((p, i) => (p.bg && !p.bg.startsWith('linear-gradient')) ? i : -1).filter(i => i >= 0),
    []
  );

  // Resolve a photo's background-stage URL (1600px WebP when real, null for gradients).
  const bgUrl = (p) => (p.bg && !p.bg.startsWith('linear-gradient')) ? p.bg : null;

  // Sync to route-driven photoIndex when it changes (e.g. nav change)
  const firstRouteSync = useRef(true);
  useEffect(() => {
    if (firstRouteSync.current) { firstRouteSync.current = false; return; }
    // On route change, crossfade to the route's chosen photo in the inactive slot
    const nextIdx = photoIndex;
    if (active === 0) { setSlotB(nextIdx); setActive(1); }
    else               { setSlotA(nextIdx); setActive(0); }
  }, [photoIndex]); // eslint-disable-line

  // Auto-cycle every 10s through real photos
  useEffect(() => {
    if (realIndices.length < 2) return;
    const interval = setInterval(() => {
      const current = active === 0 ? slotA : slotB;
      let pos = realIndices.indexOf(current);
      if (pos === -1) pos = 0;
      const nextReal = realIndices[(pos + 1) % realIndices.length];
      if (active === 0) { setSlotB(nextReal); setActive(1); }
      else               { setSlotA(nextReal); setActive(0); }
    }, 10000);
    return () => clearInterval(interval);
  }, [active, slotA, slotB, realIndices]);

  // Apply scroll / parallax / kenburns to BOTH layers
  useEffect(() => {
    const els = [refA.current, refB.current].filter(Boolean);
    if (!els.length) return;

    if (behavior === 'static') {
      els.forEach(el => { el.style.transform = 'translate3d(0,0,0) scale(1.04)'; });
      return;
    }

    if (behavior === 'parallax') {
      let targetY = 0, currentY = 0, raf, alive = true;
      const onScroll = () => { targetY = window.scrollY * 0.18; };
      const tick = () => {
        if (!alive) return;
        currentY += (targetY - currentY) * 0.14;
        if (Math.abs(targetY - currentY) < 0.05) currentY = targetY;
        const tr = `translate3d(0, ${currentY.toFixed(2)}px, 0) scale(1.04)`;
        els.forEach(el => { el.style.transform = tr; });
        raf = requestAnimationFrame(tick);
      };
      window.addEventListener('scroll', onScroll, { passive: true });
      onScroll();
      raf = requestAnimationFrame(tick);
      return () => { alive = false; cancelAnimationFrame(raf); window.removeEventListener('scroll', onScroll); };
    }
    if (behavior === 'kenburns') {
      let t0 = performance.now();
      let raf;
      const tick = (now) => {
        const t = (now - t0) / 1000;
        const s = 1.07 + Math.sin(t * 0.05) * 0.03;
        const tr = `scale(${s}) translate3d(${Math.sin(t*0.04)*10}px, ${Math.cos(t*0.03)*6}px, 0)`;
        els.forEach(el => { el.style.transform = tr; });
        raf = requestAnimationFrame(tick);
      };
      raf = requestAnimationFrame(tick);
      return () => cancelAnimationFrame(raf);
    }
  }, [behavior]);

  const g = COLOR_GRADES[gradeKey] || COLOR_GRADES.golden;

  const photoA = PHOTOS[slotA % PHOTOS.length];
  const photoB = PHOTOS[slotB % PHOTOS.length];
  const bgStr = (p) => {
    const u = bgUrl(p);
    return u ? `url("${u}")` : p.bg;
  };
  const bgA = bgStr(photoA);
  const bgB = bgStr(photoB);

  // Sample dominant color from whichever slot is currently showing
  const activePhoto = active === 0 ? photoA : photoB;
  const activeUrl = bgUrl(activePhoto);
  const activeIsReal = !!activeUrl;

  useEffect(() => {
    if (!onAccent) return;
    const sample = async () => {
      if (activeIsReal) {
        try {
          const img = new Image();
          img.crossOrigin = 'anonymous';
          img.src = activeUrl;
          await img.decode();
          const c = document.createElement('canvas');
          c.width = 24; c.height = 24;
          const ctx = c.getContext('2d');
          ctx.drawImage(img, 0, 0, 24, 24);
          const data = ctx.getImageData(0, 0, 24, 24).data;
          let r=0,g=0,b=0,n=0;
          for (let i=0; i<data.length; i+=4) {
            const R=data[i], G=data[i+1], B=data[i+2];
            const max=Math.max(R,G,B), min=Math.min(R,G,B);
            const sat = max === 0 ? 0 : (max-min)/max;
            const lum = (R+G+B)/3;
            if (lum > 20 && lum < 230) {
              const w = 1 + sat * 3;
              r += R*w; g += G*w; b += B*w; n += w;
            }
          }
          if (n > 0) { r/=n; g/=n; b/=n; }
          const max = Math.max(r,g,b), min = Math.min(r,g,b);
          if (max-min < 30) { // too muddy - push it
            r = Math.min(255, r*1.15); b = Math.max(0, b*0.85);
          }
          onAccent([Math.round(r), Math.round(g), Math.round(b)]);
        } catch (e) { /* fallback stays */ }
      } else {
        // Parse rough average from gradient string
        const matches = (activePhoto.bg || '').match(/#[0-9a-f]{6}/gi) || [];
        if (matches.length) {
          // use the middle stop (most representative)
          const mid = matches[Math.floor(matches.length/2)];
          const r = parseInt(mid.slice(1,3),16);
          const g = parseInt(mid.slice(3,5),16);
          const b = parseInt(mid.slice(5,7),16);
          onAccent([r,g,b]);
        }
      }
    };
    sample();
  }, [activePhoto.id, activeIsReal, onAccent]);

  return (
    <div className="bg-stage" aria-hidden="true">
      <div
        ref={refA}
        className="bg-img bg-img--fade"
        style={{
          backgroundImage: bgA,
          backgroundSize: 'cover',
          backgroundPosition: 'center',
          opacity: active === 0 ? 1 : 0,
        }}
      />
      <div
        ref={refB}
        className="bg-img bg-img--fade"
        style={{
          backgroundImage: bgB,
          backgroundSize: 'cover',
          backgroundPosition: 'center',
          opacity: active === 1 ? 1 : 0,
        }}
      />
      <div className="bg-grade" style={{ background: `${g.overlay}, ${g.tint}` }}/>
      <div className="bg-grain"/>
      <div className="bg-vignette"/>
    </div>
  );
};

// Navigation
const NavRail = ({ route, onNav }) => (
  <nav className="nav-rail glass">
    <div className="nav-rail__brand">LifeWithNate</div>
    {NAV.map(item => (
      <div key={item.id} className={'nav-rail__item ' + (route === item.id ? 'nav-rail__item--active' : '')} onClick={() => onNav(item.id)} title={item.label}>
        <Icon name={item.icon} size={19}/>
        <span className="nav-rail__label">{item.label}</span>
      </div>
    ))}
  </nav>
);

const NavTop = ({ route, onNav }) => (
  <GlassBox className="nav-top" pill>
    {NAV.map(item => (
      <div key={item.id} className={'nav-top__item ' + (route === item.id ? 'nav-top__item--active' : '')} onClick={() => onNav(item.id)}>{item.label}</div>
    ))}
  </GlassBox>
);

const NavMinimal = ({ route, onNav, onOpen, open }) => (
  <>
    <div style={{ position: 'fixed', top: 20, right: 20, zIndex: 55 }}>
      <GlassBox pill interactive onClick={() => onOpen(!open)} style={{ padding: '10px 18px', display: 'inline-flex', alignItems: 'center', gap: 10, fontFamily: 'var(--font-ui)', fontSize: 11, letterSpacing: '0.22em', textTransform: 'uppercase', color: 'var(--bone)' }}>
        <Icon name="menu" size={14}/>
        <span>Menu</span>
      </GlassBox>
    </div>
    {open && (
      <GlassBox style={{ position: 'fixed', top: 70, right: 20, zIndex: 55, padding: 14, minWidth: 220 }}>
        {NAV.map(item => (
          <div key={item.id} onClick={() => { onNav(item.id); onOpen(false); }} style={{
            padding: '12px 14px',
            fontFamily: 'var(--font-display)',
            fontSize: 12, letterSpacing: '0.22em', textTransform: 'uppercase',
            color: route === item.id ? 'var(--bone)' : 'var(--bone-soft)',
            cursor: 'pointer', borderRadius: 8,
            display: 'flex', alignItems: 'center', gap: 12,
          }}>
            <Icon name={item.icon} size={14}/> {item.label}
          </div>
        ))}
      </GlassBox>
    )}
  </>
);

// ───────── App ─────────
const App = () => {
  const [route, setRoute] = useState(() => localStorage.getItem('lwn:route') || 'home');
  const [article, setArticle] = useState(null);
  const [viewer, setViewer] = useState(null); // {index, photos}
  const [tweaks] = useState(() => ({ ...window.TWEAKS }));
  const [minimalMenuOpen, setMinimalMenuOpen] = useState(false);

  // persist route
  useEffect(() => { localStorage.setItem('lwn:route', route); }, [route]);

  // apply glass intensity as CSS var
  useEffect(() => {
    document.documentElement.style.setProperty('--glass-intensity', String(tweaks.glassIntensity));
    const g = COLOR_GRADES[tweaks.colorGrade] || COLOR_GRADES.golden;
    // Default accent from grade (used until image sampled)
    const hex = g.accent.replace('#','');
    const r = parseInt(hex.slice(0,2),16), gg = parseInt(hex.slice(2,4),16), b = parseInt(hex.slice(4,6),16);
    document.documentElement.style.setProperty('--accent', g.accent);
    document.documentElement.style.setProperty('--accent-rgb', `${r}, ${gg}, ${b}`);
  }, [tweaks.glassIntensity, tweaks.colorGrade]);

  // Smoothly apply extracted accent from bg image
  const applyAccent = React.useCallback(([r,g,b]) => {
    document.documentElement.style.setProperty('--accent', `rgb(${r}, ${g}, ${b})`);
    document.documentElement.style.setProperty('--accent-rgb', `${r}, ${g}, ${b}`);
  }, []);

  // ─── Scroll reveal: auto-observe .reveal + .reveal-stagger as they enter view ───
  useEffect(() => {
    const io = new IntersectionObserver((entries) => {
      entries.forEach(entry => {
        if (entry.isIntersecting) {
          entry.target.classList.add('is-in');
          io.unobserve(entry.target);
        }
      });
    }, { rootMargin: '-8% 0px -12% 0px', threshold: 0.05 });

    const scan = () => {
      document.querySelectorAll('.reveal:not(.is-in), .reveal-stagger:not(.is-in)').forEach(el => io.observe(el));
    };
    scan();
    // re-scan after route/article change has rendered
    const t1 = setTimeout(scan, 80);
    const t2 = setTimeout(scan, 300);
    return () => { io.disconnect(); clearTimeout(t1); clearTimeout(t2); };
  }, [route, article]);

  // ─── Gentle foreground drift: slow lerp-driven translate driven by scroll ───
  // Writes to --drift-y CSS var, consumed by .drift elements. Pure rAF, no CSS transition.
  useEffect(() => {
    let raf, alive = true;
    let targetY = 0, currentY = 0;
    const onScroll = () => { targetY = window.scrollY; };
    const tick = () => {
      if (!alive) return;
      // Critically-damped smoothing factor — 0.14 feels fluid without lag
      currentY += (targetY - currentY) * 0.14;
      // Snap to target when very close to avoid sub-pixel jitter
      if (Math.abs(targetY - currentY) < 0.05) currentY = targetY;
      // Drift: foreground rises ~3.5% slower than page scroll
      const drift = (-currentY * 0.035).toFixed(2);
      document.documentElement.style.setProperty('--drift-y', `${drift}px`);
      raf = requestAnimationFrame(tick);
    };
    window.addEventListener('scroll', onScroll, { passive: true });
    onScroll();
    raf = requestAnimationFrame(tick);
    return () => { alive = false; cancelAnimationFrame(raf); window.removeEventListener('scroll', onScroll); };
  }, []);

  // Background photo rotates by route (for cinematic variety)
  const routePhoto = useMemo(() => {
    const map = { home: 1, news: 0, trips: 2, gallery: 4, guides: 7 };
    return map[route] ?? 1;
  }, [route]);

  const handleNav = (r) => { setRoute(r); setArticle(null); window.scrollTo(0, 0); };
  const openArticle = (a) => { setArticle(a); window.scrollTo(0, 0); };
  const openPhoto  = (idx, photos) => setViewer({ index: idx, photos });

  const navStyle = tweaks.navStyle;
  const useTopPadding = navStyle === 'top' || navStyle === 'minimal';

  return (
    <>
      <ParallaxBG behavior={tweaks.backgroundBehavior} gradeKey={tweaks.colorGrade} photoIndex={routePhoto} onAccent={applyAccent}/>

      {/* Brand mark always */}
      <div className="brand-mark" onClick={() => handleNav('home')}>
        Life<span>With</span>Nate
      </div>

      {/* Navigation variants */}
      {navStyle === 'rail' && <NavRail route={route} onNav={handleNav}/>}
      {navStyle === 'top' && <NavTop route={route} onNav={handleNav}/>}
      {navStyle === 'minimal' && <NavMinimal route={route} onNav={handleNav} open={minimalMenuOpen} onOpen={setMinimalMenuOpen}/>}

      <main className={'page ' + (useTopPadding ? 'page--top-nav' : '')}>
        {article ? (
          <ArticleView article={article} onBack={() => setArticle(null)} />
        ) : (
          <>
            {route === 'home'    && <HomePage onNav={handleNav} onOpenArticle={openArticle} onOpenGallery={() => handleNav('gallery')}/>}
            {route === 'news'    && <NewsPage onOpenArticle={openArticle}/>}
            {route === 'trips'   && <TripsPage onOpenTrip={(t) => { /* placeholder: treat as news-style article */ openArticle({ ...t, eyebrow: 'Trip Report', category: t.date, readTime: `${t.nights} nights`, photo: t.hero, dek: t.subtitle }); }}/>}
            {route === 'gallery' && <GalleryPage onOpenPhoto={openPhoto}/>}
            {route === 'guides'  && <GuidesPage onOpenArticle={openArticle}/>}
          </>
        )}
      </main>

      {viewer && (
        <FullscreenViewer photos={viewer.photos} index={viewer.index} onClose={() => setViewer(null)} onIndex={(i) => setViewer({ ...viewer, index: i })} />
      )}

    </>
  );
};

ReactDOM.createRoot(document.getElementById('root')).render(<App/>);
