"use client";

import React, { useRef, useEffect, useState, useCallback } from 'react'
import Experience from './components/Experience'
import ScrollRig from './components/ScrollRig'
import Navbar from './components/ui/Navbar'
import FirstMile from './components/sections/FirstMile'
import MidMile from './components/sections/MidMile'
import LastMile from './components/sections/LastMile'
import Promise from './components/sections/Promise'
import { useSceneStore } from './store/useSceneStore'
import { useDeviceCaps } from './utils/deviceTier'
import './styles/experience.css'

import Lenis from 'lenis'
import gsap from 'gsap'
import { ScrollTrigger } from 'gsap/ScrollTrigger'

gsap.registerPlugin(ScrollTrigger)

/**
 * Experience3D — the scroll-driven 3D logistics story embedded in the How It
 * Works page.
 *
 * PERF refactor (this pass):
 *  1. Device tiering. `useDeviceCaps()` classifies desktop / tablet / mobile and
 *     a `fallback` flag. The tier flows into the Canvas (dpr/shadows/AA),
 *     Scene3D (LOD visibility), and ScrollRig (scroll length).
 *  2. Static fallback. Reduced-motion / no-WebGL / very-low-memory devices get a
 *     poster instead of a live WebGL scene.
 *  3. No per-frame React renders at this level. This component no longer
 *     subscribes to `scrollProgress`; the end-of-scroll canvas fade is applied
 *     imperatively, and the story overlays live in <StorySections>, which only
 *     re-renders when a section boolean flips.
 *  4. Touch-aware smooth scroll. Lenis runs on desktop only (driven by a
 *     dedicated rAF, not gsap.ticker; syncTouch off). Touch devices use native
 *     momentum scrolling — emulated inertia on a heavy WebGL page is the main
 *     cause of mobile/tablet scroll lag.
 */

/**
 * Story text panels. Isolated so its boolean subscriptions don't re-render the
 * whole experience: each selector returns a boolean, so React only re-renders
 * this small component when a section actually enters/leaves its range.
 */
function StorySections() {
  // First Mile is active from the very top (progress 0) so its card is visible
  // the instant the user enters the section — no scroll required.
  const firstActive = useSceneStore((s) => s.scrollProgress < 0.14)
  const midActive = useSceneStore((s) => s.scrollProgress >= 0.38 && s.scrollProgress < 0.50)
  const lastActive = useSceneStore((s) => s.scrollProgress >= 0.78 && s.scrollProgress < 0.875)
  const promiseActive = useSceneStore((s) => s.scrollProgress >= 0.90)

  return (
    <div className="sections-overlay-container">
      <FirstMile active={firstActive} />
      <MidMile active={midActive} />
      <LastMile active={lastActive} />
      <Promise active={promiseActive} />
    </div>
  )
}

/** Branded loading state shown over the stage while the GLB scene loads. Never
 *  a blank canvas — fades out once the scene signals ready. */
function BrandedLoader({ hidden }) {
  return (
    <div
      className="dm-hiw-3d-loader"
      aria-hidden={hidden}
      style={{
        position: 'absolute',
        inset: 0,
        zIndex: 50,
        display: 'flex',
        flexDirection: 'column',
        alignItems: 'center',
        justifyContent: 'center',
        gap: '18px',
        background: 'linear-gradient(180deg, #f5f5f7 0%, #e9edf2 100%)',
        opacity: hidden ? 0 : 1,
        pointerEvents: hidden ? 'none' : 'auto',
        transition: 'opacity 0.6s ease',
      }}
    >
      <span
        style={{
          width: 30,
          height: 30,
          borderRadius: '50%',
          border: '3px solid rgba(192,18,39,0.18)',
          borderTopColor: '#c01227',
          animation: 'dm-hiw-spin 0.8s linear infinite',
        }}
      />
      <span style={{ fontWeight: 600, letterSpacing: '0.01em', color: '#1f1f1f', fontSize: '0.95rem' }}>
        Loading Doormile Experience…
      </span>
      <style>{`@keyframes dm-hiw-spin{to{transform:rotate(360deg)}}`}</style>
    </div>
  )
}

/** Lightweight poster shown when a live scene isn't appropriate/possible. */
function StaticFallback() {
  return (
    <section
      className="dm-hiw-3d-fallback"
      style={{
        position: 'relative',
        minHeight: '70vh',
        display: 'flex',
        alignItems: 'flex-end',
        background:
          'linear-gradient(180deg, #eef1f5 0%, #dfe5ec 55%, #cfd7e0 100%)',
        overflow: 'hidden',
      }}
    >
      {/* Optional poster image; if it 404s we keep the gradient + caption. */}
      <img
        src="/images/home2-banner-1.webp"
        alt="Doormile delivery journey — first mile to last mile"
        loading="lazy"
        decoding="async"
        onError={(e) => {
          e.currentTarget.style.display = 'none'
        }}
        style={{ position: 'absolute', inset: 0, width: '100%', height: '100%', objectFit: 'cover' }}
      />
      <div style={{ position: 'relative', padding: '2rem clamp(1rem, 5vw, 4rem)', maxWidth: 720 }}>
        <p style={{ fontWeight: 700, fontSize: 'clamp(1.25rem, 3vw, 2rem)', lineHeight: 1.2, margin: 0 }}>
          From first mile to last mile, every delivery tracked.
        </p>
      </div>
    </section>
  )
}

export default function Experience3D() {
  const setLenis = useSceneStore((state) => state.setLenis)
  const caps = useDeviceCaps() // null until mounted on the client

  const containerRef = useRef(null)
  const canvasWrapperRef = useRef(null)
  const [pinState, setPinState] = useState('before')
  const [mountScene, setMountScene] = useState(false)
  const [sceneReady, setSceneReady] = useState(false)

  // Stable callback handed to the in-Canvas readiness signal.
  const handleSceneReady = useCallback(() => setSceneReady(true), [])

  const tier = caps?.tier ?? 'desktop'
  const useFallback = caps?.fallback ?? false
  const isTouch = caps?.isTouch ?? false
  const liveScene = caps != null && !useFallback

  // Defer mounting the WebGL Canvas until the section nears the viewport.
  useEffect(() => {
    if (!liveScene) return
    const el = containerRef.current
    if (!el) return
    const io = new IntersectionObserver(
      (entries) => {
        if (entries.some((e) => e.isIntersecting)) {
          setMountScene(true)
          io.disconnect()
        }
      },
      { rootMargin: '200% 0px' }, // mount well before it scrolls into view
    )
    io.observe(el)
    return () => io.disconnect()
  }, [liveScene])

  // (ScrollTrigger refreshing is owned by ScrollRig now — it refreshes on the
  // next frame, on every layout settle via ResizeObserver/fonts.ready, and again
  // when `ready` flips true. No arbitrary timeouts.)

  // Smooth scroll — DESKTOP ONLY. Touch devices keep native momentum (native is
  // smoother than emulated inertia on a heavy WebGL page, and avoids the
  // touch-scroll lag). Driven by a dedicated rAF rather than gsap.ticker.
  useEffect(() => {
    if (!liveScene || isTouch) return

    const lenis = new Lenis({
      duration: 1.2,
      lerp: 0.08,
      syncTouch: false, // never emulate touch inertia
    })
    setLenis(lenis)
    lenis.on('scroll', ScrollTrigger.update)

    let rafId
    const raf = (time) => {
      lenis.raf(time)
      rafId = requestAnimationFrame(raf)
    }
    rafId = requestAnimationFrame(raf)
    ScrollTrigger.refresh()

    return () => {
      cancelAnimationFrame(rafId)
      lenis.destroy()
      setLenis(null)
    }
  }, [liveScene, isTouch, setLenis])

  // End-of-scroll canvas fade — applied imperatively so it never triggers a
  // React render. Subscribes to the store but only touches the DOM on flip.
  useEffect(() => {
    if (!mountScene) return
    const el = canvasWrapperRef.current
    if (!el) return
    let lastDim = null
    const apply = (p) => {
      const dim = p >= 0.92
      if (dim !== lastDim) {
        lastDim = dim
        el.style.opacity = dim ? '0.85' : '1'
      }
    }
    apply(useSceneStore.getState().scrollProgress)
    const unsub = useSceneStore.subscribe((s) => apply(s.scrollProgress))
    return unsub
  }, [mountScene])

  // 3D references shared between R3F and the GSAP scroll system.
  const truckRef = useRef(null)
  const wheelRefs = React.useMemo(
    () => [{ current: null }, { current: null }, { current: null }, { current: null }],
    [],
  )
  // Kept for API compatibility (Scene3D no longer wires these; dashboard refs
  // were never attached in the generated model — the animation is a no-op).
  const dashboardRefs = React.useMemo(
    () => ({ bars: [], floorBars: [], pieQuarters: [] }),
    [],
  )

  // Pre-mount (caps unknown) / fallback: render a reserved placeholder or poster.
  if (caps == null) {
    return <div ref={containerRef} className="dm-hiw-3d" style={{ minHeight: '100vh' }} aria-hidden />
  }
  if (useFallback) {
    return <StaticFallback />
  }

  return (
    <div ref={containerRef} className={`dm-hiw-3d is-${pinState}`}>
      <div className="dm-hiw-3d-stage">
        <div
          ref={canvasWrapperRef}
          className="canvas-wrapper"
          style={{ transition: 'opacity 0.8s cubic-bezier(0.16, 1, 0.3, 1)' }}
        >
          {mountScene && (
            <Experience
              truckRef={truckRef}
              wheelRefs={wheelRefs}
              dashboardRefs={dashboardRefs}
              tier={tier}
              onReady={handleSceneReady}
            />
          )}
        </div>

        {/* Branded loader while the GLB loads — no blank canvas. Mounts with the
            Canvas, fades out the moment the scene is ready. */}
        {mountScene && <BrandedLoader hidden={sceneReady} />}

        <Navbar />
        <StorySections />
      </div>

      <ScrollRig dashboardRefs={dashboardRefs} onPinState={setPinState} tier={tier} ready={sceneReady} />
    </div>
  )
}
