// Actualités Finnhub par ticker — cache mémoire pour économiser le quota
const __newsCache = {};
const __profCache = {};
// Symbole US (sans suffixe de place) — éligible Finnhub/FMP gratuits. SAP.DE, 005930.KS → faux.
const __isUSTicker = (t) => typeof t === "string" && /^[A-Z]{1,5}(\.[A-Z])?$/.test(t);

// Description généraliste de l'activité (FMP /stable/profile), traduite en français
const __descCache = {};   // EN par ticker
const __descFRPromise = {}; // promesse FR (dédup : une seule traduction par société)
// Découpage en phrases robuste aux abréviations (Inc., Corp., Ltd., S.A.…) et aux fragments trop courts
const __ABBR = /\b(Inc|Corp|Co|Ltd|Cie|Llc|L\.?P|S\.?A|N\.?V|A\.?G|Plc|Bros|Mfg|St|Mr|Mrs|Dr|U\.S|Ph\.?D|e\.g|i\.e|vs|No|al)\.$/i;
function __sentences(t) {
  const parts = (t || "").split(/(?<=[.!?])\s+/);
  const out = [];
  let buf = "";
  for (const part of parts) {
    buf = buf ? buf + " " + part : part;
    const b = buf.trim();
    if (__ABBR.test(b) || b.length < 30) continue; // ne pas couper sur une abréviation / phrase trop courte
    out.push(b); buf = "";
  }
  if (buf.trim()) out.push(buf.trim());
  return out;
}
const __firstSentence = (t) => __sentences(t)[0] || "";
const __restSentences = (t) => __sentences(t).slice(1).join(" ").trim();

// Repli hors-US / sans FMP : résumé d'activité en français depuis Wikipédia (par nom)
async function __wikiFR(name) {
  const clean = (name || "").split("/")[0].replace(/\s+(Corporation|Corp\.?|Inc\.?|Ltd\.?|Co\.?|N\.?V\.?|S\.?A\.?|plc|AG|SE)$/i, "").trim();
  if (!clean) return null;
  try {
    const s = await fetch(`https://fr.wikipedia.org/w/api.php?action=query&list=search&srsearch=${encodeURIComponent(clean + " entreprise")}&format=json&origin=*`).then((r) => r.json());
    const title = s && s.query && s.query.search && s.query.search[0] && s.query.search[0].title;
    if (!title) return null;
    const sum = await fetch(`https://fr.wikipedia.org/api/rest_v1/page/summary/${encodeURIComponent(title)}`).then((r) => (r.ok ? r.json() : null));
    if (!sum || sum.type === "disambiguation" || !sum.extract) return null;
    return sum.extract;
  } catch (e) { return null; }
}

async function __fetchDescEN(soc) {
  if (soc.dyn && soc.detail) return soc.detail;
  const fmp = window.FMP_TOKEN;
  if (!fmp || !__isUSTicker(soc.ticker)) return null;
  if (__descCache[soc.ticker] !== undefined) return __descCache[soc.ticker];
  try {
    const arr = await fetch(`https://financialmodelingprep.com/stable/profile?symbol=${encodeURIComponent(soc.ticker)}&apikey=${fmp}`).then((r) => (r.ok ? r.json() : Promise.reject()));
    const d = (Array.isArray(arr) && arr[0] && arr[0].description) || null;
    __descCache[soc.ticker] = d;
    return d;
  } catch (e) { __descCache[soc.ticker] = null; return null; }
}

// Traduction EN→FR via l'endpoint public Google (gratuit, sans clé). Repli : texte original.
async function __translateFR(text) {
  if (!text) return text;
  try {
    const j = await fetch(`https://translate.googleapis.com/translate_a/single?client=gtx&sl=en&tl=fr&dt=t&q=${encodeURIComponent(text)}`).then((r) => r.json());
    const out = (j && j[0] || []).map((seg) => seg[0]).join("");
    return out || text;
  } catch (e) { return text; }
}

// Description FR (traduite), mise en cache et dédupliquée par société
function __getDescriptionFR(soc) {
  const key = soc.dyn ? soc.id : soc.ticker;
  if (__descFRPromise[key]) return __descFRPromise[key];
  const p = (async () => { const en = await __fetchDescEN(soc); return en ? await __translateFR(en) : await __wikiFR(soc.nom); })();
  __descFRPromise[key] = p;
  return p;
}

// Phrase de résumé du secteur d'activité (remplace l'ancienne thèse IA d'une ligne)
function SecteurResume({ soc }) {
  const fallback = () => {
    const layer = window.MAP_LAYERS.find((l) => l.id === soc.couche);
    const seg = layer && layer.segs.find((s) => s.id === soc.seg);
    if (layer) return seg ? `${layer.nom} — ${seg.nom}.` : `${layer.nom}.`;
    return null;
  };
  const [line, setLine] = React.useState(undefined);
  React.useEffect(() => {
    let alive = true;
    setLine(undefined);
    __getDescriptionFR(soc).then((fr) => { if (alive) setLine(fr ? __firstSentence(fr) : fallback()); });
    return () => { alive = false; };
  }, [soc.id]);

  if (!line) return null;
  return (
    <div style={{ marginBottom: 24 }}>
      <div style={{ fontFamily: "var(--mono)", fontSize: 10.5, letterSpacing: "0.2em", color: "oklch(0.55 0.02 80)", marginBottom: 10 }}>SECTEUR D'ACTIVITÉ</div>
      <p style={{ fontFamily: "var(--serif)", fontSize: 19, lineHeight: 1.45, margin: 0, color: "var(--ink)", textWrap: "pretty" }}>{line}</p>
    </div>
  );
}
function ActiviteFMP({ soc }) {
  const [desc, setDesc] = React.useState(undefined);
  React.useEffect(() => {
    let alive = true;
    setDesc(undefined);
    __getDescriptionFR(soc).then((fr) => { if (alive) setDesc(fr); });
    return () => { alive = false; };
  }, [soc.id]);

  // la 1re phrase sert de résumé de secteur en tête de fiche → on garde la suite,
  // courte mais en PHRASES ENTIÈRES (jamais coupé au milieu d'un mot)
  let rest = "";
  if (desc) {
    for (const s of __sentences(desc).slice(1)) {
      if (rest && (rest.length + 1 + s.length) > 240) break;
      rest = rest ? rest + " " + s : s;
    }
  }
  if (!rest) return null;
  return (
    <div style={{ marginBottom: 24 }}>
      <div style={{ fontFamily: "var(--mono)", fontSize: 10.5, letterSpacing: "0.2em", color: "oklch(0.55 0.02 80)", marginBottom: 10 }}>ACTIVITÉ DE L'ENTREPRISE</div>
      <p style={{ fontFamily: "var(--sans)", fontSize: 13.5, lineHeight: 1.65, margin: 0, color: "oklch(0.76 0.01 80)", textWrap: "pretty" }}>{rest}</p>
    </div>
  );
}

// Profil société live (Finnhub /stock/profile2) — secteur, capi, IPO, pays, site
function ProfilFinnhub({ soc, accent }) {
  const [p, setP] = React.useState(undefined); // undefined = en cours, null = indispo

  React.useEffect(() => {
    const token = window.FINNHUB_TOKEN;
    if (!token || !__isUSTicker(soc.ticker)) { setP(null); return; }
    if (__profCache[soc.ticker] !== undefined) { setP(__profCache[soc.ticker]); return; }
    let alive = true;
    setP(undefined);
    fetch(`https://finnhub.io/api/v1/stock/profile2?symbol=${encodeURIComponent(soc.ticker)}&token=${token}`)
      .then((r) => (r.ok ? r.json() : Promise.reject()))
      .then((d) => { if (!alive) return; const v = d && d.name ? d : null; __profCache[soc.ticker] = v; setP(v); })
      .catch(() => { if (alive) { __profCache[soc.ticker] = null; setP(null); } });
    return () => { alive = false; };
  }, [soc.id]);

  if (!p) return null; // silencieux si en cours ou indisponible
  const fmtCap = (m) => !m ? null : m >= 1e6 ? (m / 1e6).toFixed(2) + " T$" : m >= 1e3 ? Math.round(m / 1e3) + " Md$" : Math.round(m) + " M$";
  const rows = [
    ["Secteur", p.finnhubIndustry],
    ["Capitalisation", fmtCap(p.marketCapitalization)],
    ["En bourse depuis", p.ipo],
    ["Pays", p.country],
    ["Place", p.exchange],
  ].filter(([, v]) => v);

  return (
    <div style={{ borderTop: "1px solid oklch(0.25 0.01 265)", paddingTop: 18, marginBottom: 24 }}>
      <div style={{ display: "flex", alignItems: "center", gap: 10, marginBottom: 12 }}>
        {p.logo ? <img src={p.logo} alt="" style={{ width: 26, height: 26, borderRadius: 5, background: "oklch(0.95 0 0)", objectFit: "contain", flex: "none" }} /> : null}
        <div style={{ fontFamily: "var(--mono)", fontSize: 10.5, letterSpacing: "0.2em", color: "oklch(0.55 0.02 80)" }}>PROFIL · DONNÉES LIVE</div>
      </div>
      <dl style={{ margin: 0, display: "grid", gridTemplateColumns: "auto 1fr", gap: "7px 14px" }}>
        {rows.map(([k, v]) => (
          <React.Fragment key={k}>
            <dt style={{ fontFamily: "var(--mono)", fontSize: 11, color: "oklch(0.55 0.02 80)" }}>{k}</dt>
            <dd style={{ margin: 0, fontFamily: "var(--sans)", fontSize: 13, color: "var(--ink)" }}>{v}</dd>
          </React.Fragment>
        ))}
      </dl>
      {p.weburl ? (
        <a href={p.weburl} target="_blank" rel="noopener noreferrer" style={{
          display: "inline-block", marginTop: 12, fontFamily: "var(--mono)", fontSize: 11,
          letterSpacing: "0.06em", color: accent, textDecoration: "none",
        }}>↗ {p.weburl.replace(/^https?:\/\/(www\.)?/, "").replace(/\/$/, "")}</a>
      ) : null}
    </div>
  );
}

// Actus US via Finnhub (finance, filtré par pertinence)
async function __finnhubNews(soc) {
  const token = window.FINNHUB_TOKEN;
  const to = new Date(), from = new Date(to.getTime() - 28 * 864e5);
  const fmt = (d) => d.toISOString().slice(0, 10);
  const arr = await fetch(`https://finnhub.io/api/v1/company-news?symbol=${encodeURIComponent(soc.ticker)}&from=${fmt(from)}&to=${fmt(to)}&token=${token}`)
    .then((r) => (r.ok ? r.json() : Promise.reject()));
  const norm = (s) => (s || "").toLowerCase().normalize("NFD").replace(/[̀-ͯ]/g, "");
  const terms = [soc.nom, ...soc.nom.split(/[\s\/]+/).filter((w) => w.length >= 4), soc.ticker].map(norm).filter(Boolean);
  const concerne = (n) => { const txt = norm(n.headline + " " + (n.summary || "")); return terms.some((t) => new RegExp("\\b" + t.replace(/[.*+?^${}()|[\]\\]/g, "\\$&") + "\\b").test(txt)); };
  return (Array.isArray(arr) ? arr : []).filter((n) => n.headline && n.url && concerne(n)).slice(0, 6)
    .map((n) => ({ headline: n.headline, url: n.url, source: n.source, datetime: n.datetime }));
}

// Récupère une URL via une chaîne de proxys CORS publics (best-effort, timeout par essai)
async function __fetchViaProxy(target) {
  const proxies = [
    (u) => "https://corsproxy.io/?url=" + encodeURIComponent(u),
    (u) => "https://api.allorigins.win/raw?url=" + encodeURIComponent(u),
  ];
  for (const mk of proxies) {
    try {
      const c = new AbortController();
      const id = setTimeout(() => c.abort(), 8000);
      const r = await fetch(mk(target), { signal: c.signal });
      clearTimeout(id);
      if (!r.ok) continue;
      const t = await r.text();
      if (t && /<item/i.test(t)) return t;
    } catch (e) {}
  }
  return null;
}

// Actus monde (incl. hors-US) via Google News RSS en français, par nom, à travers un proxy CORS
async function __googleNews(soc) {
  const q = '"' + soc.nom.split("/")[0].trim() + '"';
  const rss = "https://news.google.com/rss/search?" + new URLSearchParams({ q, hl: "fr", gl: "FR", ceid: "FR:fr" });
  const txt = await __fetchViaProxy(rss);
  if (!txt) throw new Error("news indisponible");
  const doc = new DOMParser().parseFromString(txt, "text/xml");
  return [...doc.querySelectorAll("item")].slice(0, 6).map((it) => {
    const raw = it.querySelector("title") ? it.querySelector("title").textContent : "";
    const source = it.querySelector("source") ? it.querySelector("source").textContent : "";
    const headline = source && raw.endsWith(" - " + source) ? raw.slice(0, -(source.length + 3)) : raw;
    const pub = it.querySelector("pubDate") ? it.querySelector("pubDate").textContent : null;
    const link = it.querySelector("link") ? it.querySelector("link").textContent : null;
    return { headline, url: link, source, datetime: pub ? Math.floor(Date.parse(pub) / 1000) : null };
  }).filter((n) => n.headline && n.url);
}

function NewsSociete({ soc, accent }) {
  const [state, setState] = React.useState({ status: "idle", items: [] });
  const useFinnhub = window.FINNHUB_TOKEN && __isUSTicker(soc.ticker);

  React.useEffect(() => {
    let alive = true;
    const key = (soc.dyn ? soc.id : soc.ticker || soc.nom) + (useFinnhub ? "|fh" : "|gn");
    if (__newsCache[key]) { setState({ status: "ok", items: __newsCache[key] }); return; }
    setState({ status: "loading", items: [] });
    (useFinnhub ? __finnhubNews(soc) : __googleNews(soc))
      .then((items) => { if (!alive) return; __newsCache[key] = items; setState({ status: "ok", items }); })
      .catch(() => { if (alive) setState({ status: "error", items: [] }); });
    return () => { alive = false; };
  }, [soc.id]);

  const note = (txt) => (
    <p style={{ fontFamily: "var(--sans)", fontSize: 12.5, lineHeight: 1.5, color: "oklch(0.58 0.02 80)", margin: 0 }}>{txt}</p>
  );

  return (
    <div style={{ borderTop: "1px solid oklch(0.25 0.01 265)", paddingTop: 18, marginBottom: 8 }}>
      <div style={{ fontFamily: "var(--mono)", fontSize: 10.5, letterSpacing: "0.2em", color: "oklch(0.55 0.02 80)", marginBottom: 12 }}>
        ACTUALITÉS{useFinnhub ? " · " + soc.ticker : ""}
      </div>
      {state.status === "loading" ? note("Chargement…")
        : state.status === "error" ? note("Actualités momentanément indisponibles (quota ou réseau).")
        : state.items.length === 0 ? note("Aucune actualité spécifique récente.")
        : (
          <div style={{ display: "flex", flexDirection: "column", gap: 12 }}>
            {state.items.map((n, i) => (
              <a key={i} href={n.url} target="_blank" rel="noopener noreferrer" style={{
                display: "block", textDecoration: "none",
                borderLeft: `2px solid ${accent}55`, paddingLeft: 11,
              }}>
                <div style={{ fontFamily: "var(--sans)", fontSize: 13, lineHeight: 1.4, color: "var(--ink)", textWrap: "pretty" }}>
                  {n.headline}
                </div>
                <div style={{ fontFamily: "var(--mono)", fontSize: 9.5, letterSpacing: "0.06em", color: accent, marginTop: 4 }}>
                  {n.source || "—"}{n.datetime ? " · " + new Date(n.datetime * 1000).toLocaleDateString("fr-FR", { day: "2-digit", month: "short" }) : ""}
                </div>
              </a>
            ))}
          </div>
        )}
    </div>
  );
}

// Panneau latéral de détail — société sélectionnée
function PanneauSociete({ soc, onClose, onSelectSoc, accent }) {
  if (!soc) return null;
  const layer = window.MAP_LAYERS.find((l) => l.id === soc.couche);
  const seg = layer && layer.segs.find((s) => s.id === soc.seg);
  const hue = LAYER_HUES[soc.couche] || 240;
  const voisins = window.MAP_SOCIETES.filter(
    (s) => s.couche === soc.couche && s.seg === soc.seg && s.id !== soc.id
  );
  return (
    <aside style={{
      position: "fixed", top: 0, right: 0, bottom: 0, width: "min(440px, 92vw)",
      background: "oklch(0.155 0.012 265)", borderLeft: `1px solid oklch(0.32 0.04 ${hue})`,
      zIndex: 60, display: "flex", flexDirection: "column",
      boxShadow: "-30px 0 60px oklch(0.05 0 0 / 0.55)",
      animation: "slideIn .26s cubic-bezier(.2,.8,.2,1)",
    }}>
      <div style={{ padding: "26px 30px 18px", borderBottom: "1px solid oklch(0.25 0.01 265)" }}>
        <div style={{ display: "flex", justifyContent: "space-between", alignItems: "flex-start", gap: 12 }}>
          <div>
            <div style={{ fontFamily: "var(--mono)", fontSize: 11, letterSpacing: "0.18em", color: soc.dyn ? accent : layerColor(soc.couche), marginBottom: 8 }}>
              {soc.dyn ? "★ RECHERCHE · DONNÉES LIVE" : `${soc.couche} · ${layer ? layer.nom.toUpperCase() : ""}${seg ? " — " + seg.nom : ""}`}
            </div>
            <div style={{ fontFamily: "var(--serif)", fontSize: 30, fontWeight: 500, lineHeight: 1.05, color: "var(--ink)" }}>
              {soc.nom}
            </div>
            <div style={{ fontFamily: "var(--mono)", fontSize: 13, color: "oklch(0.6 0.02 80)", marginTop: 6 }}>
              {soc.ticker} · {soc.place}
            </div>
          </div>
          <button onClick={onClose} aria-label="Fermer" style={{
            fontFamily: "var(--mono)", fontSize: 13, color: "var(--ink)", background: "none",
            border: "1px solid oklch(0.35 0.01 265)", borderRadius: 999, width: 32, height: 32,
            cursor: "pointer", flex: "none",
          }}>✕</button>
        </div>
        <div style={{ display: "flex", gap: 8, marginTop: 16, flexWrap: "wrap" }}>
          <span style={{
            fontFamily: "var(--mono)", fontSize: 10.5, letterSpacing: "0.1em", padding: "4px 10px",
            borderRadius: 999, background: `oklch(0.26 0.03 ${hue})`, color: `oklch(0.8 0.06 ${hue})`,
          }}>
            {soc.taille} — {{ MEGA: "> 200 Md$", LARGE: "20–200 Md$", MID: "2–20 Md$", SMALL: "< 2 Md$" }[soc.taille]}
          </span>
          <span title={PROFIL_DESC[soc.profil]} style={{
            fontFamily: "var(--mono)", fontSize: 10.5, letterSpacing: "0.1em", padding: "4px 10px",
            borderRadius: 999,
            border: soc.profil === "SPEC" ? `1px dashed ${accent}` : "1px solid oklch(0.4 0.01 265)",
            color: soc.profil === "DISRUPTE" ? "oklch(0.7 0.14 25)" : soc.profil === "SPEC" ? accent : "var(--ink)",
          }}>{PROFIL_LABEL[soc.profil]}</span>
        </div>
      </div>
      <div style={{ padding: "22px 30px", overflowY: "auto", flex: 1 }}>
        <SecteurResume soc={soc} />
        <ActiviteFMP soc={soc} />
        {soc.detail && !soc.dyn ? (
          <div>
            <div style={{ fontFamily: "var(--mono)", fontSize: 10.5, letterSpacing: "0.2em", color: "oklch(0.55 0.02 80)", marginBottom: 10 }}>POINT TECHNIQUE</div>
            <p style={{ fontFamily: "var(--sans)", fontSize: 14.5, lineHeight: 1.65, margin: "0 0 24px", color: "oklch(0.78 0.01 80)", textWrap: "pretty" }}>
              {soc.detail}
            </p>
          </div>
        ) : null}
        {PROFIL_DESC[soc.profil] ? (
          <p style={{
            fontFamily: "var(--sans)", fontSize: 12.5, lineHeight: 1.5, color: "oklch(0.6 0.02 80)",
            borderTop: "1px solid oklch(0.25 0.01 265)", paddingTop: 14, margin: "0 0 24px",
          }}>
            Profil « {PROFIL_LABEL[soc.profil]} » : {PROFIL_DESC[soc.profil].toLowerCase()}.
          </p>
        ) : null}
        <ProfilFinnhub soc={soc} accent={accent} />
        <NewsSociete soc={soc} accent={accent} />
        {voisins.length ? (
          <div style={{ borderTop: "1px solid oklch(0.25 0.01 265)", paddingTop: 18 }}>
            <div style={{ fontFamily: "var(--mono)", fontSize: 10.5, letterSpacing: "0.2em", color: "oklch(0.55 0.02 80)", marginBottom: 12 }}>
              MÊME SOUS-SEGMENT
            </div>
            <div style={{ display: "flex", flexWrap: "wrap", gap: 7 }}>
              {voisins.map((v) => (
                <ChipSociete key={v.id} soc={v} accent={accent} onClick={() => onSelectSoc(v)} />
              ))}
            </div>
          </div>
        ) : null}
      </div>
    </aside>
  );
}

Object.assign(window, { PanneauSociete });
