/* ============================================================
   SIKA COCKPIT — Primitives partagées (formatage, marqueurs, viz)
   ============================================================ */
const { useState, useRef, useEffect, useMemo, useCallback } = React;

// ---- Formatage ----------------------------------------------------------
const eur = (n, d = 0) => "€" + Number(n).toLocaleString("fr-FR", { minimumFractionDigits: d, maximumFractionDigits: d });
const eurK = (n) => {
  const a = Math.abs(n);
  if (a >= 1000) return "€" + (n / 1000).toLocaleString("fr-FR", { maximumFractionDigits: 1 }) + "k";
  return eur(n);
};
const num = (n, d = 0) => Number(n).toLocaleString("fr-FR", { minimumFractionDigits: d, maximumFractionDigits: d });
const pct = (n, d = 0) => num(n, d) + "%";
const wks = (n) => num(n, 1) + " sem";

// ---- Tooltip global -----------------------------------------------------
const TipCtx = React.createContext(null);
function TipProvider({ children }) {
  const [tip, setTip] = useState(null);
  const show = useCallback((content, e) => {
    const x = e.clientX, y = e.clientY;
    setTip({ content, x, y });
  }, []);
  const hide = useCallback(() => setTip(null), []);
  return (
    <TipCtx.Provider value={{ show, hide }}>
      {children}
      {tip && (
        <div className="tip-pop" style={{
          left: Math.min(tip.x + 14, window.innerWidth - 296),
          top: Math.min(tip.y + 16, window.innerHeight - 120),
        }}>{tip.content}</div>
      )}
    </TipCtx.Provider>
  );
}
function useTip() {
  const ctx = React.useContext(TipCtx);
  return {
    bind: (content) => ({
      onMouseEnter: (e) => ctx && ctx.show(content, e),
      onMouseMove: (e) => ctx && ctx.show(content, e),
      onMouseLeave: () => ctx && ctx.hide(),
    }),
  };
}

// ---- Feu statut (rouge/orange/vert + glyphe pour daltonisme) ------------
const LIGHT_GLYPH = { red: "▲", amber: "■", green: "●" };
const LIGHT_LABEL = { red: "Critique", amber: "Surveiller", green: "Sain" };
function Light({ level, title }) {
  const { bind } = useTip();
  return (
    <span className={"light " + level} {...bind(
      <span><b>{LIGHT_LABEL[level]}</b>{title ? " — " + title : ""}</span>
    )}>{LIGHT_GLYPH[level]}</span>
  );
}

// ---- Marqueur nature (réel / estimé / proxy / hypothèse) ----------------
const NAT = {
  "réel": { cls: "nat-real", t: "Donnée réelle, rapportée par l'API." },
  "estimé": { cls: "nat-est", t: "Estimation (modèle/algorithme côté Amazon ou Keepa)." },
  "proxy": { cls: "nat-proxy", t: "Proxy : valeur dérivée, pas la donnée native." },
  "hypothèse": { cls: "nat-hyp", t: "Hypothèse Sika, paramétrable dans le panneau d'hypothèses." },
  "indisponible": { cls: "nat-na", t: "Donnée non exposée par l'API." },
};
const NAT_SHORT = { "réel": "RÉEL", "estimé": "EST", "proxy": "PROXY", "hypothèse": "HYP", "indisponible": "N/D" };
function Nature({ kind, note }) {
  const { bind } = useTip();
  const n = NAT[kind] || NAT["estimé"];
  return <span className={"nat " + n.cls} {...bind(<span><b>{NAT_SHORT[kind]}</b> — {note || n.t}</span>)}>{NAT_SHORT[kind]}</span>;
}

// ---- Badge fraîcheur ----------------------------------------------------
function freshClass(ageH) {
  if (ageH == null) return "na";
  if (ageH <= 6) return "ok";
  if (ageH <= 48) return "warn";
  return "stale";
}
function ageLabel(ageH) {
  if (ageH == null) return "n/d";
  if (ageH < 1) return Math.round(ageH * 60) + " min";
  if (ageH < 72) return Math.round(ageH) + " h";
  return Math.round(ageH / 24) + " j";
}
function Fresh({ src, compact }) {
  const { bind } = useTip();
  const cls = freshClass(src.ageHours);
  return (
    <span className={"fresh " + cls} {...bind(
      <span>
        <b>{src.report}</b><br />
        {src.endpoint}<br />
        Généré&nbsp;: <span className="mono">{src.generatedAt ? new Date(src.generatedAt).toLocaleString("fr-FR") : "—"}</span><br />
        Âge&nbsp;: <span className="mono">{ageLabel(src.ageHours)}</span> · SLA {src.sla}<br />
        Nature&nbsp;: {src.nature}
      </span>
    )}>
      <span className="dot"></span>{compact ? ageLabel(src.ageHours) : (src.nature === "indisponible" ? "non accordé" : ageLabel(src.ageHours))}
    </span>
  );
}

// ---- Cellule inspectable (ouvre le Data Inspector N4) -------------------
function Inspectable({ payload, children, className }) {
  const { bind } = useTip();
  return (
    <span
      className={"inspectable " + (className || "")}
      {...bind(<span>Clic droit / clic = <b>inspecter la source</b> (N4)</span>)}
      onClick={(e) => { e.stopPropagation(); window.SikaUI && window.SikaUI.inspect(payload); }}
      onContextMenu={(e) => { e.preventDefault(); e.stopPropagation(); window.SikaUI && window.SikaUI.inspect(payload); }}
    >{children}</span>
  );
}

// ---- Formula chip -------------------------------------------------------
function Formula({ children }) { return <div className="formula">{children}</div>; }

// ---- Sparkline ----------------------------------------------------------
function Sparkline({ data, w = 64, h = 18, color = "var(--accent)", fill = false, k = "v" }) {
  const vals = data.map((d) => (typeof d === "number" ? d : d[k]));
  const min = Math.min(...vals), max = Math.max(...vals);
  const rng = max - min || 1;
  const pts = vals.map((v, i) => [i / (vals.length - 1) * w, h - ((v - min) / rng) * (h - 2) - 1]);
  const d = pts.map((p, i) => (i ? "L" : "M") + p[0].toFixed(1) + " " + p[1].toFixed(1)).join(" ");
  return (
    <svg className="spark" width={w} height={h} viewBox={`0 0 ${w} ${h}`}>
      {fill && <path d={d + ` L${w} ${h} L0 ${h} Z`} fill={color} opacity=".12" />}
      <path d={d} fill="none" stroke={color} strokeWidth="1.4" />
      <circle cx={pts[pts.length - 1][0]} cy={pts[pts.length - 1][1]} r="1.7" fill={color} />
    </svg>
  );
}

// ---- Mini bar series (pour ventes etc.) --------------------------------
function Bars({ data, k = "v", w = 220, h = 56, color = "var(--accent)", overlay, ok = "var(--ink-3)" }) {
  const vals = data.map((d) => d[k]);
  const max = Math.max(...vals, ...(overlay ? data.map((d) => d[overlay] || 0) : [0])) || 1;
  const bw = w / data.length;
  return (
    <svg width={w} height={h} viewBox={`0 0 ${w} ${h}`} style={{ display: "block" }}>
      {data.map((d, i) => {
        const bh = (d[k] / max) * (h - 4);
        return <rect key={i} x={i * bw + 1} y={h - bh} width={bw - 2} height={bh} rx="1" fill={color} opacity=".85" />;
      })}
      {overlay && data.map((d, i) => d[overlay] != null && (
        <line key={"o" + i} x1={i * bw + 1} x2={i * bw + bw - 1}
          y1={h - (d[overlay] / max) * (h - 4)} y2={h - (d[overlay] / max) * (h - 4)}
          stroke={ok} strokeWidth="1.6" />
      ))}
    </svg>
  );
}

// ---- JSON syntax highlight ---------------------------------------------
function hl(obj) {
  const json = JSON.stringify(obj, null, 2);
  return json
    .replace(/&/g, "&amp;").replace(/</g, "&lt;")
    .replace(/("(\\.|[^"\\])*")(\s*:)?|\b(true|false)\b|\bnull\b|-?\d+(\.\d+)?([eE][+-]?\d+)?/g, (m, p1, p2, p3, p4) => {
      let cls = "num";
      if (p1) cls = p3 ? "key" : "str";
      else if (p4) cls = "bool";
      else if (m === "null") cls = "null";
      return `<span class="${cls}">${m}</span>`;
    });
}

Object.assign(window, {
  eur, eurK, num, pct, wks,
  TipProvider, useTip, Light, Nature, Fresh, Inspectable, Formula, Sparkline, Bars, hl,
  freshClass, ageLabel, LIGHT_LABEL,
});
