import { useEffect } from 'react'
import { Outlet, useLocation } from 'react-router-dom'
import Lenis from 'lenis'
import { gsap } from 'gsap'
import { ScrollTrigger } from 'gsap/ScrollTrigger'
import Header from './Header'
import { Footer } from './Footer'
import { LangProvider, useLang } from '@/context/LangContext'
import { useAuth, trackActivity } from '@/context/AuthContext'

gsap.registerPlugin(ScrollTrigger)

/* ── Activity tracker (logged-in members only) ──
   Logs each page view on route change, and each click on a meaningful element
   (links, buttons, or anything with data-track). The API is memberRequired, so
   anonymous visitors are never recorded. */
function ActivityTracker() {
  const { member } = useAuth()
  const { pathname, search } = useLocation()

  // page views
  useEffect(() => {
    if (!member) return
    trackActivity('view', pathname + search)
  }, [member, pathname, search])

  // element clicks
  useEffect(() => {
    if (!member) return
    const onClick = (e: MouseEvent) => {
      const el = (e.target as Element)?.closest('a,button,[role="button"],[data-track]')
      if (!el) return
      const label =
        el.getAttribute('data-track') ||
        el.getAttribute('aria-label') ||
        (el.textContent || '').trim().replace(/\s+/g, ' ').slice(0, 80) ||
        el.tagName.toLowerCase()
      trackActivity('click', window.location.pathname, label)
    }
    document.addEventListener('click', onClick, true)
    return () => document.removeEventListener('click', onClick, true)
  }, [member])

  return null
}

/* ── Persian-digit localizer ──────────────────────────────────────────
   In fa, every Latin digit shown anywhere on the page is rendered as a
   Persian digit (۰-۹). Skips inputs, code, SVG charts and [data-latin]. */
const FA_DIGITS = ['۰', '۱', '۲', '۳', '۴', '۵', '۶', '۷', '۸', '۹']
const FA_SKIP = 'script,style,input,textarea,code,pre,svg,[contenteditable],[data-latin]'
function persianizeNode(root: Node) {
  if (root.nodeType === Node.TEXT_NODE) {
    const t = root as Text
    const v = t.nodeValue
    if (!v || !/[0-9]/.test(v)) return
    const p = t.parentElement
    if (p && p.closest(FA_SKIP)) return
    t.nodeValue = v.replace(/[0-9]/g, (d) => FA_DIGITS[+d])
    return
  }
  if (root.nodeType !== Node.ELEMENT_NODE) return
  const el = root as Element
  if (el.matches(FA_SKIP) || el.closest(FA_SKIP)) return
  const walker = document.createTreeWalker(el, NodeFilter.SHOW_TEXT, {
    acceptNode(n) {
      const par = (n as Text).parentElement
      if (!par || par.closest(FA_SKIP)) return NodeFilter.FILTER_REJECT
      return /[0-9]/.test(n.nodeValue || '') ? NodeFilter.FILTER_ACCEPT : NodeFilter.FILTER_REJECT
    },
  })
  const nodes: Text[] = []
  let n: Node | null
  while ((n = walker.nextNode())) nodes.push(n as Text)
  for (const t of nodes) {
    const v = t.nodeValue!
    t.nodeValue = v.replace(/[0-9]/g, (d) => FA_DIGITS[+d])
  }
}

function DigitLocalizer() {
  const { lang } = useLang()
  const { pathname } = useLocation()
  useEffect(() => {
    if (lang !== 'fa') return
    const raf = requestAnimationFrame(() => persianizeNode(document.body))
    // late-loaded / async content (fetched cards, tab switches) gets converted as it mounts
    const obs = new MutationObserver((muts) => {
      for (const m of muts) m.addedNodes.forEach((n) => persianizeNode(n))
    })
    obs.observe(document.body, { childList: true, subtree: true })
    return () => { cancelAnimationFrame(raf); obs.disconnect() }
  }, [lang, pathname])
  return null
}

/* Lenis smooth scroll synced to the GSAP ticker + ScrollTrigger (joinspread.app recipe).
   One shared clock means scroll-driven animations never stutter against the smoothing. */
function useSmoothScroll() {
  const { pathname } = useLocation()

  useEffect(() => {
    if (window.matchMedia('(prefers-reduced-motion: reduce)').matches) return

    // Touch devices (iOS/Android) have excellent NATIVE momentum scrolling.
    // Running Lenis JS smoothing on top of it = double-smoothing → heavy/laggy feel.
    // So: smooth only on desktop pointers; let touch use native scroll.
    const isTouch = window.matchMedia('(pointer: coarse)').matches

    const lenis = new Lenis({
      duration: 1.2,
      easing: (t) => Math.min(1, 1.001 - Math.pow(2, -10 * t)), // exponential ease-out
      smoothWheel: !isTouch,   // desktop wheel: smooth
      syncTouch: false,        // touch: hands off to native momentum (no JS smoothing)
    })

    // drive ScrollTrigger from Lenis
    lenis.on('scroll', ScrollTrigger.update)

    // run Lenis off GSAP's ticker (single RAF clock), lag smoothing off for tight sync
    const onTick = (time: number) => lenis.raf(time * 1000)
    gsap.ticker.add(onTick)
    gsap.ticker.lagSmoothing(0)

    return () => {
      lenis.off('scroll', ScrollTrigger.update)
      gsap.ticker.remove(onTick)
      lenis.destroy()
    }
  }, [])

  // reset scroll on route change (Lenis owns scroll position)
  useEffect(() => {
    window.scrollTo({ top: 0, behavior: 'auto' })
    ScrollTrigger.refresh()
  }, [pathname])
}

export default function RootLayout() {
  useSmoothScroll()
  return (
    <LangProvider>
      <DigitLocalizer />
      <ActivityTracker />
      <div style={{ minHeight: '100dvh', display: 'flex', flexDirection: 'column' }}>
        <Header />
        <main style={{ flex: 1 }}>
          <Outlet />
        </main>
        <Footer />
      </div>
    </LangProvider>
  )
}
