// Centroïdes de repli si le géocodage ville échoue (lon, lat)
const COUNTRY_CENTROIDS = {
  US: [-98, 39], CN: [104, 35], TW: [121, 23.7], KR: [127.8, 36.5], JP: [138, 36],
  DE: [10.4, 51.2], NL: [5.3, 52.1], FR: [2.4, 46.6], GB: [-1.5, 52.5], CA: [-106, 56],
  IL: [35, 31.5], SG: [103.8, 1.35], IN: [78.9, 22], CH: [8.2, 46.8], SE: [16, 62],
  DK: [10, 56], IE: [-8, 53.4], AU: [134, -25], FI: [26, 64], NO: [9, 61],
};

// Résout un ticker → société + coordonnées du siège (FMP pour la ville, Nominatim pour les coords)
const __resolveCache = {};
async function resolveTicker(symRaw) {
  const sym = (symRaw || "").trim().toUpperCase();
  // US (AAPL, BRK.B) ou international avec suffixe de place (SAP.DE, 005930.KS, 2330.TW)
  if (!/^[A-Z0-9]{1,8}(\.[A-Z]{1,4})?$/.test(sym)) throw new Error("Ticker invalide");
  if (__resolveCache[sym]) return __resolveCache[sym];
  const fmp = window.FMP_TOKEN;
  if (!fmp) throw new Error("Clé FMP manquante (window.FMP_TOKEN dans index.html)");

  // Symbole nu d'abord, puis suffixes de place courants (Euronext, Xetra, LSE, Asie…)
  // pour qu'un ticker international comme AL2SI (→ AL2SI.PA, 2CRSi) soit trouvé sans saisir le suffixe.
  const SUFFIXES = [".PA", ".DE", ".L", ".AS", ".SW", ".MI", ".MC", ".BR", ".ST", ".CO", ".OL", ".HE", ".LS", ".VI", ".TO", ".HK", ".T", ".KS", ".TW", ".SI", ".AX"];
  const candidates = sym.includes(".") ? [sym] : [sym, ...SUFFIXES.map((s) => sym + s)];
  let p = null, resolvedSym = sym;
  for (const cand of candidates) {
    try {
      const arr = await fetch(`https://financialmodelingprep.com/stable/profile?symbol=${encodeURIComponent(cand)}&apikey=${fmp}`).then((r) => (r.ok ? r.json() : null));
      const cp = Array.isArray(arr) ? arr[0] : null;
      if (cp && cp.companyName) { p = cp; resolvedSym = cand; break; }
    } catch (e) {}
  }
  if (!p) throw new Error("Société introuvable pour « " + sym + " »");

  // géocodage du siège
  let lon, lat;
  const params = new URLSearchParams({ format: "json", limit: "1" });
  if (p.city) params.set("city", p.city);
  if (p.state) params.set("state", p.state);
  if (p.country) params.set("country", p.country);
  if (p.city || p.state) {
    try {
      const g = await fetch(`https://nominatim.openstreetmap.org/search?${params}`, { headers: { Accept: "application/json" } }).then((r) => r.json());
      if (g && g[0]) { lat = +g[0].lat; lon = +g[0].lon; }
    } catch (e) {}
  }
  if (lon == null) {
    const c = COUNTRY_CENTROIDS[p.country] || [0, 20];
    lon = c[0] + (Math.random() - 0.5) * 6;
    lat = c[1] + (Math.random() - 0.5) * 6;
  }

  const capM = p.marketCap ? p.marketCap / 1e6 : 0;
  const taille = capM > 200000 ? "MEGA" : capM > 20000 ? "LARGE" : capM > 2000 ? "MID" : "SMALL";
  const these = (p.description || "").split(/(?<=[.!?])\s/)[0] || p.industry || "Société cotée recherchée.";
  const soc = {
    id: "dyn:" + resolvedSym, nom: p.companyName, ticker: resolvedSym,
    place: p.exchange || p.country || "",
    couche: "DYN", seg: null, taille, profil: "MIXTE",
    these, detail: p.description || "", dyn: true,
    ville: [p.city, p.state, p.country].filter(Boolean).join(", "),
  };
  const res = { soc, lon, lat };
  __resolveCache[sym] = res;
  return res;
}

// Shell de l'application Globe
const GLOBE_TWEAK_DEFAULTS = /*EDITMODE-BEGIN*/{
  "accent": "#54D7FF",
  "glow": true,
  "rotationAuto": true
}/*EDITMODE-END*/;

function AppGlobe() {
  const [t, setTweak] = useTweaks(GLOBE_TWEAK_DEFAULTS);
  const accent = t.accent;
  const api = React.useRef(null);
  const [selected, setSelected] = React.useState(null);
  const [hover, setHover] = React.useState(null);
  const [sidebarOpen, setSidebarOpen] = React.useState(true);
  const [reperesOpen, setReperesOpen] = React.useState(false);
  const [overlays, setOverlays] = React.useState({ signal: false, nvidia: false });
  const [filtres, setFiltres] = React.useState({ tailles: [], profils: [], couches: [], q: "" });
  const [extras, setExtras] = React.useState(() => {
    try { return JSON.parse(localStorage.getItem("carteIA.globe.extras") || "[]"); } catch (e) { return []; }
  });
  const [addState, setAddState] = React.useState({ loading: false, error: null });

  // persistance des tickers ajoutés
  React.useEffect(() => {
    try { localStorage.setItem("carteIA.globe.extras", JSON.stringify(extras)); } catch (e) {}
  }, [extras]);

  const removeTicker = (id) => {
    setExtras((prev) => prev.filter((e) => e.soc.id !== id));
    setSelected((cur) => (cur && cur.id === id ? null : cur));
  };

  const addTicker = async (sym) => {
    setAddState({ loading: true, error: null });
    try {
      const res = await resolveTicker(sym);
      setExtras((prev) => (prev.some((e) => e.soc.id === res.soc.id) ? prev : [...prev, res]));
      if (api.current && api.current.addNode) api.current.addNode({ soc: res.soc, lon: res.lon, lat: res.lat, clusterKey: "__dyn" });
      setSelected(res.soc);
      if (api.current) api.current.flyToSoc(res.soc.id, 5);
      setAddState({ loading: false, error: null });
    } catch (e) {
      setAddState({ loading: false, error: e.message || "Échec de la recherche" });
    }
  };

  React.useEffect(() => {
    document.documentElement.style.setProperty("--accent", accent);
  }, [accent]);
  React.useEffect(() => {
    const h = (e) => { if (e.key === "Escape") setSelected(null); };
    window.addEventListener("keydown", h);
    return () => window.removeEventListener("keydown", h);
  }, []);

  const pick = (s) => {
    setSelected(s);
    if (api.current) api.current.flyToSoc(s.id, 5);
  };

  return (
    <div style={{ position: "fixed", inset: 0, overflow: "hidden" }} data-screen-label="Globe Market">
      <Globe
        accent={accent} glowOn={t.glow} autoRotate={t.rotationAuto}
        overlays={overlays} filtres={filtres} selected={selected} extras={extras}
        onSelect={(s) => setSelected((cur) => (cur && cur.id === s.id ? null : s))}
        onHover={setHover}
        registerApi={(a) => { api.current = a; }}
      />

      {/* Titre */}
      <div style={{
        position: "absolute", left: sidebarOpen ? 316 : 14, top: sidebarOpen ? 14 : 64,
        zIndex: 20, pointerEvents: "none", transition: "left .2s",
      }}>
        <div style={{ fontFamily: "var(--mono)", fontSize: 9.5, letterSpacing: "0.24em", color: accent, marginBottom: 5 }}>
          CARTOGRAPHIE DES MARCHÉS COTÉS
        </div>
        <div style={{ fontFamily: "var(--sans)", fontSize: 23, fontWeight: 680, letterSpacing: "-0.01em", color: "var(--ink)", lineHeight: 1.05 }}>
          Globe Market
        </div>
        <div style={{ fontFamily: "var(--mono)", fontSize: 10, color: "oklch(0.6 0.03 240)", marginTop: 5, letterSpacing: "0.06em" }}>
          ~49 % du S&P 500 · capex 710 Md$ · TAM optique 18→90 Md$
        </div>
      </div>

      <Sidebar filtres={filtres} setFiltres={setFiltres} onPick={pick}
        selected={selected} accent={accent} open={sidebarOpen} setOpen={setSidebarOpen}
        extras={extras} onAddTicker={addTicker} addState={addState} onRemoveTicker={removeTicker} />
      <BarreHaut filtres={filtres} setFiltres={setFiltres} overlays={overlays}
        setOverlays={setOverlays} api={api} accent={accent} />
      <ZoomControles api={api} accent={accent}
        rotation={t.rotationAuto} setRotation={(v) => setTweak("rotationAuto", v)} />
      <Legende accent={accent} />
      <EncartReperes accent={accent} open={reperesOpen} setOpen={setReperesOpen} />

      {/* Tooltip survol */}
      {hover && (!selected || selected.id !== hover.soc.id) ? (
        <div style={{
          position: "absolute", left: Math.min(hover.x + 16, window.innerWidth - 300), top: Math.max(hover.y - 14, 110),
          transform: "translateY(-100%)", zIndex: 45, pointerEvents: "none",
          maxWidth: 280, padding: "10px 13px", borderRadius: 5,
          background: "oklch(0.17 0.045 257 / 0.96)", border: `1px solid ${accent}55`,
          boxShadow: "0 8px 30px oklch(0.05 0.02 260 / 0.6)",
        }}>
          <div style={{ display: "flex", alignItems: "baseline", gap: 8 }}>
            <span style={{ fontFamily: "var(--sans)", fontSize: 13.5, fontWeight: 640, color: "var(--ink)" }}>{hover.soc.nom}</span>
            <span style={{ fontFamily: "var(--mono)", fontSize: 10, color: accent }}>{hover.soc.ticker}</span>
          </div>
          <div style={{ fontFamily: "var(--mono)", fontSize: 9, letterSpacing: "0.1em", color: "oklch(0.6 0.03 240)", margin: "4px 0 6px" }}>
            {hover.soc.couche} · {hover.soc.taille} · {PROFIL_LABEL[hover.soc.profil].toUpperCase()} · {hover.soc.place}
          </div>
          <div style={{ fontFamily: "var(--sans)", fontSize: 11.5, lineHeight: 1.5, color: "oklch(0.78 0.02 240)" }}>
            {hover.soc.these}
          </div>
        </div>
      ) : null}

      <PanneauSociete soc={selected} onClose={() => setSelected(null)}
        onSelectSoc={(s) => pick(s)} accent={accent} />

      <TweaksPanel>
        <TweakSection label="Ambiance" />
        <TweakColor label="Accent" value={t.accent} options={["#54D7FF", "#7BA7FF", "#5BE3C0", "#B48BFF"]}
          onChange={(v) => setTweak("accent", v)} />
        <TweakToggle label="Halo lumineux" value={t.glow} onChange={(v) => setTweak("glow", v)} />
        <TweakSection label="Mouvement" />
        <TweakToggle label="Rotation auto" value={t.rotationAuto} onChange={(v) => setTweak("rotationAuto", v)} />
      </TweaksPanel>
    </div>
  );
}

ReactDOM.createRoot(document.getElementById("root")).render(<AppGlobe />);
