"use client"

import { useState, useEffect, useMemo } from "react"
import { AnimatePresence, motion, useReducedMotion } from "framer-motion"
import { useLocale } from "next-intl"
import { X } from "lucide-react"

/**
 * « Où nous agissons », carte du Sénégal (SVG, projection Mercator maison, sans clé API ni dépendance).
 * Vue nationale : carte du Sénégal (toutes les régions), avec Dakar mis en évidence et un marqueur
 * cliquable (halo pulsé) sur la zone de la raffinerie. Au clic, une popup affiche la carte détaillée
 * de la région de Dakar : la raffinerie et les actions de la Fondation.
 * GeoJSON local : public/geo/senegal-regions.json.
 */

type Role = "usine" | "pipeline" | "voisine"
type Commune = { id: string; name: string; role: Role; lng: number; lat: number; noteFr: string; noteEn: string; lblDx: number; lblDy: number; anchor: "start" | "middle" | "end" }

const COMMUNES: Commune[] = [
  { id: "diamaguene", name: "SAR", role: "usine", lng: -17.3180, lat: 14.7280, noteFr: "Usine de raffinage de la SAR (baie de Hann)", noteEn: "SAR refinery plant (Hann Bay)", lblDx: 3, lblDy: -1.4, anchor: "start" },
]

// Les regions ou la Fondation intervient au-dela de la zone riveraine, telles
// que les nomment les sources : Diourbel, Louga et Thies. Elles sont posees sur
// la carte nationale, la ou l'oeil les cherche.
type Action = { titre: string; texte: string }
type Source = { label: string; url: string }
/** Une fiche : un programme, mene en un lieu. Un lieu peut en compter plusieurs. */
/** `axe` : l'axe strategique dont releve le programme, tel que la Fondation
 *  les nomme. Affiche en etiquette au-dessus du titre de la fiche. */
type Detail = { axe?: string; titre: string; intro: string; actions: Action[]; sources?: Source[] }
type Intervention = {
  id: string; name: string; lng: number; lat: number
  dx: number; dy: number; anchor: "start" | "middle" | "end"; ptDy?: number
  detailFr: Detail[]; detailEn: Detail[]
}

// Toutes les fiches sont adossees a une publication du compte officiel de la
// SAR, completee le cas echeant par une depeche de l'APS.
const POST_NUTRITION = "https://www.linkedin.com/posts/societe-africaine-de-raffinage_rse-sar-impactsocial-activity-7454494451265171457-NpIu"
const DEPECHE_APS = "https://aps.sn/la-sar-et-le-cndn-lancent-un-projet-dassistance-alimentaire-destine-a-1-500-talibes-et-a-50-menages/"
const POST_ECOLES = "https://www.linkedin.com/posts/societe-africaine-de-raffinage_fondationsar-sar-rse-activity-7478086960860008449-bMJC"
const POST_ECOLES_PRIMAIRES = "https://www.linkedin.com/posts/societe-africaine-de-raffinage_dans-le-cadre-de-notre-politique-rse-et-fid%C3%A8le-activity-7399094091793211392-j_LV"
const POST_INONDATIONS = "https://www.linkedin.com/posts/societe-africaine-de-raffinage_sar-rse-solidaritaez-activity-7365739150181863424-Vv_E"

const src = (url: string): Source[] => [{ label: "LinkedIn officiel de la SAR", url }]
const srcEn = (url: string): Source[] => [{ label: "SAR official LinkedIn", url }]

const NUTRITION_FR: Detail = {
  axe: "Santé et développement communautaire",
  titre: "Nutrition des enfants talibés",
  intro:
    "Le 24 avril 2026, la SAR s'est engagée aux côtés du Conseil national de développement de la nutrition (CNDN) pour lancer un programme d'assistance alimentaire destiné aux daaras et aux populations vulnérables des régions de Louga, Diourbel, Thiès et Dakar.",
  actions: [
    {
      titre: "Les bénéficiaires",
      texte:
        "1 500 enfants talibés issus de 30 daaras, ainsi que 50 ménages vulnérables de la commune de Diamaguène Sicap Mbao.",
    },
    {
      titre: "Le financement",
      texte:
        "51 millions de FCFA apportés par la SAR, portés à plus de 61 millions avec la contribution du CNDN.",
    },
  ],
  sources: [
    { label: "LinkedIn officiel de la SAR", url: POST_NUTRITION },
    { label: "APS, 24 avril 2026", url: DEPECHE_APS },
  ],
}

const NUTRITION_EN: Detail = {
  axe: "Health and community development",
  titre: "Nutrition for talibé children",
  intro:
    "On 24 April 2026, SAR joined forces with the National Council for Nutrition Development (CNDN) to launch a food assistance programme for daaras and vulnerable people in the Louga, Diourbel, Thiès and Dakar regions.",
  actions: [
    {
      titre: "Who benefits",
      texte:
        "1,500 talibé children from 30 daaras, together with 50 vulnerable households in the commune of Diamaguène Sicap Mbao.",
    },
    {
      titre: "Funding",
      texte:
        "51 million CFA francs from SAR, brought to over 61 million with the CNDN's contribution.",
    },
  ],
  sources: [
    { label: "SAR official LinkedIn", url: POST_NUTRITION },
    { label: "APS, 24 April 2026", url: DEPECHE_APS },
  ],
}

// Une seule visite du Directeur general, trois communes inondees.
const INONDATIONS_FR: Detail = {
  axe: "Développement communautaire",
  titre: "Solidarité après les inondations",
  intro:
    "À la suite des inondations, le Directeur général de la SAR est allé à la rencontre des maires des communes touchées pour exprimer le soutien de l'entreprise aux populations éprouvées.",
  actions: [
    {
      titre: "Les communes concernées",
      texte: "Diamaguène Sicap Mbao, Tivaouane Diacksao et Thiaroye-sur-Mer.",
    },
    {
      titre: "L'engagement",
      texte: "Rester aux côtés des collectivités locales dans les moments de difficulté.",
    },
  ],
  sources: src(POST_INONDATIONS),
}

const INONDATIONS_EN: Detail = {
  axe: "Community development",
  titre: "Solidarity after the floods",
  intro:
    "Following the floods, SAR's Managing Director met the mayors of the affected communes to convey the company's support to the people hit by them.",
  actions: [
    {
      titre: "Communes concerned",
      texte: "Diamaguène Sicap Mbao, Tivaouane Diacksao and Thiaroye-sur-Mer.",
    },
    {
      titre: "The commitment",
      texte: "Standing by local authorities in difficult times.",
    },
  ],
  sources: srcEn(POST_INONDATIONS),
}

/** La rehabilitation d'ecoles, menee dans plusieurs regions. La localite
 *  concernee est passee en parametre : le programme est le meme, le lieu non. */
const ecolesFr = (localite: string): Detail => ({
  axe: "Éducation",
  titre: "Réhabilitation d'infrastructures scolaires",
  intro:
    "La Fondation SAR réhabilite des établissements scolaires pour offrir aux élèves et aux enseignants des conditions d'apprentissage dignes.",
  actions: [{ titre: "La localité", texte: localite }],
})

const ecolesEn = (localite: string): Detail => ({
  axe: "Education",
  titre: "Refurbishment of school facilities",
  intro:
    "The SAR Foundation refurbishes schools so that pupils and teachers have decent learning conditions.",
  actions: [{ titre: "Locality", texte: localite }],
})

/** Le soutien aux cites religieuses, pour l'organisation de leurs evenements. */
const citesFr = (localites: string): Detail => ({
  axe: "Développement communautaire",
  titre: "Soutien aux cités religieuses",
  intro:
    "La Fondation SAR accompagne les cités religieuses dans l'organisation de leurs grands événements.",
  actions: [{ titre: "Les cités concernées", texte: localites }],
})

const citesEn = (localites: string): Detail => ({
  axe: "Community development",
  titre: "Support for religious cities",
  intro:
    "The SAR Foundation supports religious cities in organising their major events.",
  actions: [{ titre: "Cities concerned", texte: localites }],
})

const INTERVENTIONS: Intervention[] = [
  { id: "diourbel", name: "Diourbel", lng: -16.2311, lat: 14.6556, dx: 2.4, dy: 0.4, anchor: "start",
    detailFr: [NUTRITION_FR, citesFr("Touba et Darou Mouhty.")],
    detailEn: [NUTRITION_EN, citesEn("Touba and Darou Mouhty.")] },
  { id: "louga", name: "Louga", lng: -16.2247, lat: 15.6144, dx: 2.4, dy: 0.4, anchor: "start",
    detailFr: [NUTRITION_FR], detailEn: [NUTRITION_EN] },
  {
    id: "thies", name: "Thiès", lng: -16.9246, lat: 14.7910, dx: 0, dy: -2.2, anchor: "middle", ptDy: -2.2,
    detailFr: [
      NUTRITION_FR,
      citesFr("Tivaouane."),
      {
        axe: "Sport",
        titre: "Semaine nationale de la jeunesse",
        intro: "La Fondation SAR soutient l'organisation de la semaine nationale de la jeunesse.",
        actions: [{ titre: "Les localités", texte: "Mbour et la ville de Thiès." }],
      },
    ],
    detailEn: [
      NUTRITION_EN,
      citesEn("Tivaouane."),
      {
        axe: "Sport",
        titre: "National Youth Week",
        intro: "The SAR Foundation supports the organisation of the national youth week.",
        actions: [{ titre: "Localities", texte: "Mbour and the town of Thiès." }],
      },
    ],
  },
  {
    // Podor est sur le fleuve Senegal, qui est la frontiere : a sa latitude
    // exacte, 16,65, le point tombait sur le trait de contour du fond de plan,
    // simplifie, et paraissait sortir du pays. Il est ramene a l'interieur.
    id: "podor", name: "Podor", lng: -14.9700, lat: 16.5700, dx: 2.4, dy: 0.4, anchor: "start",
    detailFr: [ecolesFr("La ville de Podor.")],
    detailEn: [ecolesEn("The town of Podor.")],
  },
  {
    id: "saint-louis", name: "Saint-Louis", lng: -16.4896, lat: 16.0179, dx: -2.4, dy: 0.4, anchor: "end",
    detailFr: [{
      axe: "Éducation",
      titre: "Formation de trente jeunes",
      intro: "La Fondation SAR prend en charge la formation de trente jeunes de la région.",
      actions: [{ titre: "Les bénéficiaires", texte: "30 jeunes, dont la formation est financée intégralement." }],
    }],
    detailEn: [{
      axe: "Education",
      titre: "Training for thirty young people",
      intro: "The SAR Foundation funds the training of thirty young people from the region.",
      actions: [{ titre: "Who benefits", texte: "30 young people, whose training is fully funded." }],
    }],
  },
  {
    id: "mbour", name: "Mbour", lng: -16.9667, lat: 14.4167, dx: -2.4, dy: 0.8, anchor: "end",
    detailFr: [ecolesFr("Joal-Fadiouth.")],
    detailEn: [ecolesEn("Joal-Fadiouth.")],
  },
  {
    id: "kaolack", name: "Kaolack", lng: -16.0726, lat: 14.1652, dx: 2.4, dy: 0.4, anchor: "start",
    detailFr: [citesFr("Kaolack et Porokhane.")],
    detailEn: [citesEn("Kaolack and Porokhane.")],
  },
  {
    id: "ziguinchor", name: "Ziguinchor", lng: -16.2719, lat: 12.5833, dx: 2.4, dy: 0.4, anchor: "start",
    detailFr: [ecolesFr("Diembéring.")],
    detailEn: [ecolesEn("Diembéring.")],
  },
]

type ActionDakar = {
  id: string; name: string; lng: number; lat: number
  dx: number; dy: number; anchor: "start" | "middle" | "end"
  // Libelle sur plusieurs lignes, quand le nom est trop long pour la carte.
  lignes?: string[]
  detailFr: Detail[]; detailEn: Detail[]
}

const ACTIONS_DAKAR: ActionDakar[] = [
  {
    id: "guediawaye", name: "Guédiawaye", lng: -17.4053, lat: 14.7761, dx: 0, dy: -2.8, anchor: "middle",
    detailFr: [{
      axe: "Éducation",
      titre: "Réhabilitation du lycée Banque islamique",
      intro: "Dans le cadre de son programme de réhabilitation des établissements scolaires, la Fondation SAR a mené une réhabilitation d'ensemble du lycée Banque islamique de Guédiawaye. Le Directeur général en a réceptionné les travaux.",
      actions: [
        { titre: "Les salles de classe", texte: "24 salles réhabilitées et mobilier scolaire rénové." },
        { titre: "L'infirmerie et l'eau", texte: "L'infirmerie a été rénovée et un point d'eau installé, avec un réservoir de 1 000 litres." },
      ],
      sources: src(POST_ECOLES),
    }],
    detailEn: [{
      axe: "Education",
      titre: "Refurbishment of the Banque islamique school",
      intro: "As part of its school refurbishment programme, the SAR Foundation carried out a full refurbishment of the Banque islamique secondary school in Guédiawaye. The Managing Director handed over the works.",
      actions: [
        { titre: "Classrooms", texte: "24 classrooms refurbished and school furniture renewed." },
        { titre: "Infirmary and water", texte: "The infirmary was renovated and a water point installed, with a 1,000-litre tank." },
      ],
      sources: srcEn(POST_ECOLES),
    }],
  },
  {
    id: "yoff", name: "Yoff", lng: -17.4667, lat: 14.7500, dx: 0, dy: 3.6, anchor: "middle",
    detailFr: [{
      axe: "Éducation",
      titre: "Rénovation de la maternelle El Hadji Doudou Ndoye",
      intro: "À l'école élémentaire El Hadji Doudou Ndoye, dans la commune de Yoff, la Fondation SAR est intervenue sur la maternelle.",
      actions: [
        { titre: "Les salles", texte: "2 salles de maternelle rénovées et repeintes, portes et fenêtres changées." },
        { titre: "Les sanitaires", texte: "Construction de toilettes." },
      ],
      sources: src(POST_ECOLES),
    }],
    detailEn: [{
      axe: "Education",
      titre: "Renovation of the El Hadji Doudou Ndoye nursery",
      intro: "At the El Hadji Doudou Ndoye primary school, in the commune of Yoff, the SAR Foundation worked on the nursery section.",
      actions: [
        { titre: "Classrooms", texte: "2 nursery classrooms renovated and repainted, doors and windows replaced." },
        { titre: "Sanitation", texte: "New toilets built." },
      ],
      sources: srcEn(POST_ECOLES),
    }],
  },
  {
    id: "golf-sud", name: "Golf Sud", lng: -17.3930, lat: 14.7680, dx: 3, dy: 1, anchor: "start",
    detailFr: [{
      axe: "Éducation",
      titre: "Réhabilitation de l'école de Golf Nord",
      intro: "Dans le cadre de son programme de soutien à l'éducation, la SAR a réhabilité l'école baraque de Golf Nord, dans la commune de Golf Sud.",
      actions: [
        { titre: "Les salles de classe", texte: "Rénovation des salles de classe." },
        { titre: "Les espaces communs", texte: "Mise à niveau des espaces communs de l'école." },
      ],
      sources: src(POST_ECOLES_PRIMAIRES),
    }],
    detailEn: [{
      axe: "Education",
      titre: "Refurbishment of the Golf Nord school",
      intro: "As part of its education support programme, SAR refurbished the Golf Nord school, in the commune of Golf Sud.",
      actions: [
        { titre: "Classrooms", texte: "Classrooms renovated." },
        { titre: "Shared areas", texte: "The school's shared areas were upgraded." },
      ],
      sources: srcEn(POST_ECOLES_PRIMAIRES),
    }],
  },
  {
    id: "tivaouane-d", name: "Tivaouane Diacksao", lng: -17.3695, lat: 14.7502, dx: 3, dy: 2.8, anchor: "start",
    detailFr: [
      {
        axe: "Éducation",
        titre: "Réhabilitation de deux écoles primaires",
        intro: "Dans la commune de Tivaouane Diacksao, la SAR est intervenue sur deux écoles primaires, Mbaye Diouf et Moustapha Khaly Ndiaye.",
        actions: [
          { titre: "Les sanitaires", texte: "Amélioration des infrastructures sanitaires des deux écoles." },
          { titre: "Les espaces communs", texte: "Mise à niveau des espaces communs." },
        ],
        sources: src(POST_ECOLES_PRIMAIRES),
      },
      INONDATIONS_FR,
    ],
    detailEn: [
      {
        axe: "Education",
        titre: "Refurbishment of two primary schools",
        intro: "In the commune of Tivaouane Diacksao, SAR worked on two primary schools, Mbaye Diouf and Moustapha Khaly Ndiaye.",
        actions: [
          { titre: "Sanitation", texte: "Sanitary facilities upgraded at both schools." },
          { titre: "Shared areas", texte: "Shared areas upgraded." },
        ],
        sources: srcEn(POST_ECOLES_PRIMAIRES),
      },
      INONDATIONS_EN,
    ],
  },
  {
    id: "thiaroye-mer", name: "Thiaroye-sur-Mer", lng: -17.3750, lat: 14.7444, dx: 0, dy: 3.8, anchor: "middle",
    detailFr: [
      {
        axe: "Développement communautaire",
        titre: "Petit déjeuner dans les écoles primaires",
        intro: "La Fondation SAR soutient la distribution d'un petit déjeuner aux élèves des écoles primaires de Thiaroye-sur-Mer.",
        actions: [{ titre: "Les bénéficiaires", texte: "Les élèves des écoles primaires de la commune." }],
      },
      {
        axe: "Santé",
        titre: "Vidéosurveillance de l'hôpital psychiatrique",
        intro: "La Fondation SAR a fait installer un système de vidéosurveillance à l'hôpital psychiatrique de Thiaroye.",
        actions: [{ titre: "L'équipement", texte: "Un système de vidéosurveillance, pour la sécurité des patients et du personnel." }],
      },
      INONDATIONS_FR,
    ],
    detailEn: [
      {
        axe: "Community development",
        titre: "Breakfast in primary schools",
        intro: "The SAR Foundation supports the distribution of breakfast to primary school pupils in Thiaroye-sur-Mer.",
        actions: [{ titre: "Who benefits", texte: "Pupils of the commune's primary schools." }],
      },
      {
        axe: "Health",
        titre: "CCTV at the psychiatric hospital",
        intro: "The SAR Foundation had a CCTV system installed at the Thiaroye psychiatric hospital.",
        actions: [{ titre: "The equipment", texte: "A CCTV system, for the safety of patients and staff." }],
      },
      INONDATIONS_EN,
    ],
  },
  {
    id: "diamaguene-sm", name: "Diamaguène Sicap Mbao", lignes: ["Diamaguène", "Sicap Mbao"], lng: -17.3450, lat: 14.7700, dx: 3, dy: -1.2, anchor: "start",
    detailFr: [
      NUTRITION_FR,
      {
        axe: "Développement durable",
        titre: "Drainage des eaux de pluie",
        intro: "La Fondation SAR soutient la réalisation d'ouvrages secondaires de drainage des eaux de pluie dans la commune.",
        actions: [{ titre: "Les ouvrages", texte: "Des ouvrages secondaires de drainage, pour limiter les inondations." }],
      },
      INONDATIONS_FR,
    ],
    detailEn: [
      NUTRITION_EN,
      {
        axe: "Sustainable development",
        titre: "Stormwater drainage",
        intro: "The SAR Foundation supports the building of secondary stormwater drainage works in the commune.",
        actions: [{ titre: "The works", texte: "Secondary drainage works, to limit flooding." }],
      },
      INONDATIONS_EN,
    ],
  },
  {
    id: "pikine", name: "Pikine", lng: -17.3906, lat: 14.7548, dx: -3, dy: 1.2, anchor: "end",
    detailFr: [{
      axe: "Sport",
      titre: "Compétitions intra-scolaires",
      intro: "La Fondation SAR soutient l'organisation de compétitions sportives entre établissements scolaires de Pikine.",
      actions: [{ titre: "Les participants", texte: "Les élèves des établissements de la commune." }],
    }],
    detailEn: [{
      axe: "Sport",
      titre: "Inter-school competitions",
      intro: "The SAR Foundation supports sports competitions between schools in Pikine.",
      actions: [{ titre: "Who takes part", texte: "Pupils from the commune's schools." }],
    }],
  },
  {
    id: "camberene", name: "Cambérène", lng: -17.4333, lat: 14.7667, dx: -3, dy: -1.6, anchor: "end",
    detailFr: [citesFr("Cambérène.")],
    detailEn: [citesEn("Cambérène.")],
  },
]

/** Les programmes conduits a l'echelle du pays, ou en un lieu de Dakar que la
 *  carte ne situe pas : ils n'ont pas de point, ils ont leur liste. */
const PROGRAMMES_NATIONAUX_FR: Detail[] = [
  {
    axe: "Santé",
    titre: "Octobre rose",
    intro: "La Fondation SAR soutient l’Amicale des femmes de la SAR (AFSAR) dans l’organisation d’Octobre rose, mois de sensibilisation au cancer du sein.",
    actions: [{ titre: "La portée", texte: "Une campagne menée à l'échelle nationale." }],
  },
  {
    axe: "Santé",
    titre: "Fonds rose de la DER",
    intro: "La Fondation SAR soutient le fonds rose de la Délégation à l'entrepreneuriat rapide, destiné aux femmes victimes du cancer.",
    actions: [{ titre: "Les bénéficiaires", texte: "Les femmes victimes du cancer, accompagnées dans leur activité." }],
  },
  {
    axe: "Santé",
    titre: "Ascenseur du CNAO",
    intro: "La Fondation SAR a financé la réalisation d'un ascenseur au Centre national d'appareillage orthopédique, à Dakar.",
    actions: [{ titre: "L'ouvrage", texte: "Un ascenseur, pour l'accès des patients aux étages du centre." }],
  },
  {
    axe: "Éducation",
    titre: "Concours général",
    intro: "La Fondation SAR soutient le Concours général, qui distingue chaque année les meilleurs élèves du pays.",
    actions: [{ titre: "Les lauréats", texte: "Les meilleurs élèves des lycées du Sénégal." }],
  },
]

const PROGRAMMES_NATIONAUX_EN: Detail[] = [
  {
    axe: "Health",
    titre: "Pink October",
    intro: "The SAR Foundation supports the SAR Women’s Association (AFSAR) in organising Pink October, the breast cancer awareness month.",
    actions: [{ titre: "Reach", texte: "A nationwide campaign." }],
  },
  {
    axe: "Health",
    titre: "The DER pink fund",
    intro: "The SAR Foundation supports the pink fund of the Delegation for Rapid Entrepreneurship, for women affected by cancer.",
    actions: [{ titre: "Who benefits", texte: "Women affected by cancer, supported in their business activity." }],
  },
  {
    axe: "Health",
    titre: "Lift at the CNAO",
    intro: "The SAR Foundation funded a lift at the National Orthopaedic Appliance Centre in Dakar.",
    actions: [{ titre: "The works", texte: "A lift, so patients can reach the centre's upper floors." }],
  },
  {
    axe: "Education",
    titre: "The Concours général",
    intro: "The SAR Foundation supports the Concours général, which each year distinguishes the country's best pupils.",
    actions: [{ titre: "The winners", texte: "The best pupils in Senegal's secondary schools." }],
  },
]


// Cadre géographique de l'encart de zoom (englobe les 6 communes avec marge)
const ZOOM_BBOX = { minLng: -17.500, maxLng: -17.300, minLat: 14.690, maxLat: 14.800 }

// Carte nationale
const VB_W = 100
const VB_H = 78

// Encart de zoom (popup)
const ZOOM_VB_W = 100
const ZOOM_VB_H = 78

// Projection Mercator normalisée [0..1]
function mercator(lng: number, lat: number): [number, number] {
  const x = (lng + 180) / 360
  const rad = (lat * Math.PI) / 180
  const y = (1 - Math.log(Math.tan(Math.PI / 4 + rad / 2)) / Math.PI) / 2
  return [x, y]
}

// Fonction de projection qui cadre une liste de points de référence dans un viewBox donné
function makeProjector(refPts: [number, number][], pad: number, vbW: number, vbH: number) {
  let minX = Infinity, maxX = -Infinity, minY = Infinity, maxY = -Infinity
  for (const [lng, lat] of refPts) {
    const [x, y] = mercator(lng, lat)
    if (x < minX) minX = x
    if (x > maxX) maxX = x
    if (y < minY) minY = y
    if (y > maxY) maxY = y
  }
  const scale = Math.min((vbW - 2 * pad) / (maxX - minX || 1e-6), (vbH - 2 * pad) / (maxY - minY || 1e-6))
  const tx = (vbW - (maxX + minX) * scale) / 2
  const ty = (vbH - (maxY + minY) * scale) / 2
  return (lng: number, lat: number): [number, number] => {
    const [x, y] = mercator(lng, lat)
    return [x * scale + tx, y * scale + ty]
  }
}

type Geom = { type: string; coordinates: unknown }
function geomToPath(g: Geom, project: (lng: number, lat: number) => [number, number]): string {
  let d = ""
  const rings = (a: unknown) => {
    if (Array.isArray(a) && Array.isArray(a[0]) && typeof (a[0] as unknown[])[0] === "number") {
      d += (a as [number, number][]).map((p, i) => {
        const [X, Y] = project(p[0], p[1])
        return `${i === 0 ? "M" : "L"}${X.toFixed(2)},${Y.toFixed(2)}`
      }).join(" ") + "Z"
      return
    }
    if (Array.isArray(a)) a.forEach(rings)
  }
  rings(g.coordinates)
  return d
}

const ROLE_COLOR: Record<Role, string> = { usine: "#111827", pipeline: "#E52621", voisine: "#9aa6b0" }

export default function FondationZonesMap() {
  const locale = useLocale()
  const isFr = locale !== "en"
  const reduce = !!useReducedMotion()
  const [active, setActive] = useState<string | null>(null)
  const [zoomOpen, setZoomOpen] = useState(false)
  const [geo, setGeo] = useState<{ features: { properties: { shapeName?: string }; geometry: Geom }[] } | null>(null)
  // Le repère cliquable porte un tabindex : le navigateur lui dessinait un
  // rectangle de focus rouge (la couleur d'anneau globale) en travers de la
  // carte. On le neutralise et on marque la sélection à la manière d'un fond
  // de plan : un cercle de sélection fin, centré sur le symbole.
  const [repereFocus, setRepereFocus] = useState(false)
  // Le point d'intervention selectionne. Son detail remplace le texte de la
  // colonne de droite : le visiteur lit ce qui a ete fait la ou il a clique,
  // sans quitter la carte des yeux.
  const [zoneActive, setZoneActive] = useState<string | null>(null)
  // Point survole. Six communes tiennent dans quelques kilometres : afficher
  // les six noms en permanence les faisait se chevaucher. Le point garde son
  // numero, le nom n'apparait qu'au survol ou a l'ouverture.
  const [survol, setSurvol] = useState<string | null>(null)

  useEffect(() => {
    let cancelled = false
    fetch("/geo/senegal-regions.json")
      .then((r) => (r.ok ? r.json() : Promise.reject()))
      .then((d) => { if (!cancelled) setGeo(d) })
      .catch(() => {})
    return () => { cancelled = true }
  }, [])

  // Fermer la popup avec la touche Échap
  useEffect(() => {
    if (!zoomOpen) return
    const onKey = (e: KeyboardEvent) => { if (e.key === "Escape") setZoomOpen(false) }
    window.addEventListener("keydown", onKey)
    return () => window.removeEventListener("keydown", onKey)
  }, [zoomOpen])

  // ── Carte nationale (vue d'ensemble) ────────────────────────────────────
  const senegal = useMemo(() => {
    if (!geo) return null
    const refPts: [number, number][] = []
    const collectPts = (a: unknown) => {
      if (Array.isArray(a) && Array.isArray(a[0]) && typeof (a[0] as unknown[])[0] === "number") {
        refPts.push(...(a as [number, number][]))
        return
      }
      if (Array.isArray(a)) a.forEach(collectPts)
    }
    geo.features.forEach((f) => collectPts(f.geometry.coordinates))
    const project = makeProjector(refPts, 2, VB_W, VB_H)
    const regionPaths = geo.features.map((f) => ({
      name: f.properties.shapeName ?? "",
      path: geomToPath(f.geometry, project),
      isDakar: (f.properties.shapeName ?? "").toLowerCase() === "dakar",
    }))
    const indicator = project((ZOOM_BBOX.minLng + ZOOM_BBOX.maxLng) / 2, (ZOOM_BBOX.minLat + ZOOM_BBOX.maxLat) / 2)
    const interventions = INTERVENTIONS.map((i) => ({ ...i, pt: project(i.lng, i.lat) }))
    return { regionPaths, indicator, interventions }
  }, [geo])

  // ── Détail de la région de Dakar ─────────────────────────────
  // Le cadrage se cale sur la region elle-meme, et non sur une emprise fixe :
  // celle-ci en rognait la moitie, dont Yoff et la pointe des Almadies.
  const zoom = useMemo(() => {
    if (!geo) return null
    const feat = geo.features.find((f) => (f.properties.shapeName ?? "").toLowerCase() === "dakar")
    if (!feat) return null
    // Le cadrage se cale sur les lieux a montrer, non sur la region entiere :
    // les six communes tiennent dans un quart de la presqu'ile, et une vue
    // regionale les tassait au point de rendre les noms illisibles.
    const lieux: [number, number][] = [
      ...ACTIONS_DAKAR.map((a) => [a.lng, a.lat] as [number, number]),
      ...COMMUNES.map((c) => [c.lng, c.lat] as [number, number]),
    ]
    const lngs = lieux.map((l) => l[0])
    const lats = lieux.map((l) => l[1])
    const MARGE_LNG = 0.022
    const MARGE_LAT = 0.014
    const cadre: [number, number][] = [
      [Math.min(...lngs) - MARGE_LNG, Math.min(...lats) - MARGE_LAT],
      [Math.max(...lngs) + MARGE_LNG, Math.max(...lats) + MARGE_LAT],
    ]
    const project = makeProjector(cadre, 0, ZOOM_VB_W, ZOOM_VB_H)
    return { project, landPath: geomToPath(feat.geometry, project) }
  }, [geo])

  const zproj = zoom?.project

  // Le detail affiche a droite, s'il y en a un.
  const zoneDetail = (() => {
    const i =
      INTERVENTIONS.find((x) => x.id === zoneActive) ??
      ACTIONS_DAKAR.find((x) => x.id === zoneActive)
    const fiches = i && (isFr ? i.detailFr : i.detailEn)
    return i && fiches && fiches.length ? { nom: i.name, fiches } : null
  })()

  // ── Détail : la région de Dakar et les actions de la Fondation ─────────────────
  // Il ne s'ouvre plus en fenetre modale : il recouvre exactement la carte du
  // Senegal, dans le meme cadre. Le regard reste au meme endroit, on passe du
  // pays au detail sans changer de contexte.
  const detail = (
    <AnimatePresence>
      {zoomOpen && (
          <motion.div
            className="absolute inset-0 z-20 overflow-hidden border-2 border-gray-200 bg-[#d6e8f3] shadow-sm"
            initial={{ opacity: 0, scale: 0.98 }}
            animate={{ opacity: 1, scale: 1 }}
            exit={{ opacity: 0, scale: 0.99 }}
            transition={{ duration: 0.25, ease: [0.25, 0.46, 0.45, 0.94] }}
          >
            {/* Le titre et le bouton de fermeture sont poses sur la carte, sans
                bandeau : le dessin occupe alors tout le cadre, comme la carte
                nationale qu'il recouvre. */}
            <h4 className="absolute left-3 top-3 z-10 max-w-[70%] text-[13px] font-bold leading-tight text-gray-900 sm:text-sm"
                style={{ paintOrder: "stroke" }}>
              <span className="bg-white/85 px-2 py-1 shadow-sm backdrop-blur box-decoration-clone">
                {isFr ? "Nos actions dans la région de Dakar" : "Our work in the Dakar region"}
              </span>
            </h4>
            <button
              onClick={() => setZoomOpen(false)}
              aria-label={isFr ? "Fermer" : "Close"}
              className="absolute right-2 top-2 z-10 rounded-full bg-white/85 p-1.5 text-gray-500 shadow-sm backdrop-blur transition-colors hover:bg-white hover:text-gray-800"
            >
              <X className="h-4 w-4" />
            </button>

            <div className="relative h-full w-full overflow-hidden">
              <svg viewBox={`0 0 ${ZOOM_VB_W} ${ZOOM_VB_H}`} className="absolute inset-0 h-full w-full" role="img"
                   aria-label={isFr ? "Région de Dakar : la raffinerie et les actions de la Fondation" : "Dakar region: the refinery and the Foundation's work"}>
                {zoom?.landPath && (
                  <path d={zoom.landPath} fill="#eef0e8" stroke="#9fb8c9" strokeWidth="0.3" />
                )}

                {zproj && (() => {
                  const [lx, ly] = zproj(-17.385, 14.700)
                  return (
                    <text x={lx} y={ly} textAnchor="middle" fontSize="2.6" fontStyle="italic" fill="#5b86a5">
                      {isFr ? "Baie de Hann" : "Hann Bay"}
                    </text>
                  )
                })()}


                {zproj && COMMUNES.map((c) => {
                  const [X, Y] = zproj(c.lng, c.lat)
                  const on = active === c.id
                  const r = c.role === "usine" ? 1.5 : c.role === "pipeline" ? 1.7 : 1.5
                  return (
                    <g key={c.id} style={{ cursor: "pointer" }} onMouseEnter={() => setActive(c.id)} onMouseLeave={() => setActive(null)}>
                      {c.role === "usine" && !reduce && (
                        <motion.circle cx={X} cy={Y} r={r} fill="none" stroke="#111827" strokeWidth="0.4"
                          animate={{ r: [r, r * 2.4], opacity: [0.5, 0] }} transition={{ duration: 1.8, repeat: Infinity, ease: "easeOut" }} />
                      )}
                      {/* Blanc cercle de noir : la raffinerie se distingue ainsi
                          des points rouges pleins des interventions, sans peser
                          sur le fond de plan. */}
                      <circle cx={X} cy={Y} r={r + (on ? 0.9 : 0)} fill="#fff"
                        stroke={ROLE_COLOR[c.role]} strokeWidth="0.7" style={{ transition: "r 0.2s" }} />
                      <text x={X + c.lblDx} y={Y + c.lblDy} textAnchor={c.anchor} fontSize="2.2" fontWeight={on ? 700 : 600}
                        fill={on ? "#7a1410" : "#374151"} style={{ paintOrder: "stroke", stroke: "#fff", strokeWidth: 0.7 }}>
                        {c.name}
                      </text>
                      <title>{c.name}</title>
                    </g>
                  )
                })}
                {/* Les actions de la Fondation dans la region : point clignotant,
                    cliquable, dont le detail s'affiche dans la colonne de droite. */}
                {zproj && ACTIONS_DAKAR.map((a, n) => {
                  const [X, Y] = zproj(a.lng, a.lat)
                  const ouvert = zoneActive === a.id
                  const montre = ouvert || survol === a.id
                  return (
                    <g
                      key={a.id}
                      role="button"
                      tabIndex={0}
                      aria-label={isFr ? `Voir l'action de la Fondation à ${a.name}` : `See the Foundation's work in ${a.name}`}
                      style={{ cursor: "pointer", outline: "none" }}
                      onClick={() => setZoneActive(ouvert ? null : a.id)}
                      onKeyDown={(e) => { if (e.key === "Enter" || e.key === " ") setZoneActive(ouvert ? null : a.id) }}
                      onMouseEnter={() => setSurvol(a.id)}
                      onMouseLeave={() => setSurvol(null)}
                    >
                      <circle cx={X} cy={Y} r="3.4" fill="transparent" />
                      {ouvert && (
                        <circle cx={X} cy={Y} r="4.2" fill="none" stroke="#7a1410" strokeWidth="0.4" strokeDasharray="1.2 0.9" />
                      )}
                      {/* Un point, et rien de plus. Six pastilles numerotees se
                          touchaient : a ce diametre les points se separent, et
                          c'est le clignotement qui les signale. Les noms se
                          lisent dans la liste de la colonne de droite. */}
                      {reduce ? (
                        <circle cx={X} cy={Y} r={montre ? 1.6 : 1.1} fill="#E52621" stroke="#fff" strokeWidth="0.35" />
                      ) : (
                        <motion.circle
                          cx={X} cy={Y} r={montre ? 1.6 : 1.1} fill="#E52621" stroke="#fff" strokeWidth="0.35"
                          animate={{ opacity: montre ? 1 : [1, 0.3, 1] }}
                          transition={{ duration: 1.6, repeat: montre ? 0 : Infinity, ease: "easeInOut", delay: n * 0.18 }}
                          style={{ transition: "r 0.2s" }}
                        />
                      )}
                      <text x={X + a.dx} y={Y + a.dy} textAnchor={a.anchor}
                            fontSize={montre ? "2.8" : "2.5"} fontWeight="700" fill="#7a1410"
                            style={{ paintOrder: "stroke", stroke: "#fff", strokeWidth: 0.9, pointerEvents: "none" }}>
                        {(a.lignes ?? [a.name]).map((ligne, k) => (
                          <tspan key={ligne} x={X + a.dx} dy={k === 0 ? 0 : 3}>
                            {ligne}
                          </tspan>
                        ))}
                      </text>
                    </g>
                  )
                })}
              </svg>
            </div>
          </motion.div>
      )}
    </AnimatePresence>
  )

  return (
    <div className="grid grid-cols-1 items-center gap-6 lg:grid-cols-[minmax(0,1.05fr)_minmax(0,1fr)] lg:gap-10">
      {/* ── Carte nationale ────────────────────────────────────────────── */}
      <div className="relative">
        <div className="relative w-full overflow-hidden border-2 border-gray-200 bg-[#d6e8f3] shadow-sm" style={{ aspectRatio: `${VB_W} / ${VB_H}` }}>
          {!geo && <div className="absolute inset-0 flex items-center justify-center text-xs text-gray-400">Chargement de la carte…</div>}

          <p className="absolute bottom-2 right-2 z-10 bg-white/85 px-2.5 py-1.5 text-[11px] font-bold uppercase tracking-[0.08em] leading-none text-gray-800 shadow-sm backdrop-blur">
            {isFr ? "Sénégal" : "Senegal"}
          </p>
          <svg viewBox={`0 0 ${VB_W} ${VB_H}`} className="absolute inset-0 h-full w-full" role="img"
               aria-label={isFr ? "Carte du Sénégal : zone d'action de la Fondation SAR" : "Map of Senegal: SAR Foundation's areas of action"}>

            {senegal?.regionPaths.map((r) => (
              <path
                key={r.name}
                d={r.path}
                fill={r.isDakar ? "#eef0e8" : "#f4f6f1"}
                stroke={r.isDakar ? "#9fb8c9" : "#c9d3d9"}
                strokeWidth={r.isDakar ? "0.3" : "0.2"}
              />
            ))}

            {/* Les interventions de la Fondation hors zone riveraine */}
            {senegal?.interventions.map((i) => {
              const [x, y0] = i.pt
              // `ptDy` remonte le symbole quand il se serre contre un voisin.
              const y = y0 + (i.ptDy ?? 0)
                const cliquable = Boolean(i.detailFr)
                const ouvert = zoneActive === i.id
                return (
                  <g
                    key={i.id}
                    role={cliquable ? "button" : undefined}
                    tabIndex={cliquable ? 0 : undefined}
                    aria-label={cliquable ? (isFr ? `Voir l'action de la Fondation à ${i.name}` : `See the Foundation's work in ${i.name}`) : undefined}
                    style={{ cursor: cliquable ? "pointer" : "default", outline: "none" }}
                    onClick={cliquable ? () => setZoneActive(ouvert ? null : i.id) : undefined}
                    onKeyDown={cliquable ? (e) => { if (e.key === "Enter" || e.key === " ") setZoneActive(ouvert ? null : i.id) } : undefined}
                  >
                    {/* Cible de clic large : un disque de 0,9 unite est trop
                        petit pour la souris comme pour le doigt. */}
                    {cliquable && <circle cx={x} cy={y} r="3.2" fill="transparent" />}
                    {cliquable && !reduce && !ouvert && (
                      <motion.circle cx={x} cy={y} r="0.9" fill="none" stroke="#E52621" strokeWidth="0.3"
                        animate={{ r: [0.9, 3.2], opacity: [0.55, 0] }}
                        transition={{ duration: 1.8, repeat: Infinity, ease: "easeOut" }} />
                    )}
                    {ouvert && (
                      <circle cx={x} cy={y} r="2.4" fill="none" stroke="#7a1410" strokeWidth="0.4" strokeDasharray="1.2 0.9" />
                    )}
                    <circle cx={x} cy={y} r={ouvert ? 1.3 : 0.9} fill="#E52621" stroke="#fff" strokeWidth="0.3" style={{ transition: "r 0.2s" }} />
                  <text
                    x={x + i.dx}
                    y={y + i.dy}
                    textAnchor={i.anchor}
                    fontSize={ouvert ? "2.8" : "2.5"}
                    fontWeight="700"
                    fill="#7a1410"
                    style={{ paintOrder: "stroke", stroke: "#fff", strokeWidth: 0.7 }}
                  >
                    {i.name}
                  </text>
                  </g>
                )
            })}

            {/* Bouton clignotant → ouvre le détail de la région de Dakar */}
            {senegal?.indicator && (() => {
              const [ix, iy] = senegal.indicator
              return (
                <g
                  role="button"
                  tabIndex={0}
                  aria-label={isFr ? "Voir les actions de la Fondation dans la région de Dakar" : "See the Foundation's work in the Dakar region"}
                  style={{ cursor: "pointer", outline: "none" }}
                  onClick={() => setZoomOpen(true)}
                  onKeyDown={(e) => { if (e.key === "Enter" || e.key === " ") setZoomOpen(true) }}
                  onFocus={() => setRepereFocus(true)}
                  onBlur={() => setRepereFocus(false)}
                >
                  {!reduce && (
                    <motion.circle cx={ix} cy={iy} r="1.1" fill="none" stroke="#E52621" strokeWidth="0.35"
                      animate={{ r: [1.1, 3.6], opacity: [0.6, 0] }} transition={{ duration: 1.8, repeat: Infinity, ease: "easeOut" }} />
                  )}
                  {/* Cercle de sélection : blanc doublé de gris ardoise, lisible
                      sur la terre comme sur la mer, sans couleur de marque. */}
                  {repereFocus && (
                    <>
                      <circle cx={ix} cy={iy} r="3.4" fill="none" stroke="#fff" strokeWidth="1" strokeOpacity="0.9" />
                      <circle cx={ix} cy={iy} r="3.4" fill="none" stroke="#111827" strokeWidth="0.45" strokeDasharray="1.2 0.9" />
                    </>
                  )}
                  <circle cx={ix} cy={iy} r="1.1" fill="#E52621" stroke="#fff" strokeWidth="0.3" />
                  {/* Meme traitement que les points d'intervention : le nom seul,
                      en dessous. Les trois reperes se lisent de la meme facon. */}
                  {/* Sous le symbole : Dakar est a l'extremite ouest du pays, un
                      libelle pose a gauche sortait du cadre de la carte. */}
                  <text x={ix} y={iy + 3.4} textAnchor="middle" fontSize="2.5" fontWeight="700" fill="#7a1410"
                        style={{ paintOrder: "stroke", stroke: "#fff", strokeWidth: 0.7 }}>
                    Dakar
                  </text>
                </g>
              )
            })()}
          </svg>

          {detail}
        </div>
      </div>

      {/* ── Colonne droite : texte explicatif de la carte ──────────────── */}
      {/* Colonne de droite, au gabarit du panneau « Distribution » de la page
          Activites : carte a filet gris, etiquette pleine en haut a gauche,
          entrees « terme puis glose » a puce rouge. Elle porte soit le propos
          general, soit le detail du point clique. */}
      <div className="relative border-2 border-gray-200 bg-white px-5 pb-5 pt-11 sm:px-7 sm:pb-6 sm:pt-12">
        <p className="absolute left-0 top-0 bg-primary px-4 py-2 text-sm font-bold uppercase tracking-[0.06em] text-white">
          {zoneDetail
            ? (isFr ? "Notre action" : "Our work")
            : (isFr ? "Notre périmètre d'action" : "Where we act")}
        </p>

        {zoneDetail && (
          <button
            onClick={() => setZoneActive(null)}
            className="absolute right-4 top-3 text-[11px] font-semibold uppercase tracking-widest text-gray-400 transition-colors hover:text-gray-700"
          >
            {isFr ? "Retour" : "Back"}
          </button>
        )}

        <AnimatePresence mode="wait">
          {zoneDetail ? (
            <motion.div
              key={zoneActive ?? "zone"}
              initial={{ opacity: 0, x: 12 }}
              animate={{ opacity: 1, x: 0 }}
              exit={{ opacity: 0, x: -8 }}
              transition={{ duration: 0.28, ease: [0.22, 1, 0.36, 1] }}
            >
              <p className="text-[11px] font-bold uppercase tracking-[0.14em] text-gray-400">
                {zoneDetail.nom}
              </p>

              {/* Un lieu peut porter plusieurs programmes : chacun sa fiche,
                  separee de la suivante par un filet. */}
              {zoneDetail.fiches.map((fiche, k) => (
                <div key={fiche.titre} className={k > 0 ? "mt-5 border-t border-neutral-200 pt-4" : "mt-1"}>
                  {/* L'axe strategique, en etiquette : d'une fiche a l'autre,
                      on voit a quel registre appartient l'action. */}
                  {fiche.axe && (
                    <p className="mb-1.5 inline-block bg-primary px-2 py-1 text-[9.5px] font-bold uppercase tracking-[0.08em] text-white">
                      {fiche.axe}
                    </p>
                  )}
                  <h3 className="text-[15px] font-bold leading-snug text-gray-900 sm:text-base">
                    {fiche.titre}
                  </h3>
                  <p className="mt-2 text-[13px] leading-[1.7] text-gray-600">
                    {fiche.intro}
                  </p>

                  <ul className="mt-2 divide-y divide-neutral-100">
                    {fiche.actions.map((a) => (
                      <li key={a.titre} className="flex items-start gap-2.5 py-2.5">
                        <span className="mt-[7px] h-1.5 w-1.5 flex-shrink-0 rounded-full bg-primary/60" aria-hidden />
                        <p className="text-[12.5px] leading-snug text-neutral-700">
                          <span className="font-semibold text-gray-900">{a.titre}</span>
                          {" : "}
                          {a.texte}
                        </p>
                      </li>
                    ))}
                  </ul>

                  {fiche.sources && (
                    <p className="mt-2 text-[11px] leading-relaxed text-gray-400">
                      {fiche.sources.length > 1
                        ? (isFr ? "Sources : " : "Sources: ")
                        : (isFr ? "Source : " : "Source: ")}
                      {fiche.sources.map((source, i) => (
                        <span key={source.url}>
                          {i > 0 && ", "}
                          <a
                            href={source.url}
                            target="_blank"
                            rel="noopener noreferrer"
                            className="underline underline-offset-2 transition-colors hover:text-primary"
                          >
                            {source.label}
                          </a>
                        </span>
                      ))}
                    </p>
                  )}
                </div>
              ))}
            </motion.div>
          ) : (
            <motion.div
              key="general"
              initial={{ opacity: 0, x: -8 }}
              animate={{ opacity: 1, x: 0 }}
              exit={{ opacity: 0, x: 12 }}
              transition={{ duration: 0.28, ease: [0.22, 1, 0.36, 1] }}
            >
              <h3 className="text-lg font-bold leading-snug text-gray-900 sm:text-xl">
                {isFr ? "Un engagement national" : "A national commitment"}
              </h3>
              <p className="mt-3 text-[13px] leading-[1.7] text-gray-600">
                {isFr
                  ? "La Fondation SAR met en œuvre la politique de Responsabilité Sociétale de la Société Africaine de Raffinage (SAR). Si ses actions accordent une priorité aux communautés riveraines de ses installations, son engagement s'étend à l'ensemble du territoire national, afin de contribuer durablement au développement économique, social et environnemental du Sénégal."
                  : "The SAR Foundation implements the Corporate Social Responsibility policy of the Société Africaine de Raffinage (SAR). While its initiatives prioritise communities living near its facilities, its commitment extends across the entire country, aiming to make a lasting contribution to Senegal's economic, social and environmental development."}
              </p>

              {/* Les programmes que la carte ne peut pas situer : menes a
                  l'echelle du pays, ou en un lieu de Dakar sans point dedie.
                  Sans cette liste, ils seraient absents de la page. */}
              <div className="mt-5 border-t border-neutral-200 pt-4">
                <ul className="divide-y divide-neutral-100">
                  {(isFr ? PROGRAMMES_NATIONAUX_FR : PROGRAMMES_NATIONAUX_EN).map((p) => (
                    <li key={p.titre} className="flex items-start gap-2.5 py-2.5">
                      <span className="mt-[7px] h-1.5 w-1.5 flex-shrink-0 rounded-full bg-primary/60" aria-hidden />
                      <p className="text-[12.5px] leading-snug text-neutral-700">
                        <span className="font-semibold text-gray-900">{p.titre}</span>
                        {" : "}
                        {p.intro}
                      </p>
                    </li>
                  ))}
                </ul>
              </div>
            </motion.div>
          )}
        </AnimatePresence>
      </div>
    </div>
  )
}
