/* FoxBot Pro — base components: Sparkline, PnLDisplay, ScoreBadge, PairLogo */

const { useState, useEffect, useRef, useMemo } = React;

/* ─── Animated count-up ─────────────────────────────── */
function useCountUp(target, duration = 800, decimals = 2) {
  const [v, setV] = useState(target);
  const prev = useRef(target);
  useEffect(() => {
    const from = prev.current; const to = target;
    if (from === to) return;
    const start = performance.now();
    let raf;
    const tick = (now) => {
      const t = Math.min(1, (now - start) / duration);
      const eased = 1 - Math.pow(1 - t, 3);
      setV(from + (to - from) * eased);
      if (t < 1) raf = requestAnimationFrame(tick);
      else prev.current = to;
    };
    raf = requestAnimationFrame(tick);
    return () => cancelAnimationFrame(raf);
  }, [target, duration]);
  return v;
}

/* ─── Number formatting ─────────────────────────────── */
function fmtNum(n, opts = {}) {
  const { decimals, sign = false } = opts;
  if (n === null || n === undefined || isNaN(n)) return "—";
  const abs = Math.abs(n);
  let d = 2;
  if (abs < 0.01) d = 5;
  else if (abs < 1) d = 4;
  else if (abs < 1000) d = 2;
  else d = 2;
  if (decimals !== undefined) d = decimals;
  const s = n.toLocaleString("en-US", { minimumFractionDigits: d, maximumFractionDigits: d });
  return sign && n > 0 ? "+" + s : s;
}
function fmtEur(n, sign = false) {
  if (n === null || n === undefined) return "—";
  const s = Math.abs(n).toLocaleString("fr-FR", { minimumFractionDigits: 2, maximumFractionDigits: 2 });
  return (n < 0 ? "−" : (sign && n > 0 ? "+" : "")) + s + " €";
}
function fmtPct(n, sign = true, decimals = 2) {
  if (n === null || n === undefined) return "—";
  const s = Math.abs(n).toFixed(decimals);
  return (n < 0 ? "−" : (sign ? "+" : "")) + s + "%";
}

/* ─── Sparkline ─────────────────────────────────────── */
function Sparkline({ data, width = 80, height = 24, color = "var(--accent-400)", fill = true, animated = true }) {
  const path = useMemo(() => {
    if (!data || data.length < 2) return { line: "", area: "", points: [] };
    const min = Math.min(...data); const max = Math.max(...data);
    const range = max - min || 1;
    const points = data.map((v, i) => [
      (i / (data.length - 1)) * width,
      height - 2 - ((v - min) / range) * (height - 4)
    ]);
    const line = points.map(([x, y], i) => (i === 0 ? `M${x},${y}` : `L${x},${y}`)).join(" ");
    const area = `${line} L${width},${height} L0,${height} Z`;
    return { line, area, points };
  }, [data, width, height]);

  const last = data[data.length - 1];
  const first = data[0];
  const up = last >= first;
  const c = color === "auto" ? (up ? "var(--bull)" : "var(--bear)") : color;
  const gradId = useMemo(() => `spark-${Math.random().toString(36).slice(2, 9)}`, []);

  return (
    <svg width={width} height={height} style={{ display: "block", overflow: "visible" }}>
      <defs>
        <linearGradient id={gradId} x1="0" y1="0" x2="0" y2="1">
          <stop offset="0%" stopColor={c} stopOpacity="0.32" />
          <stop offset="100%" stopColor={c} stopOpacity="0" />
        </linearGradient>
      </defs>
      {fill && <path d={path.area} fill={`url(#${gradId})`} />}
      <path
        d={path.line}
        stroke={c}
        strokeWidth="1.5"
        fill="none"
        strokeLinecap="round"
        strokeLinejoin="round"
        style={animated ? { strokeDasharray: 200, strokeDashoffset: 200, animation: "spark-draw 800ms 80ms ease-out forwards" } : {}}
      />
      {path.points.length > 0 && (
        <circle
          cx={path.points[path.points.length - 1][0]}
          cy={path.points[path.points.length - 1][1]}
          r="2"
          fill={c}
        />
      )}
    </svg>
  );
}
// Inject keyframe once
if (typeof document !== "undefined" && !document.getElementById("spark-kf")) {
  const s = document.createElement("style");
  s.id = "spark-kf";
  s.textContent = `@keyframes spark-draw { to { stroke-dashoffset: 0; } }`;
  document.head.appendChild(s);
}

/* ─── PnL display (animated, variant sizing) ────────── */
function PnLDisplay({ value, pct, variant = "md", showPct = true, prefix = "€" }) {
  const animated = useCountUp(value);
  const animPct = useCountUp(pct || 0);
  const up = value >= 0;
  const sizes = {
    sm: { num: 18, pct: 11 },
    md: { num: 28, pct: 12 },
    lg: { num: 36, pct: 13 },
    hero: { num: 48, pct: 14 },
  };
  const s = sizes[variant];
  const stackPct = variant === "md" || variant === "sm";

  return (
    <div style={{ display: "flex", flexDirection: stackPct ? "column" : "row", alignItems: stackPct ? "flex-start" : "baseline", gap: stackPct ? 2 : 10, whiteSpace: "nowrap", minWidth: 0 }}>
      <span
        className="mono"
        style={{
          fontSize: s.num,
          fontWeight: 600,
          letterSpacing: "-0.04em",
          lineHeight: 1.1,
          color: up ? "var(--bull)" : "var(--bear)",
          textShadow: variant === "hero" ? `0 0 32px ${up ? "var(--bull-glow)" : "var(--bear-glow)"}` : "none",
          whiteSpace: "nowrap",
        }}
      >
        {up ? "+" : "−"}{prefix}{Math.abs(animated).toLocaleString("fr-FR", { minimumFractionDigits: 2, maximumFractionDigits: 2 })}
      </span>
      {showPct && pct !== undefined && (
        <span className="mono" style={{ fontSize: s.pct, color: up ? "var(--bull)" : "var(--bear)", opacity: 0.85 }}>
          {fmtPct(animPct)}
        </span>
      )}
    </div>
  );
}

/* ─── Score badge ───────────────────────────────────── */
function ScoreBadge({ score, size = "md" }) {
  let cls = "score-low";
  if (score >= 9) cls = "score-premium";
  else if (score >= 8) cls = "score-good";
  else if (score >= 7) cls = "score-mid";
  return (
    <span className={`score-badge ${cls}`} style={size === "lg" ? { fontSize: 14, padding: "5px 12px 5px 10px" } : {}}>
      <span className="dot"></span>
      <span className="num">{score.toFixed(1)}</span>
      <span className="max">/10</span>
    </span>
  );
}

/* ─── Pair logo (color circle with symbol) ──────────── */
function PairLogo({ pair, size = 28 }) {
  const data = window.MOCK.PAIRS[pair] || { color: "#666", sym: pair };
  const sym = (pair || "?").slice(0, Math.min(4, pair.length));
  return (
    <div
      className="pair-logo"
      style={{
        width: size, height: size,
        background: `linear-gradient(135deg, ${data.color} 0%, ${shade(data.color, -20)} 100%)`,
        fontSize: size <= 24 ? 9 : 11,
      }}
    >
      {sym}
    </div>
  );
}
function shade(hex, amt) {
  const c = parseInt(hex.slice(1), 16);
  let r = Math.max(0, Math.min(255, ((c >> 16) & 0xff) + amt));
  let g = Math.max(0, Math.min(255, ((c >> 8) & 0xff) + amt));
  let b = Math.max(0, Math.min(255, (c & 0xff) + amt));
  return "#" + ((r << 16) | (g << 8) | b).toString(16).padStart(6, "0");
}

/* ─── Direction badge ───────────────────────────────── */
function DirBadge({ dir }) {
  const isLong = dir === "LONG";
  return (
    <span className={`dir-badge ${isLong ? "dir-long" : "dir-short"}`}>
      {isLong ? <Icon.ArrowUp size={11} /> : <Icon.ArrowDown size={11} />}
      {dir}
    </span>
  );
}

/* ─── Toast system ──────────────────────────────────── */
const TOAST_BUS = { listeners: new Set() };
function emitToast(toast) { TOAST_BUS.listeners.forEach(fn => fn(toast)); }
function ToastHost() {
  const [items, setItems] = useState([]);
  useEffect(() => {
    const onToast = (t) => {
      const id = Math.random().toString(36).slice(2);
      setItems((prev) => [...prev, { ...t, id }]);
      setTimeout(() => setItems((prev) => prev.filter(x => x.id !== id)), t.duration || 3800);
    };
    TOAST_BUS.listeners.add(onToast);
    return () => TOAST_BUS.listeners.delete(onToast);
  }, []);
  return (
    <div className="toasts">
      {items.map(t => (
        <div key={t.id} className={`toast ${t.type || "info"}`}>
          <span className="toast-icon">
            {t.type === "success" ? <Icon.Check size={16} /> : t.type === "warn" ? <Icon.AlertTriangle size={16}/> : t.type === "error" ? <Icon.X size={16}/> : <Icon.Sparkles size={16}/>}
          </span>
          <div style={{ display: "flex", flexDirection: "column", gap: 2 }}>
            <span className="toast-msg">{t.msg}</span>
            {t.sub && <span className="toast-sub">{t.sub}</span>}
          </div>
        </div>
      ))}
    </div>
  );
}

/* ─── Risk level calculation (multi-factor, 0-13 → 4 buckets) ─── */
function calcRiskLevel(signal) {
  if (!signal) return { level: "MODERE", score: 6, factors: {} };
  let s = 0;
  const factors = {};

  // 1. ATR — volatilité instantanée
  const atr = Number(signal.atr) || 0;
  if (atr < 0.3)      { s += 0; factors.atr = "calme"; }
  else if (atr < 1.5) { s += 1; factors.atr = "normal"; }
  else if (atr < 3.0) { s += 2; factors.atr = "volatil"; }
  else                { s += 3; factors.atr = "extrême"; }

  // 2. SL distance — stop trop serré OU trop large = fragile
  const slPct = Math.abs(Number(signal.slPct) || 0);
  if (slPct === 0)       { s += 2; factors.sl = "inconnu"; }
  else if (slPct < 0.4)  { s += 2; factors.sl = "trop serré"; }
  else if (slPct < 1.5)  { s += 1; factors.sl = "raisonnable"; }
  else if (slPct < 3.0)  { s += 0; factors.sl = "confortable"; }
  else                   { s += 2; factors.sl = "trop large"; }

  // 3. Levier
  const lev = Number(signal.leverage) || 10;
  if (lev <= 3)       s += 0;
  else if (lev <= 5)  s += 1;
  else if (lev <= 10) s += 2;
  else                s += 3;
  factors.lev = `x${lev}`;

  // 4. Volume (liquidité)
  const vol = Number(signal.volume) || 0;
  if (vol >= 100)     { s += 0; factors.vol = "fort"; }
  else if (vol >= 50) { s += 1; factors.vol = "moyen"; }
  else                { s += 2; factors.vol = "faible"; }

  // 5. Score inverse (faible score = setup fragile)
  const sc = Number(signal.score) || 0;
  if (sc >= 9)      s += 0;
  else if (sc >= 8) s += 1;
  else if (sc >= 7) s += 2;
  else              s += 3;
  factors.score = sc.toFixed(1);

  // 0-13 → 4 buckets
  let level;
  if (s <= 3)      level = "FAIBLE";
  else if (s <= 6) level = "MODERE";
  else if (s <= 9) level = "ELEVE";
  else             level = "EXTREME";
  return { level, score: s, factors };
}
window.calcRiskLevel = calcRiskLevel;

function RiskBadge({ signal, size = "md" }) {
  const { level } = calcRiskLevel(signal);
  const cls = "risk-badge risk-" + level.toLowerCase();
  const lvlLabel = level === "MODERE" ? "MODÉRÉ" : level === "ELEVE" ? "ÉLEVÉ" : level;
  const icon = level === "EXTREME" ? "⚠️ " : "";
  return (
    <span className={cls} style={size === "sm" ? { fontSize: 10, padding: "2px 6px" } : undefined}>
      <span className="dot"></span>
      {icon}{lvlLabel}
    </span>
  );
}
window.RiskBadge = RiskBadge;

Object.assign(window, { Sparkline, PnLDisplay, ScoreBadge, PairLogo, DirBadge, ToastHost, emitToast, useCountUp, fmtNum, fmtEur, fmtPct, calcRiskLevel, RiskBadge });
