"use client"

import { useState, useEffect, useRef, useContext, type KeyboardEvent } from "react"
import { useLocale } from "next-intl"
import Image from "next/image"
import {
  motion, AnimatePresence, useReducedMotion,
  useMotionValue, useTransform, animate,
} from "framer-motion"
import { useInView } from "react-intersection-observer"
import { ContexteRevelationPanneau } from "@/lib/panel-reveal"
import {
  GraduationCap, Users, Leaf, HeartPulse, Trophy,
  type LucideIcon,
} from "lucide-react"

/**
 * « Nos axes d'engagement », Roue d'engagement (thème clair, intégrée à la page).
 * Les cinq axes sont montés sur une roue qui tourne autour du noyau « Fondation SAR ».
 * Un cran de lecture fixe, à 9 h face à la fiche, reçoit l'axe courant : la roue tourne
 * d'un cran, l'axe vient s'y caler, et sa fiche se déplie alors sur la gauche pendant
 * 3,4 s avant que la roue reprenne sa rotation. Survol = pause ; clic = rotation directe
 * vers l'axe choisi (par le plus court chemin).
 * Repli mobile en noyau + liste accordéon. 100% SVG/DOM, respecte prefers-reduced-motion.
 */

type Axis = {
  Icon: LucideIcon
  titleFr: string
  titleEn: string
  descFr: string
  descEn: string
  actionsFr: string[]
  actionsEn: string[]
}

const AXES: Axis[] = [
  {
    Icon: GraduationCap,
    titleFr: "Éducation",
    titleEn: "Education",
    descFr: "Renforcer l'accès à une éducation de qualité pour les jeunes, en particulier dans les zones défavorisées.",
    descEn: "Strengthening access to quality education for young people, especially in underserved areas.",
    actionsFr: [
      "Construction et rénovation d'écoles",
      "Bourses scolaires et universitaires",
      "Soutien aux centres de formation",
      "Mentorat et orientation professionnelle",
    ],
    actionsEn: [
      "Building and renovating schools",
      "School and university scholarships",
      "Support to training centres",
      "Mentoring and career guidance",
    ],
  },
  {
    Icon: Users,
    titleFr: "Développement communautaire",
    titleEn: "Community development",
    descFr: "Améliorer les conditions de vie des communautés riveraines du site de raffinage.",
    descEn: "Improving the living conditions of communities around the refining site.",
    actionsFr: [
      "Infrastructures de base (eau, santé, école)",
      "Campagnes de dépistage",
      "Soutien aux groupements de femmes",
      "Lutte contre la malnutrition infantile",
    ],
    actionsEn: [
      "Basic infrastructure (water, health, schools)",
      "Screening campaigns",
      "Support to women's groups",
      "Fighting child malnutrition",
    ],
  },
  {
    Icon: Leaf,
    titleFr: "Développement durable",
    titleEn: "Sustainable development",
    descFr: "Préserver l'environnement et lutter contre les effets du changement climatique.",
    descEn: "Protecting the environment and tackling the effects of climate change.",
    actionsFr: [
      "Reboisement (50 ha) et restauration d'écosystèmes",
      "Gestion et recyclage des déchets",
      "Agriculture durable",
      "Énergies renouvelables (solaire)",
    ],
    actionsEn: [
      "Reforestation (50 ha) and ecosystem restoration",
      "Waste management and recycling",
      "Sustainable agriculture",
      "Renewable energy (solar)",
    ],
  },
  {
    Icon: HeartPulse,
    titleFr: "Santé",
    titleEn: "Health",
    descFr: "Promouvoir le bien-être des communautés par l'accès à des soins de qualité.",
    descEn: "Promoting community well-being through access to quality healthcare.",
    actionsFr: [
      "Construction et équipement de centres de santé",
      "Centre médico-social de la SAR",
      "Campagnes médicales mobiles (vaccinations, dépistages)",
    ],
    actionsEn: [
      "Building and equipping health centres",
      "SAR medico-social centre",
      "Mobile medical campaigns (vaccinations, screenings)",
    ],
  },
  {
    Icon: Trophy,
    titleFr: "Sport",
    titleEn: "Sport",
    descFr: "Promouvoir le sport et l'inclusion par des infrastructures et le soutien aux associations.",
    descEn: "Promoting sport and inclusion through facilities and support to clubs.",
    actionsFr: [
      "Programme sport-santé pour la jeunesse",
      "Réhabilitation d'infrastructures sportives",
      "Appui aux associations sportives (ASC)",
      "Inclusion des personnes en situation de handicap",
    ],
    actionsEn: [
      "Youth sport-health programme",
      "Rehabilitating sports facilities",
      "Support to local sports clubs",
      "Inclusion of people with disabilities",
    ],
  },
]

const R = 35           // rayon de la piste, en % du conteneur carré
const CORE = 50
const LOGO_W = 30      // largeur du logo au moyeu, en % du conteneur
// Amorce des rayons : au-delà du demi-encombrement du logo (LOGO_W / 2), sinon son
// fond opaque tronquerait le départ des rayons les plus proches de l'horizontale.
const SPOKE_IN = LOGO_W / 2 + 1.5
const STEP = 360 / AXES.length
const DETENT = 180     // cran de lecture : 9 h, face à la fiche
const TURN_MS = 1150   // durée d'un cran de rotation
const SHOW_MS = 3400   // durée d'affichage de la fiche

// Angle de repos de chaque axe sur la roue (départ à 12 h, répartis uniformément)
const angleOf = (i: number) => -90 + i * STEP
const polar = (deg: number, r: number) => {
  const a = (deg * Math.PI) / 180
  return { x: CORE + r * Math.cos(a), y: CORE + r * Math.sin(a) }
}

const POS = AXES.map((_, i) => polar(angleOf(i), R))
const SPOKES = AXES.map((_, i) => ({ a: polar(angleOf(i), SPOKE_IN), b: polar(angleOf(i), R - 2) }))

// Rotation à appliquer à la roue pour amener l'axe i sur le cran de lecture
const rotFor = (i: number) => DETENT - angleOf(i)

// Angle équivalent le plus proche : la roue tourne d'un cran, jamais d'un tour complet
function shortestTo(from: number, to: number) {
  let delta = (to - from) % 360
  if (delta > 180) delta -= 360
  if (delta < -180) delta += 360
  return from + delta
}

/** `actif` : à passer lorsque le composant vit dans un panneau d'onglets, pour
 *  que la roue s'arrête quand le panneau est masqué. Hors onglets, laisser le
 *  défaut : c'est alors la seule visibilité à l'écran qui commande. */
export default function AxesConstellation({ actif = true }: { actif?: boolean }) {
  const locale = useLocale()
  const isEn = locale === "en"
  const reduce = useReducedMotion()
  const dansPanneau = useContext(ContexteRevelationPanneau)
  const [ref, vueEcran] = useInView({ triggerOnce: true, threshold: 0.25 })
  // Un panneau inactif est en display:none : l'observateur d'intersection ne s'y
  // déclenche pas, et ne se rattrape pas non plus à l'affichage. Dans ce cas
  // c'est donc l'onglet qui commande, jamais la visibilité mesurée.
  const inView = dansPanneau ? actif : vueEcran
  const [mobile, setMobile] = useState(false)

  // Bascule desktop / mobile
  useEffect(() => {
    const mq = window.matchMedia("(max-width: 767px)")
    const sync = () => setMobile(mq.matches)
    sync()
    mq.addEventListener("change", sync)
    return () => mq.removeEventListener("change", sync)
  }, [])

  const t = (a: Axis) => (isEn ? a.titleEn : a.titleFr)
  const d = (a: Axis) => (isEn ? a.descEn : a.descFr)

  return (
    <div ref={ref} className="relative">
      <div className="relative">
        {mobile ? (
          <MobileList axes={AXES} t={t} d={d} isEn={isEn} reduce={!!reduce} inView={inView} />
        ) : (
          <Wheel axes={AXES} t={t} d={d} isEn={isEn} reduce={!!reduce} inView={inView} />
        )}
      </div>
    </div>
  )
}

/* ────────────────────────────────────────────────────────────────────────── */
/* Desktop, la roue d'engagement (rotation crantée automatique)               */
/* ────────────────────────────────────────────────────────────────────────── */

function Wheel({
  axes, t, d, isEn, reduce, inView,
}: {
  axes: Axis[]
  t: (a: Axis) => string
  d: (a: Axis) => string
  isEn: boolean
  reduce: boolean
  inView: boolean
}) {
  const [target, setTarget] = useState(0)         // axe que la roue amène au cran
  const [arrived, setArrived] = useState(false)   // l'axe est calé → fiche dépliée
  const [hovering, setHovering] = useState(false) // survol = pause de la rotation

  const active = arrived ? target : null

  // Rotation de la roue. On part un cran en amont pour que la première mise en
  // place soit visible, puis on contre-tourne les nœuds pour les garder d'aplomb.
  const rot = useMotionValue(rotFor(0) + STEP)
  const counterRot = useTransform(rot, (v) => -v)
  const first = useRef(true)

  useEffect(() => {
    if (!inView) return
    const to = shortestTo(rot.get(), rotFor(target))
    if (reduce) {
      rot.set(to)
      setArrived(true)
      return
    }
    // Le premier cran attend que les nœuds aient fini d'apparaître
    const delay = first.current ? 0.85 : 0
    first.current = false
    const controls = animate(rot, to, {
      duration: TURN_MS / 1000,
      delay,
      ease: [0.33, 0.02, 0.2, 1], // départ appuyé, arrivée qui se pose sur le cran
      onComplete: () => setArrived(true),
    })
    return () => controls.stop()
  }, [target, inView, reduce, rot])

  // Une fois calé (et hors survol), on patiente puis la roue avance d'un cran
  useEffect(() => {
    if (!arrived || hovering || !inView) return
    const id = setTimeout(() => {
      setArrived(false)
      setTarget((i) => (i + 1) % axes.length)
    }, SHOW_MS)
    return () => clearTimeout(id)
  }, [arrived, hovering, inView, axes.length])

  // Clic sur un axe : la roue tourne directement dessus (sauf s'il est déjà au cran)
  const pick = (i: number) => {
    if (i === target) return
    setArrived(false)
    setTarget(i)
  }

  // Flèches ← / → : cran précédent / suivant
  const onKeyDown = (e: KeyboardEvent) => {
    if (e.key !== "ArrowRight" && e.key !== "ArrowLeft") return
    e.preventDefault()
    const dir = e.key === "ArrowRight" ? 1 : -1
    setArrived(false)
    setTarget((i) => (i + dir + axes.length) % axes.length)
  }

  return (
    <div
      className="mx-auto mt-2 flex w-full max-w-[1180px] flex-col items-center justify-center gap-5 md:flex-row md:gap-8 lg:gap-14"
      onMouseEnter={() => setHovering(true)}
      onMouseLeave={() => setHovering(false)}
    >
      {/* Fiche de l'axe calé, à gauche, dans l'axe du cran de lecture.
          min-h réservé : la section garde la même taille même quand la fiche se referme. */}
      <div
        className="flex w-full shrink-0 items-center justify-center md:w-[280px] lg:w-[360px] min-h-[340px] md:min-h-[440px] lg:min-h-[600px]"
        style={{ perspective: 1000 }}
      >
        <AnimatePresence mode="wait">
          {active !== null && (
            <motion.div
              key={active}
              role="status"
              className="w-full max-w-[400px] border-2 border-gray-200 bg-white p-6 shadow-sm lg:p-7"
              style={{ transformOrigin: "right center" }}
              initial={reduce ? { opacity: 0 } : { opacity: 0, rotateY: -22, scaleX: 0.9, x: 20 }}
              animate={reduce ? { opacity: 1 } : { opacity: 1, rotateY: 0, scaleX: 1, x: 0 }}
              exit={reduce ? { opacity: 0 } : { opacity: 0, rotateY: -14, scaleX: 0.94, x: 12 }}
              transition={{ duration: reduce ? 0.2 : 0.42, ease: [0.16, 1, 0.3, 1] }}
            >
              <h3 className="text-lg font-bold leading-tight text-gray-900 sm:text-xl">{t(axes[active])}</h3>
              <div className="my-3 h-0.5 w-10 bg-primary" aria-hidden />
              <p className="text-sm leading-relaxed text-gray-600">{d(axes[active])}</p>
              <ul className="mt-4 space-y-1.5">
                {(isEn ? axes[active].actionsEn : axes[active].actionsFr).map((it, i) => (
                  <motion.li
                    key={i}
                    className="flex items-start gap-2.5 text-[12px] leading-snug text-gray-600"
                    initial={reduce ? false : { opacity: 0, x: -6 }}
                    animate={{ opacity: 1, x: 0 }}
                    transition={{ duration: 0.3, delay: 0.18 + i * 0.05, ease: "easeOut" }}
                  >
                    <span className="mt-[6px] h-1 w-1 flex-shrink-0 bg-primary" aria-hidden />
                    <span>{it}</span>
                  </motion.li>
                ))}
              </ul>
            </motion.div>
          )}
        </AnimatePresence>
      </div>

      {/* La roue */}
      <div
        className="relative aspect-square w-full max-w-[380px] shrink-0 outline-none md:max-w-[440px] lg:max-w-[600px]"
        role="group"
        aria-label={isEn ? "Areas of commitment" : "Axes d'engagement"}
        onKeyDown={onKeyDown}
      >
        {/* Partie tournante : rayons + nœuds d'axes */}
        <motion.div className="absolute inset-0" style={{ rotate: rot }}>
          <svg viewBox="0 0 100 100" className="absolute inset-0 h-full w-full" aria-hidden>
            {SPOKES.map((s, i) => (
              <motion.line
                key={i}
                x1={s.a.x} y1={s.a.y} x2={s.b.x} y2={s.b.y}
                className="stroke-primary"
                strokeWidth="0.22"
                initial={{ pathLength: reduce ? 1 : 0, opacity: 0 }}
                animate={inView ? { pathLength: 1, opacity: i === target ? 0.6 : 0.18 } : {}}
                transition={{ duration: 0.7, delay: reduce ? 0 : 0.25 + i * 0.06, ease: "easeOut" }}
              />
            ))}
          </svg>

          {/* Nœuds d'axes (HTML, vrais boutons accessibles), contre-tournés pour rester d'aplomb */}
          {axes.map((a, i) => {
            const p = POS[i]
            const isOn = active === i
            // Mise en retrait : l'axe au cran est rouge plein, il ressort de lui-même.
            // On se contente d'un léger retrait d'échelle, baisser l'opacité effacerait
            // les plaquettes blanches sur le fond clair.
            const dimmed = !hovering && i !== target
            return (
              <div
                key={a.titleFr}
                className="absolute -translate-x-1/2 -translate-y-1/2"
                style={{ left: `${p.x}%`, top: `${p.y}%` }}
              >
                <motion.div style={{ rotate: counterRot }}>
                  <motion.button
                    type="button"
                    onClick={() => pick(i)}
                    aria-label={isEn ? `Area: ${t(a)}` : `Axe : ${t(a)}`}
                    aria-expanded={isOn}
                    initial={{ opacity: 0, scale: 0 }}
                    animate={inView ? { opacity: 1, scale: dimmed ? 0.93 : 1 } : {}}
                    transition={{ duration: 0.5, delay: reduce ? 0 : 0.35 + i * 0.07, ease: [0.16, 1, 0.3, 1] }}
                    whileHover={{ scale: 1.1 }}
                    className="group relative block outline-none focus-visible:ring-2 focus-visible:ring-primary focus-visible:ring-offset-2"
                  >
                    {/* Plaquette carrée, le libellé est en absolu pour que la
                        plaquette reste exactement centrée sur la piste (et donc sur le cran) */}
                    <span
                      className={`relative flex h-14 w-14 items-center justify-center border-2 transition-colors duration-300 md:h-[4.25rem] md:w-[4.25rem] ${
                        isOn
                          ? "border-primary bg-primary shadow-md"
                          : "border-gray-300 bg-white shadow-sm group-hover:border-primary"
                      }`}
                    >
                      <a.Icon
                        className={`h-6 w-6 transition-colors duration-300 md:h-7 md:w-7 ${
                          isOn ? "text-white" : "text-gray-400 group-hover:text-primary"
                        }`}
                        strokeWidth={1.6}
                        aria-hidden
                      />
                    </span>
                    {/* Libellé */}
                    <span
                      className={`absolute left-1/2 top-full mt-2 w-24 -translate-x-1/2 text-center text-[11px] font-semibold leading-tight tracking-tight transition-colors duration-300 md:text-xs ${
                        isOn ? "text-primary" : "text-gray-500 group-hover:text-gray-900"
                      }`}
                    >
                      {t(a)}
                    </span>
                  </motion.button>
                </motion.div>
              </div>
            )
          })}
        </motion.div>

        {/* Moyeu fixe, le logo de la Fondation.
            Le fichier est un JPEG à fond blanc, neutralisé au mix-blend-multiply.
            Le fond est porté par ce conteneur et non hérité de la section :
            son animation (scale/opacity) crée un contexte d'empilement qui isole le
            blend, lequel n'aurait donc pas l'aplat de la section pour backdrop.
            Doit rester aligné sur le fond de la section (bg-white). */}
        <motion.div
          className="pointer-events-none absolute left-1/2 top-1/2 -translate-x-1/2 -translate-y-1/2 bg-white"
          style={{ width: `${LOGO_W}%` }}
          initial={{ opacity: 0, scale: 0.8 }}
          animate={inView ? { opacity: 1, scale: 1 } : {}}
          transition={{ duration: 0.5, ease: [0.16, 1, 0.3, 1] }}
        >
          <Image
            src="/fondation-sar-logo.jpg"
            alt={isEn ? "SAR Foundation logo" : "Logo Fondation SAR"}
            width={830}
            height={445}
            className="h-auto w-full mix-blend-multiply"
          />
        </motion.div>
      </div>
    </div>
  )
}

/* ────────────────────────────────────────────────────────────────────────── */
/* Mobile, noyau + liste accordéon reliée par un cordon lumineux              */
/* ────────────────────────────────────────────────────────────────────────── */

function MobileList({
  axes, t, d, isEn, reduce, inView,
}: {
  axes: Axis[]
  t: (a: Axis) => string
  d: (a: Axis) => string
  isEn: boolean
  reduce: boolean
  inView: boolean
}) {
  const [open, setOpen] = useState<number | null>(null)

  return (
    <div className="mx-auto mt-6 max-w-md">
      {/* Moyeu, le logo de la Fondation (fond blanc du JPEG neutralisé au multiply) */}
      <div className="relative mb-7 flex flex-col items-center">
        <div className="bg-white">
          <Image
            src="/fondation-sar-logo.jpg"
            alt={isEn ? "SAR Foundation logo" : "Logo Fondation SAR"}
            width={830}
            height={445}
            className="h-auto w-40 mix-blend-multiply"
          />
        </div>
      </div>

      {/* Liste reliée par un cordon vertical */}
      <div className="relative space-y-2.5 pl-5">
        <div
          className="absolute left-1 top-0 bottom-0 w-px"
          style={{ background: "linear-gradient(to bottom, var(--primary), color-mix(in oklab, var(--primary) 12%, transparent))" }}
          aria-hidden
        />
        {axes.map((a, i) => {
          const isOn = open === i
          return (
            <motion.div
              key={a.titleFr}
              initial={{ opacity: 0, x: 16 }}
              animate={inView ? { opacity: 1, x: 0 } : {}}
              transition={{ duration: 0.4, delay: reduce ? 0 : i * 0.06 }}
              className="relative"
            >
              {/* Pastille de jonction sur le cordon */}
              <span
                className={`absolute -left-[1.05rem] top-4 h-2 w-2 transition-all ${isOn ? "bg-primary" : "bg-gray-300"}`}
                aria-hidden
              />
              <button
                type="button"
                onClick={() => setOpen(isOn ? null : i)}
                aria-expanded={isOn}
                className={`flex w-full items-center justify-between border-2 border-gray-200 px-4 py-3 text-left transition-colors ${
                  isOn ? "bg-white" : "bg-white/60"
                }`}
              >
                <span className={`text-sm font-semibold transition-colors ${isOn ? "text-primary" : "text-gray-900"}`}>{t(a)}</span>
                <span className={`ml-2 transition-transform ${isOn ? "rotate-180 text-primary" : "text-gray-400"}`} aria-hidden>⌄</span>
              </button>
              <AnimatePresence initial={false}>
                {isOn && (
                  <motion.div
                    initial={{ height: 0, opacity: 0 }}
                    animate={{ height: "auto", opacity: 1 }}
                    exit={{ height: 0, opacity: 0 }}
                    transition={{ duration: 0.3, ease: "easeInOut" }}
                    className="overflow-hidden"
                  >
                    <div className="px-4 pb-3 pt-2.5">
                      <p className="text-[13px] leading-relaxed text-gray-600">{d(a)}</p>
                      <ul className="mt-2.5 space-y-1.5">
                        {(isEn ? a.actionsEn : a.actionsFr).map((it, k) => (
                          <li key={k} className="flex items-start gap-2.5 text-[12px] leading-snug text-gray-600">
                            <span className="mt-[6px] h-1 w-1 flex-shrink-0 bg-primary" aria-hidden />
                            <span>{it}</span>
                          </li>
                        ))}
                      </ul>
                    </div>
                  </motion.div>
                )}
              </AnimatePresence>
            </motion.div>
          )
        })}
      </div>
    </div>
  )
}
