// ====================================================================
// UI — iconos, chrome (Sidebar, TopBar) y átomos (KPI, Badge, etc.)
// ====================================================================
const ICON = {
  summary:   "M3 13h8V3H3v10zm0 8h8v-6H3v6zm10 0h8V11h-8v10zm0-18v6h8V3h-8z",
  mentions:  "M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2v10z",
  competitors:"M16 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2 M8.5 11a4 4 0 1 0 0-8 4 4 0 0 0 0 8z M22 21v-2a4 4 0 0 0-3-3.87 M16 3.13a4 4 0 0 1 0 7.75",
  collection:"M3 3v18h18 M7 16l4-4 4 3 5-6",
  narratives:"M3 3v18h18 M18 9l-5 5-4-4-5 5",
  alerts:    "M10.29 3.86 1.82 18a2 2 0 0 0 1.71 3h16.94a2 2 0 0 0 1.71-3L13.71 3.86a2 2 0 0 0-3.42 0z M12 9v4 M12 17h.01",
  search:    "M11 19a8 8 0 1 0 0-16 8 8 0 0 0 0 16z M21 21l-4.35-4.35",
  expand:    "M15 3h6v6 M9 21H3v-6 M21 3l-7 7 M3 21l7-7",
  x:         "M18 6 6 18 M6 6l12 12",
  external:  "M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6 M15 3h6v6 M10 14 21 3",
  arrowUp:   "M12 19V5 M5 12l7-7 7 7",
  arrowDown: "M12 5v14 M19 12l-7 7-7-7",
  check:     "M20 6 9 17l-5-5",
  refresh:   "M23 4v6h-6 M1 20v-6h6 M3.51 9a9 9 0 0 1 14.85-3.36L23 10 M1 14l4.64 4.36A9 9 0 0 0 20.49 15",
  copy:      "M9 9h10a2 2 0 0 1 2 2v10a2 2 0 0 1-2 2H9a2 2 0 0 1-2-2V11a2 2 0 0 1 2-2z M5 15H4a2 2 0 0 1-2-2V3a2 2 0 0 1 2-2h10a2 2 0 0 1 2 2v1",
  send:      "M22 2 11 13 M22 2l-7 20-4-9-9-4 20-7z",
  spark:     "M13 2 3 14h9l-1 8 10-12h-9l1-8z",
  shield:    "M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z",
  flame:     "M8.5 14.5A2.5 2.5 0 0 0 11 12c0-1.38-.5-2-1-3-1.072-2.143-.224-4.054 2-6 .5 2.5 2 4.9 4 6.5 2 1.6 3 3.5 3 5.5a7 7 0 1 1-14 0c0-1.153.433-2.294 1-3a2.5 2.5 0 0 0 2.5 2.5z",
  clock:     "M12 22a10 10 0 1 0 0-20 10 10 0 0 0 0 20z M12 6v6l4 2",
  target:    "M12 22a10 10 0 1 0 0-20 10 10 0 0 0 0 20z M12 18a6 6 0 1 0 0-12 6 6 0 0 0 0 12z M12 14a2 2 0 1 0 0-4 2 2 0 0 0 0 4z",
  ban:       "M12 22a10 10 0 1 0 0-20 10 10 0 0 0 0 20z M4.93 4.93l14.14 14.14",
  bulb:      "M9 18h6 M10 22h4 M12 2a7 7 0 0 0-4 12.7c.6.5 1 1.2 1 2V18h6v-1.3c0-.8.4-1.5 1-2A7 7 0 0 0 12 2z",
  list:      "M8 6h13 M8 12h13 M8 18h13 M3 6h.01 M3 12h.01 M3 18h.01",
  compass:   "M12 22a10 10 0 1 0 0-20 10 10 0 0 0 0 20z M16.24 7.76l-2.12 6.36-6.36 2.12 2.12-6.36 6.36-2.12z",
  star:      "M12 2l3.09 6.26L22 9.27l-5 4.87 1.18 6.88L12 17.77l-6.18 3.25L7 14.14 2 9.27l6.91-1.01L12 2z",
  arrowRight:"M5 12h14 M12 5l7 7-7 7",
};
function Ic({ name, style }) {
  return (
    <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.8" strokeLinecap="round" strokeLinejoin="round" style={style}>
      {ICON[name].split(" M").map((seg, i) => <path key={i} d={(i === 0 ? "" : "M") + seg} />)}
    </svg>
  );
}

// ---------- Badge de sentimiento ----------
function SentBadge({ s, t, severity }) {
  const negLabel = (severity !== undefined && s === "neg")
    ? (severity >= 75 ? t("negative_severe") : severity < 45 ? t("negative_mild") : t("negative"))
    : t("negative");
  const map = { pos: ["pos", t("positive")], neg: ["neg", negLabel], neu: ["neu", t("neutral")] };
  const [cls, label] = map[s] || map.neu;
  return <span className={`badge ${cls}`}><span className="bdot"></span>{label}</span>;
}

// ---------- Severidad ----------
function Severity({ value }) {
  const val = Math.min(100, Math.max(0, Number(value || 0)));
  const angle = (val / 100) * 180 - 180; // degrees from -180 to 0
  const rad = (angle * Math.PI) / 180;
  const r = 13; // needle length
  const tx = 23 + r * Math.cos(rad);
  const ty = 22 + r * Math.sin(rad);
  const color = val >= 75 ? "var(--neg)" : val >= 45 ? "var(--gold)" : "var(--brand)";
  
  return (
    <span className="sev-gauge-container" style={{ display: "inline-flex", flexDirection: "column", alignItems: "center", width: 50, verticalAlign: "middle", lineHeight: 1, padding: "2px 0" }}>
      <svg width="46" height="24" viewBox="0 0 46 24" style={{ overflow: "visible" }}>
        {/* Background track (semi-circle arc) */}
        <path d="M 6,22 A 17,17 0 0,1 40,22" fill="none" stroke="rgba(255,255,255,0.15)" strokeWidth="3.5" strokeLinecap="round" />
        {/* Active colored track */}
        {val > 0 && (
          <path d={`M 6,22 A 17,17 0 0,1 ${23 + 17*Math.cos(rad)},${23 + 17*Math.sin(rad)}`} fill="none" stroke={color} strokeWidth="3.5" strokeLinecap="round" />
        )}
        {/* Center pivot */}
        <circle cx="23" cy="22" r="2.5" fill="#fff" />
        {/* Needle pointer */}
        <line x1="23" y1="22" x2={tx} y2={ty} stroke="#fff" strokeWidth="2" strokeLinecap="round" />
      </svg>
      <span style={{ fontSize: "10px", fontWeight: "700", color: color, marginTop: "2px", fontFamily: "var(--font-mono)", letterSpacing: "-0.5px" }}>{val}</span>
    </span>
  );
}

// ---------- Logos de tienda (App Store / Play Store) ----------
// size: 'sm' (dentro de .ic 22px) | 'md' (heatmap header, badges)
function StoreGlyph({ glyph, size = "sm" }) {
  const px = size === "md" ? 15 : 13;
  if (glyph === "apple")
    return (
      <svg viewBox="0 0 24 24" width={px} height={px} fill="#fff" style={{ display:"block" }}>
        <path d="M16.365 1.43c0 1.14-.49 2.27-1.18 3.08-.74.9-1.99 1.57-2.99 1.49-.12-1.12.49-2.31 1.18-3.07.77-.86 2.09-1.5 2.99-1.5zM20.9 17.14c-.03.07-.46 1.58-1.52 3.12-.94 1.34-1.94 2.71-3.43 2.71-1.5 0-1.9-.88-3.63-.88-1.7 0-2.3.91-3.67.91-1.38 0-2.33-1.26-3.43-2.8C2.32 18.06 1.5 14.6 2.95 12.26c.85-1.4 2.38-2.28 4.02-2.3 1.45-.03 2.66.96 3.6.96.86 0 2.34-1.19 3.95-1.01.67.03 2.56.27 3.77 2.04-.1.06-2.25 1.32-2.23 3.93.03 3.12 2.73 4.16 2.84 4.26z"/>
      </svg>
    );
  if (glyph === "gplay")
    return (
      <svg viewBox="0 0 24 24" width={px} height={px} fill="#fff" style={{ display:"block" }}>
        <path d="M4 3l13 9-13 9z"/>
      </svg>
    );
  return null;
}

// ---------- Source tag (ícono plataforma + nombre) ----------
function SourceTag({ platform, name }) {
  const p = PLATFORMS[platform] || { color: "#888", abbr: "?", label: platform };
  return (
    <span className="src">
      <span className="ic" style={{ background: p.color }}>{p.glyph ? <StoreGlyph glyph={p.glyph} /> : p.abbr}</span>
      {name || p.label}
    </span>
  );
}

// ---------- KPI ----------
const KPI_TONE = {
  risk:  { c: "var(--neg)", bg: "var(--neg-bg)" },
  blue:  { c: "var(--blue)", bg: "var(--blue-bg)" },
  brand: { c: "var(--brand)", bg: "var(--pos-bg)" },
};
function KPI({ icon, label, value, unit, trend, dir, tone, spark }) {
  const tdir = dir === "up" ? "up" : dir === "down" ? "down" : "flat";
  const tk = KPI_TONE[tone] || KPI_TONE.blue;
  return (
    <div className="card kpi fade-in">
      <span className="accent-bar" style={{ background: tk.c }}></span>
      <div className="kpi-top">
        <span className="kpi-ic" style={{ background: tk.bg, color: tk.c }}><Ic name={icon} /></span>
        <div className="label">{label}</div>
      </div>
      <div className="value">{value}{unit && <span className="unit">{unit}</span>}</div>
      <div className="kpi-foot">
        <div className={`trend ${tdir}`}>
          {dir !== "flat" && <Ic name={dir === "up" ? "arrowUp" : "arrowDown"} style={{ width: 14, height: 14 }} />}
          {trend > 0 ? "+" : ""}{trend}{unit === "%" ? "pp" : ""}
        </div>
        {spark && <Sparkline data={spark} color={tk.c} w={84} h={30} />}
      </div>
    </div>
  );
}

// ---------- Sidebar ----------
const NAV = [
  { id: "competitors", icon: "competitors", key: "nav_competitors" },
  { id: "summary", icon: "summary", key: "nav_summary" },
  { id: "deep_research", icon: "search", key: "deep_research_btn" },
];
function Sidebar({ active, onNav, onKiosk }) {
  const { t } = useLang();
  const alertCount = MOCK.ALERTS.filter(a => a.level === "crisis").length + 1;
  const health = 100 - MOCK.KPIS.risk.value;
  const hColor = health >= 60 ? "var(--brand)" : health >= 35 ? "var(--gold)" : "var(--neg)";
  return (
    <aside className="sidebar">
      <div className="brand">
        <img className="brand-logo" src="/logo.png" alt="QFIN" />
        <div style={{ borderLeft: "1px solid rgba(255,255,255,0.15)", paddingLeft: "12px", marginLeft: "4px", display: "flex", alignItems: "center" }}>
          <img src="/logos/qcrece.png" alt="Qcrece" style={{
            height: "30px", width: "auto", display: "block",
            borderRadius: "8px",
            boxShadow: "0 2px 12px rgba(0,0,0,0.45), 0 0 0 1px rgba(255,255,255,0.08)"
          }} />
        </div>
      </div>
      <div className="nav-label" style={{ display: "flex", alignItems: "center", padding: "10px 10px 8px" }}>
        <span style={{
          fontFamily: "var(--font-head)", fontWeight: 800, fontSize: "13.5px",
          letterSpacing: "0.14em", textTransform: "uppercase",
          color: "rgba(255,255,255,0.88)", lineHeight: 1, display: "inline-flex", alignItems: "baseline", gap: "1px"
        }}>
          BLACKWELL<span style={{ color: "var(--neg)", fontSize: "16px", fontWeight: 900, letterSpacing: 0, lineHeight: 1 }}>.</span>
        </span>
      </div>
      <nav className="nav">
        {NAV.map(n => {
          const Tilt3D = window.Tilt3D || (({ children, ...p }) => <div {...p}>{children}</div>);
          return (
            <Tilt3D key={n.id} max={15} style={{ display: 'block', borderRadius: '11px', overflow: 'hidden' }}>
              <button className={`nav-item ${active === n.id ? "active" : ""}`} onClick={() => onNav(n.id)} style={{ width: '100%' }}>
                <Ic name={n.icon} />
                <span className="nav-txt">{t(n.key)}</span>
                {n.badge && <span className="badge-count">{alertCount}</span>}
              </button>
            </Tilt3D>
          );
        })}
      </nav>
      <div className="sidebar-foot">
        <div className="sidebar-meta">
          <div className="lab">{t("health")}</div>
          <div className="big" style={{ color: hColor }}>{health}<span className="u">/100</span></div>
          <div className="row"><span className="pill" style={{ background: hColor }}></span>{t("monitored")}</div>
        </div>
        <button className="kiosk-btn" onClick={onKiosk}>
          <Ic name="expand" /><span className="kiosk-txt">{t("kiosk")}</span>
        </button>
      </div>
    </aside>
  );
}

// ---------- TopBar ----------
function TopBar({ title, searchQuery, setSearchQuery, isSearchable }) {
  const { t, lang, setLang } = useLang();
  return (
    <header className="topbar">
      <div className="titles">
        <h1>{title}</h1>
      </div>
      <div className="spacer"></div>
      {isSearchable ? (
        <div className="searchbox">
          <Ic name="search" />
          <input
            placeholder={t("search")}
            value={searchQuery || ""}
            onChange={e => setSearchQuery(e.target.value)}
          />
        </div>
      ) : (
        <div style={{ width: 220 }}></div>
      )}
      <div className="lang-toggle">
        <button className={lang === "es" ? "active" : ""} onClick={() => setLang("es")}>ES</button>
        <button className={lang === "en" ? "active" : ""} onClick={() => setLang("en")}>EN</button>
        <button className={lang === "zh" ? "active" : ""} onClick={() => setLang("zh")}>中文</button>
      </div>
    </header>
  );
}

// ---------- Loading overlay (React, ingest manual) ----------
function LoadingOverlay({ progress = 0, details = "", errMsg = "" }) {
  const { t } = useLang();
  return (
    <div className="qo-loading-overlay">
      <img src="/logo.png" alt="QFIN" className="l-logo" style={{ height: 38, opacity: 0.85 }} />
      <div className="loading-spinner"></div>
      <div className="l-title">{t("loading_title")}</div>
      
      {/* Barra de progreso real */}
      {!errMsg && (
        <div style={{
          width: "280px",
          height: "6px",
          background: "rgba(160,180,220,.12)",
          borderRadius: "4px",
          overflow: "hidden",
          margin: "8px 0"
        }}>
          <div style={{
            width: `${progress}%`,
            height: "100%",
            background: "var(--brand)",
            borderRadius: "4px",
            transition: "width .4s ease"
          }}></div>
        </div>
      )}
      {!errMsg && progress > 0 && (
        <div style={{ fontFamily: "var(--font-mono)", fontSize: "12px", color: "var(--brand)", fontWeight: "600" }}>
          {progress}%
        </div>
      )}

      {errMsg
        ? <div className="l-err">{errMsg}</div>
        : <div className="l-sub" style={{ minHeight: "40px", display: "flex", alignItems: "center", justifyContent: "center", textAlign: "center" }}>
            {details || t("loading_sub1")}
          </div>
      }
      <div className="l-hint">⌛ 1–2 min · no cerrar esta ventana</div>
    </div>
  );
}

// ---------- Logo de competidor (favicon de marca + fallback a inicial) ----------
// Dominios de marcas legítimas con presencia web limpia. El clúster montadeudas
// (iPeso, MaxCrédito, MexaCash) no tiene logo corporativo → cae a inicial.
const LOGO_DOMAINS = {
  // Club Kosher — regulados
  kueski:    "kueski.com",
  klar:      "klar.mx",
  stori:     "storicard.com",
  tala:      "tala.mx",
  kubo:      "kubofinanciero.com",
  creditea:  "creditea.mx",
  moneyman:  "moneyman.mx",
  avafin:    "avafin.mx",
  crece:     "crece.app",       // marca Crece by QFIN
  // Clúster controvertido — iconos de sus páginas de descarga / landing
  fintopia:  "fintopia.com",
  didi:      "web.didiglobal.com",
  maxcredito:"maxcredito.com",
  mexacash:  "mexacash.com",
  mexdin:    "mexdin.com",
  ipeso:     "ipeso.mx",
};
// Normaliza texto quitando tildes: "MaxCrédito" → "maxcredito"
function stripAccents(s) {
  // NFD descompone "é" en "e" + combinador; luego limpiamos combinadores U+0300..U+036F
  return (s || "").toLowerCase().normalize("NFD").replace(/\p{Mn}/gu, "");
}
function logoUrlFor(name) {
  const n = stripAccents(name);
  if (n.includes("qcrece") || n === "crece") return "/logos/qcrece.png";
  if (n.includes("fintopia")) return "/logos/fintopia.png";
  if (n.includes("mexdin")) return "/logos/mexdin.png";
  if (n.includes("moneyman")) return "/logos/moneyman.png";
  if (n.includes("tala")) return "/logos/tala.png";

  for (const k in LOGO_DOMAINS) {
    if (n.includes(stripAccents(k))) return `https://www.google.com/s2/favicons?domain=${LOGO_DOMAINS[k]}&sz=64`;
  }
  return null;
}
// Caja fija: el logo SIEMPRE mide 36×36 aunque se use fuera de .comp-row
const LOGO_BOX = {
  width: 36, height: 36, borderRadius: 10, display: "flex", alignItems: "center",
  justifyContent: "center", fontWeight: 700, fontSize: 13, flex: "none", overflow: "hidden",
};
function CompLogo({ name, color }) {
  const url = logoUrlFor(name);
  const [err, setErr] = React.useState(false);
  if (url && !err) {
    return (
      <div className="logo" style={{ ...LOGO_BOX, background: "#fff", padding: 5 }}>
        <img src={url} alt={name} onError={() => setErr(true)} style={{ width: "100%", height: "100%", objectFit: "contain" }} />
      </div>
    );
  }
  return <div className="logo" style={{ ...LOGO_BOX, background: (color || "#888") + "26", color: color || "#888" }}>{(name || "?")[0]}</div>;
}

Object.assign(window, { Ic, SentBadge, Severity, SourceTag, KPI, Sidebar, TopBar, NAV, CompLogo, LoadingOverlay });
