/* global React, ReactDOM, window */
// =====================================================================
// WindMar Performance Marketing Command Center — main app
// =====================================================================

const { useState, useEffect, useRef } = React;

const TWEAKS_DEFAULTS = /*EDITMODE-BEGIN*/{
  "lang": "es",
  "density": "comfortable",
  "tab": "overview",
  "showBrandRail": true,
  "accent": "orange"
}/*EDITMODE-END*/;

// ---------- Dashboard switcher (top-level) ----------
function DashboardSwitcher({ dashboardKey, setDashboardKey, lang }) {
  const dashboards = window.DASHBOARDS || {};
  const order = ["general", "consultores", "fl", "accion", "reclutamiento"];
  return (
    <div style={{
      display: "flex", alignItems: "center", gap: 8,
      padding: "8px 20px",
      background: "var(--wm-blue-900)",
      borderBottom: "1px solid rgba(255,255,255,0.08)",
    }}>
      <span style={{ fontSize: 10, fontWeight: 800, letterSpacing: "0.12em", color: "rgba(255,255,255,0.55)", marginRight: 4 }}>
        {lang==="es"?"DASHBOARD":"DASHBOARD"}
      </span>
      {order.map(k => {
        const d = dashboards[k];
        if (!d) return null;
        const isActive = dashboardKey === k;
        const tooltips = {
          general:       "Las 4 cuentas principales: Solar, Roofing, Anker y Water",
          consultores:   "Cuenta Consultores Élite (Puerto Rico)",
          fl:            "Cuentas de Florida: Consultores FL + Windmar FL",
          accion:        "Cuenta Windmar en Acción (eventos / comunidad)",
          reclutamiento: "Cuenta Careers + campañas con 'reclutamiento' / 'careers' en cualquier cuenta",
        };
        return (
          <button
            key={k}
            onClick={() => setDashboardKey(k)}
            title={tooltips[k]}
            style={{
              padding: "5px 12px",
              borderRadius: 6,
              border: "1px solid " + (isActive ? "var(--wm-orange)" : "rgba(255,255,255,0.15)"),
              background: isActive ? "var(--wm-orange)" : "transparent",
              color: isActive ? "var(--wm-white)" : "rgba(255,255,255,0.8)",
              fontSize: 12, fontWeight: 700, cursor: "pointer",
              transition: "all 160ms",
            }}>
            {lang==="es" ? d.label_es : d.label_en}
          </button>
        );
      })}
    </div>
  );
}

// ---------- Account filter (level 2) ----------
function AccountFilter({ dashboardKey, accountFilter, setAccountFilter, lang }) {
  const dash = window.DASHBOARDS?.[dashboardKey];
  if (!dash || dash.accounts.length <= 1) return null;
  return (
    <div style={{
      display: "flex", alignItems: "center", gap: 6,
      padding: "8px 20px",
      background: "var(--wm-bg-muted)",
      borderBottom: "1px solid var(--op-chrome-line)",
    }}>
      <span style={{ fontSize: 10, fontWeight: 800, letterSpacing: "0.10em", color: "var(--wm-fg-muted)", marginRight: 4 }}>
        {lang==="es"?"CUENTA":"ACCOUNT"}
      </span>
      <button
        className={"chip " + (accountFilter === "all" ? "is-active" : "")}
        onClick={() => setAccountFilter("all")}
        title={lang==="es"?"Suma de todas las cuentas de este dashboard":"All accounts of this dashboard combined"}>
        {lang==="es"?"Todas":"All"}
      </button>
      {dash.accounts.map(a => (
        <button
          key={a.ext}
          className={"chip " + (accountFilter === a.ext ? "is-active" : "")}
          onClick={() => setAccountFilter(a.ext)}
          title={`Cuenta Meta: ${a.label} (${a.ext})`}>
          {a.label}
        </button>
      ))}
      {dash.regex && (
        <span style={{ fontSize: 10, color: "var(--wm-fg-muted)", marginLeft: 12 }}>
          ⓘ {lang==="es"
            ? `+ campañas con "${dash.regex.split('|').join('/')}" de otras cuentas`
            : `+ campaigns matching "${dash.regex.split('|').join('/')}" from other accounts`}
        </span>
      )}
    </div>
  );
}

// ---------- Tweaks panel ----------
function Tweaks({ tweaks, setTweak }) {
  const [open, setOpen] = useState(false);

  useEffect(() => {
    const handler = (e) => {
      if (e.data?.type === "__activate_edit_mode") setOpen(true);
      if (e.data?.type === "__deactivate_edit_mode") setOpen(false);
    };
    window.addEventListener("message", handler);
    window.parent.postMessage({ type: "__edit_mode_available" }, "*");
    return () => window.removeEventListener("message", handler);
  }, []);

  const close = () => {
    setOpen(false);
    window.parent.postMessage({ type: "__edit_mode_dismissed" }, "*");
  };

  if (!open) return null;
  const T = window.TweaksPanel ? window.TweaksPanel : null;
  if (!T) return null;
  return (
    <window.TweaksPanel onClose={close} title="Tweaks">
      <window.TweakSection title="Language / Idioma">
        <window.TweakRadio
          options={[{ label: "Español", value: "es" }, { label: "English", value: "en" }]}
          value={tweaks.lang}
          onChange={(v) => setTweak("lang", v)}
        />
      </window.TweakSection>
      <window.TweakSection title="Density">
        <window.TweakRadio
          options={[{ label: "Comfortable", value: "comfortable" }, { label: "Compact", value: "compact" }]}
          value={tweaks.density}
          onChange={(v) => setTweak("density", v)}
        />
      </window.TweakSection>
      <window.TweakSection title="Profit accent">
        <window.TweakRadio
          options={[
            { label: "Orange", value: "orange" },
            { label: "Green", value: "green" },
            { label: "Blue", value: "blue" },
          ]}
          value={tweaks.accent}
          onChange={(v) => setTweak("accent", v)}
        />
      </window.TweakSection>
      <window.TweakSection title="Brand rail">
        <window.TweakToggle
          label="Show vertical brand rail"
          checked={tweaks.showBrandRail}
          onChange={(v) => setTweak("showBrandRail", v)}
        />
      </window.TweakSection>
    </window.TweaksPanel>
  );
}

// ---------- Visual month-grid range calendar (vanilla, no deps) ----------
const _fmtLocal = (d) => `${d.getFullYear()}-${String(d.getMonth()+1).padStart(2,"0")}-${String(d.getDate()).padStart(2,"0")}`;
function MiniCalendar({ from, to, onPick, lang }) {
  const seed = from ? new Date(from + "T00:00:00") : new Date();
  const [view, setView] = useState(new Date(seed.getFullYear(), seed.getMonth(), 1));
  const y = view.getFullYear(), m = view.getMonth();
  const monthName = view.toLocaleDateString(lang === "es" ? "es-ES" : "en-US", { month: "long", year: "numeric" });
  const firstW = (new Date(y, m, 1).getDay() + 6) % 7; // lunes=0
  const days = new Date(y, m + 1, 0).getDate();
  const cells = [];
  for (let i = 0; i < firstW; i++) cells.push(null);
  for (let d = 1; d <= days; d++) cells.push(new Date(y, m, d));
  const today = _fmtLocal(new Date());
  const dow = lang === "es" ? ["L","M","M","J","V","S","D"] : ["M","T","W","T","F","S","S"];
  const NAVY = "var(--wm-blue,#1D429B)";
  const btn = { background: "none", border: "none", cursor: "pointer", color: "var(--wm-fg-muted)", padding: 4, borderRadius: 6, display: "inline-flex" };
  return (
    <div style={{ width: 248 }}>
      <div style={{ display: "flex", alignItems: "center", justifyContent: "space-between", marginBottom: 6 }}>
        <button style={btn} onClick={()=>setView(new Date(y, m-1, 1))} aria-label="prev"><window.Icon name="chevron-left" size={16} /></button>
        <div style={{ fontSize: 12, fontWeight: 800, color: "var(--wm-fg,#13203f)", textTransform: "capitalize" }}>{monthName}</div>
        <button style={btn} onClick={()=>setView(new Date(y, m+1, 1))} aria-label="next"><window.Icon name="chevron-right" size={16} /></button>
      </div>
      <div style={{ display: "grid", gridTemplateColumns: "repeat(7,1fr)", gap: 2, fontSize: 9.5, fontWeight: 800, color: "var(--wm-fg-muted)", textAlign: "center", marginBottom: 2 }}>
        {dow.map((d,i)=><div key={i}>{d}</div>)}
      </div>
      <div style={{ display: "grid", gridTemplateColumns: "repeat(7,1fr)", gap: 2 }}>
        {cells.map((d, i) => {
          if (!d) return <div key={i} />;
          const iso = _fmtLocal(d);
          const isFrom = from && iso === from, isTo = to && iso === to;
          const inRange = from && to && iso >= from && iso <= to;
          const isToday = iso === today;
          const edge = isFrom || isTo;
          return (
            <button key={i} onClick={()=>onPick(iso)} style={{
              height: 30, border: "none", cursor: "pointer", fontSize: 12, fontWeight: edge ? 800 : 600,
              borderRadius: edge ? 7 : (inRange ? 0 : 7),
              background: edge ? NAVY : (inRange ? "var(--op-blue-50,#EAF0FB)" : "transparent"),
              color: edge ? "#fff" : (isToday ? NAVY : "var(--wm-fg,#13203f)"),
              outline: isToday && !edge ? "1px solid " + NAVY : "none",
            }}>{d.getDate()}</button>
          );
        })}
      </div>
    </div>
  );
}

// ---------- Date range picker (presets + custom calendar) ----------
function DateRangePicker({ value, onChange, lang }) {
  const [showCustom, setShowCustom] = useState(false);
  const [customFrom, setCustomFrom] = useState("");
  const [customTo, setCustomTo] = useState("");

  const presets = lang === "es"
    ? [
        { v: "today", l: "Hoy",     t: "Solo el día de hoy" },
        { v: "7d",    l: "7 días",  t: "Últimos 7 días incluyendo hoy" },
        { v: "30d",   l: "30 días", t: "Últimos 30 días incluyendo hoy" },
        { v: "90d",   l: "90 días", t: "Últimos 90 días incluyendo hoy" },
        { v: "mtd",   l: "MTD",     t: "Month-to-Date: del día 1 de este mes hasta hoy" },
        { v: "qtd",   l: "QTD",     t: "Quarter-to-Date: del día 1 de este trimestre hasta hoy" },
      ]
    : [
        { v: "today", l: "Today",   t: "Today only" },
        { v: "7d",    l: "7d",      t: "Last 7 days including today" },
        { v: "30d",   l: "30d",     t: "Last 30 days including today" },
        { v: "90d",   l: "90d",     t: "Last 90 days including today" },
        { v: "mtd",   l: "MTD",     t: "Month-to-Date: from the 1st of this month to today" },
        { v: "qtd",   l: "QTD",     t: "Quarter-to-Date: from the 1st of this quarter to today" },
      ];

  const isCustom = typeof value === "object" && value.from && value.to;

  // Helper to default custom values from current selection
  useEffect(() => {
    if (showCustom && (!customFrom || !customTo)) {
      const today = new Date().toISOString().slice(0, 10);
      const monthAgo = new Date(Date.now() - 30 * 86400 * 1000).toISOString().slice(0, 10);
      setCustomFrom(customFrom || monthAgo);
      setCustomTo(customTo || today);
    }
  }, [showCustom]);

  return (
    <div style={{ display: "inline-flex", alignItems: "center", gap: 6, marginRight: 8, position: "relative" }}>
      <div className="chip-group">
        {presets.map(o => (
          <button
            key={o.v}
            className={"chip " + (!isCustom && value === o.v ? "is-active" : "")}
            onClick={() => { onChange(o.v); setShowCustom(false); }}
            title={o.t}
          >
            {o.l}
          </button>
        ))}
        <button
          className={"chip " + (isCustom ? "is-active" : "") + (showCustom ? " is-active" : "")}
          onClick={() => setShowCustom(!showCustom)}
          title={lang==="es"?"Rango personalizado":"Custom range"}
        >
          <window.Icon name="calendar" size={11} /> {isCustom ? `${value.from} → ${value.to}` : (lang==="es"?"Custom":"Custom")}
        </button>
      </div>
      {showCustom && (
        <div className="card" style={{
          position: "absolute", top: 38, right: 0, zIndex: 100,
          padding: 14, minWidth: 280, boxShadow: "0 8px 24px rgba(0,0,0,0.15)",
        }} onClick={(e) => e.stopPropagation()}>
          <div style={{ fontSize: 11, fontWeight: 800, color: "var(--wm-blue)", marginBottom: 8 }}>
            {lang==="es"?"RANGO PERSONALIZADO":"CUSTOM RANGE"}
          </div>
          <div style={{ display: "flex", justifyContent: "center", marginBottom: 10 }}>
            <MiniCalendar from={customFrom} to={customTo} lang={lang} onPick={(iso) => {
              if (!customFrom || (customFrom && customTo)) { setCustomFrom(iso); setCustomTo(""); }
              else if (iso >= customFrom) { setCustomTo(iso); }
              else { setCustomTo(customFrom); setCustomFrom(iso); }
            }} />
          </div>
          <div style={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: 10, marginBottom: 12 }}>
            <div>
              <label style={{ fontSize: 10, color: "var(--wm-fg-muted)", fontWeight: 700 }}>{lang==="es"?"Desde":"From"}</label>
              <input type="date" value={customFrom} onChange={(e) => setCustomFrom(e.target.value)}
                style={{ width: "100%", padding: 6, border: "1px solid var(--op-line-strong)", borderRadius: 6, fontSize: 12, marginTop: 2 }} />
            </div>
            <div>
              <label style={{ fontSize: 10, color: "var(--wm-fg-muted)", fontWeight: 700 }}>{lang==="es"?"Hasta":"To"}</label>
              <input type="date" value={customTo} onChange={(e) => setCustomTo(e.target.value)}
                style={{ width: "100%", padding: 6, border: "1px solid var(--op-line-strong)", borderRadius: 6, fontSize: 12, marginTop: 2 }} />
            </div>
          </div>
          {/* Quick presets for past dates */}
          <div style={{ fontSize: 10, fontWeight: 700, color: "var(--wm-fg-muted)", marginBottom: 4 }}>{lang==="es"?"Atajos":"Quick"}:</div>
          <div style={{ display: "flex", gap: 4, flexWrap: "wrap", marginBottom: 10 }}>
            {[
              [lang==="es"?"Ayer":"Yesterday", -1, -1],
              [lang==="es"?"Esta semana":"This week", -6, 0],
              [lang==="es"?"Sem. pasada":"Last week", -13, -7],
              [lang==="es"?"Este mes":"This month", "mtd", 0],
              [lang==="es"?"Mes pasado":"Last month", "lm", "lme"],
              ["YTD", "ytd", 0],
            ].map(([l, from, to], i) => (
              <button key={i} className="btn btn-ghost btn-sm" style={{ padding: "3px 7px", fontSize: 10 }}
                onClick={() => {
                  const today = new Date();
                  const fmt = (d) => d.toISOString().slice(0, 10);
                  let f, t;
                  if (from === "mtd") { f = fmt(new Date(today.getFullYear(), today.getMonth(), 1)); t = fmt(today); }
                  else if (from === "ytd") { f = fmt(new Date(today.getFullYear(), 0, 1)); t = fmt(today); }
                  else if (from === "lm") { const lm = new Date(today.getFullYear(), today.getMonth()-1, 1); const lme = new Date(today.getFullYear(), today.getMonth(), 0); f = fmt(lm); t = fmt(lme); }
                  else { f = fmt(new Date(Date.now() + from*86400*1000)); t = fmt(new Date(Date.now() + to*86400*1000)); }
                  setCustomFrom(f); setCustomTo(t);
                }}>{l}</button>
            ))}
          </div>
          <div style={{ display: "flex", justifyContent: "flex-end", gap: 6 }}>
            <button className="btn btn-ghost btn-sm" onClick={() => setShowCustom(false)}>
              {lang==="es"?"Cancelar":"Cancel"}
            </button>
            <button className="btn btn-primary btn-sm" onClick={() => {
              if (customFrom && customTo) {
                onChange({ from: customFrom, to: customTo });
                setShowCustom(false);
              }
            }}>
              {lang==="es"?"Aplicar":"Apply"}
            </button>
          </div>
        </div>
      )}
    </div>
  );
}

// ---------- Top bar ----------
function TopBar({ lang, setLang, dateRange, setDateRange, loading, lastFetchAt, dataSource, objectiveFilter, setObjectiveFilter }) {
  const t = window.i18n[lang];
  const lastFetchLabel = lastFetchAt
    ? lastFetchAt.toLocaleTimeString([], { hour: "2-digit", minute: "2-digit" })
    : "—";
  return (
    <header className="topbar">
      <div className="topbar-row">
        <div className="topbar-brand">
          <img src="assets/logos/windmar-blue-principal.png" alt="WindMar" />
          <div className="topbar-product">
            {t.appName}
            <small>{t.tagline}</small>
          </div>
        </div>
        <div className="topbar-spacer" />
        <div className="topbar-filters">
          <select
            value={objectiveFilter}
            onChange={(e) => setObjectiveFilter(e.target.value)}
            title={lang==="es"?"Filtrar campañas por objetivo (Lead Gen excluye Awareness del CAC/CM)":"Filter campaigns by objective"}
            style={{
              padding: "5px 10px", borderRadius: 6,
              border: "1px solid var(--op-chrome-line)",
              fontSize: 11, fontWeight: 700, background: "white", marginRight: 8,
              fontFamily: "var(--wm-font-sans)", cursor: "pointer",
              color: objectiveFilter === "awareness" ? "var(--op-amber)" : objectiveFilter === "all" ? "var(--wm-fg-muted)" : "var(--wm-blue)",
            }}>
            <option value="leadgen">{lang==="es"?"🎯 Performance":"🎯 Performance"}</option>
            <option value="awareness">{lang==="es"?"📢 Branding":"📢 Branding"}</option>
            <option value="all">{lang==="es"?"Todo (perf+branding)":"All (perf+branding)"}</option>
          </select>
          <DateRangePicker value={dateRange} onChange={setDateRange} lang={lang} />
          <window.LiveDot />
          <span style={{ fontSize: 11, fontWeight: 700, color: "var(--wm-fg-muted)", marginRight: 8 }}>
            {dataSource === "supabase"
              ? (lang === "es" ? `Live · ${lastFetchLabel}` : `Live · ${lastFetchLabel}`)
              : (lang === "es" ? "Mock" : "Mock")}
            {loading ? (lang === "es" ? " · actualizando…" : " · refreshing…") : ""}
          </span>
        </div>
        <div className="topbar-actions">
          <div className="lang-switch" style={{ display: "inline-flex", border: "1px solid var(--op-chrome-line)", borderRadius: 8, overflow: "hidden" }}>
            <button
              onClick={()=>setLang("es")}
              title="Cambiar a español"
              style={{ padding: "6px 10px", fontSize: 11, fontWeight: 800, border: "none", cursor: "pointer", background: lang==="es"?"var(--wm-blue)":"transparent", color: lang==="es"?"var(--wm-white)":"var(--wm-fg-muted)" }}>ES</button>
            <button
              onClick={()=>setLang("en")}
              title="Switch to English"
              style={{ padding: "6px 10px", fontSize: 11, fontWeight: 800, border: "none", cursor: "pointer", background: lang==="en"?"var(--wm-blue)":"transparent", color: lang==="en"?"var(--wm-white)":"var(--wm-fg-muted)" }}>EN</button>
          </div>
          <button
            className="icon-btn"
            title={lang==="es"?"Forzar refresh (ingest + queries)":"Force refresh"}
            onClick={async () => {
              try {
                await Promise.allSettled([
                  fetch("/api/cron/meta_ingest"),
                  fetch("/api/cron/zoho_crm_ingest"),
                  fetch("/api/cron/evaluate_alerts"),
                ]);
                // Use window.__currentScope (saved by fetchFromSupabase on every call)
                if (window.fetchFromSupabase) {
                  const s = window.__currentScope || {};
                  await window.fetchFromSupabase(dateRange, s.dashboardKey || "general", s.accountFilter || "all");
                }
                window.dispatchEvent(new CustomEvent("supabase-data-updated", { detail: { reason: "manual-refresh" } }));
              } catch (e) { console.warn("refresh failed", e); }
            }}>
            <window.Icon name="refresh-cw" size={16} />
          </button>
          <button
            className="icon-btn"
            title={lang==="es"?"Abrir Supabase (ver tablas)":"Open Supabase project"}
            onClick={() => window.open(`https://supabase.com/dashboard/project/${(window.SUPABASE_URL||'').split('/')[2]?.split('.')[0]}`, "_blank")}>
            <window.Icon name="database" size={16} />
          </button>
          <button
            className="icon-btn"
            title={lang==="es"?"Ver historial de acciones":"View action history"}
            onClick={async () => {
              try {
                const r = await fetch(`${window.SUPABASE_URL}/rest/v1/action_log?select=kind,status,triggered_by,executed_at,error_message&order=created_at.desc&limit=20`, {
                  headers: { apikey: window.SUPABASE_ANON_KEY, Authorization: `Bearer ${window.SUPABASE_ANON_KEY}` },
                });
                if (r.status === 401) {
                  alert(lang==="es"?"action_log requiere auth · solo es accesible vía API (no anon).":"action_log requires auth");
                  return;
                }
                const rows = await r.json();
                alert((lang==="es"?"Últimas 20 acciones:\n\n":"Last 20 actions:\n\n") +
                  rows.map(a => `${a.executed_at||a.kind}: ${a.kind} · ${a.status} · ${a.triggered_by||'system'}${a.error_message?' · err: '+a.error_message:''}`).join("\n"));
              } catch (e) { alert("Error: " + e.message); }
            }}>
            <window.Icon name="history" size={16} />
          </button>
          <div style={{ width: 32, height: 32, borderRadius: "50%", background: "var(--wm-blue)", color: "white", display: "grid", placeItems: "center", fontSize: 12, fontWeight: 800 }}
            title="Julian">JB</div>
        </div>
      </div>
    </header>
  );
}

// ---------- Profit Command Bar ----------
function CommandBar({ lang, accent }) {
  const t = window.i18n[lang];
  const totals = window.profitTotals;
  const fmt = window.fmt;

  const cells = [
    { key: "spend",   label: t.profit.spend,   value: totals.spend.val,   delta: totals.spend.delta,   format: "money", inverse: true },
    { key: "revenue", label: t.profit.revenue, value: totals.revenue.val, delta: totals.revenue.delta, format: "money" },
    { key: "cm",      label: t.profit.cm,      value: totals.cm.val,      delta: totals.cm.delta,      format: "money", featured: true },
    { key: "cmPct",   label: t.profit.cmPct,   value: totals.cmPct.val,   delta: totals.cmPct.delta,   format: "pct"   },
    { key: "cac",     label: t.profit.cac,     value: totals.cac.val,     delta: totals.cac.delta,     format: "money", inverse: true },
    { key: "leads",   label: t.profit.leads,   value: totals.leads.val,   delta: totals.leads.delta,   format: "num", subline: totals.leads_no_utms > 0 ? `${totals.leads_no_utms} sin UTMs` : null },
    { key: "sales",   label: t.profit.sales,   value: totals.sales.val,   delta: totals.sales.delta,   format: "num",
      subline: (() => {
        const parts = [];
        const strict = (window.profitTotals?.sales_strict ?? 0);
        const noUtms = (window.profitTotals?.sales_no_utms ?? 0);
        const unknown = (window.profitTotals?.sales_unknown ?? 0);
        if (strict > 0) parts.push(`${strict} strict`);
        if (noUtms > 0) parts.push(`${noUtms} sin UTMs`);
        if (unknown > 0) parts.push(`${unknown} unknowns→`);
        return parts.length ? parts.join(" · ") : null;
      })()
    },
  ];

  // Map metric keys for cells that support drill-down
  const drilldownMap = {
    leads: "leads",
    sales: "sales",
  };

  // Hover explanations for each KPI cell
  const kpiTooltips = {
    spend:   "Gasto total en Meta Ads para el filtro/período actual (de plataforma directo)",
    revenue: "Ingresos = suma de (avg deal × % por producto). Solar 15%, Roofing 10%, Anker 15%, Water 15%",
    cm:      "Margen de Contribución = Ingresos − Gasto en Ads. Negativo si gastaste más de lo que rentaste",
    cmPct:   "% Margen = CM / Ingresos. Mide qué % de los ingresos queda después de pagar los ads",
    cac:     "Costo de Adquisición = Gasto / Ventas. Cuánto costó cada venta digital",
    leads:   "Leads digitales strict (Lead_Source + UTMs válidos) del CRM en el período. Click para ver la lista",
    sales:   "Ventas digital strict (won) atribuidas a campañas Meta conocidas. Click para ver la lista",
  };

  return (
    <div className="profit-bar">
      {cells.map(c => {
        const drillKey = drilldownMap[c.key];
        const clickable = !!drillKey && window.openDrilldown;
        return (
          <div
            key={c.key}
            className={"profit-cell " + (c.featured ? "profit-cell--featured " : "") + (clickable ? "profit-cell--clickable" : "")}
            onClick={clickable ? () => window.openDrilldown(drillKey, c.label, c.value) : undefined}
            style={clickable ? { cursor: "pointer" } : undefined}
            title={(kpiTooltips[c.key] || "") + (clickable ? (lang==="es"?"\nClick → ver lista exacta de personas":"\nClick → see exact list of people") : "")}
          >
            <div className="profit-label">
              {c.label}
              {clickable && <span style={{ marginLeft: 4, opacity: 0.45, fontSize: 9 }}>↗</span>}
            </div>
            <div className={"profit-value " + (c.featured ? "profit-value--accent accent-"+accent : "")}>
              {c.format === "money" && <window.AnimatedNumber value={c.value} format={c.value > 50000 ? "moneyk" : "money"} />}
              {c.format === "pct"   && <window.AnimatedNumber value={c.value} format="pct" />}
              {c.format === "num"   && <window.AnimatedNumber value={c.value} format="num" />}
            </div>
            <div className="profit-meta">
              <window.Trend value={c.delta} inverse={c.inverse} size={10} />
              <span style={{ fontSize: 10, color: "rgba(255,255,255,0.45)" }}>{t.profit.vsLastPeriod}</span>
            </div>
            {c.subline && (
              <div style={{ fontSize: 10, color: "var(--wm-orange)", marginTop: 2, fontWeight: 700 }}>
                ⚠ {c.subline}
              </div>
            )}
          </div>
        );
      })}
    </div>
  );
}

// ---------- Tabs ----------
function Tabs({ tab, setTab, lang }) {
  const t = window.i18n[lang];
  const tooltipsEs = {
    overview:  "Resumen del estado actual: alertas, top creativos, márgenes",
    funnel:    "Embudo CRM: leads → trabajados → contactados → citas → ventas",
    creatives: "Lista de anuncios agrupados por campaña, con métricas e imágenes",
    decisions: "Centro de decisiones (windmar-marketing): campañas por CM, audiencias→decisión, salud creativa y bitácora",
    products:  "P&L por producto: Solar, Roofing, Anker, Water",
    market:    "Análisis de competencia y posicionamiento",
    campaigns: "Detalle de leads por campaña Meta",
    tracking:  "Leads digitales con UTMs faltantes o inválidos (sin trackeo completo)",
    unknowns:  "Ventas sin Lead_Source identificado, posibles atribuciones perdidas",
    alerts:    "Alertas y notificaciones del sistema",
    google:    "Google Ads: reportes, campañas, anuncios, keywords y recomendaciones",
    agents:    "Agentes en vivo: corridas, costo, audiencias descubiertas (windmar-marketing)",
    knowledge: "RAG / knowledge: busca en el cerebro de marketing (notas, doctrina, campañas)",
    cro:       "CRO de social: hook, retención, fatiga y veredicto por creativo (escalar/arreglar/matar)",
    ejecutivos: "Analytics ejecutivo: CM por mes, ganados/perdidos por canal, qué producto corrió el movimiento",
    ejecuciones: "Lead Association: leads tuyos sin atribuir (robados) + asociar lead↔deal en Zoho (handoff, atribución lead-first)",
  };
  const tooltipsEn = {
    overview:  "Snapshot of today: alerts, top creatives, margins",
    funnel:    "CRM funnel: leads → worked → contacted → appts → sales",
    creatives: "List of ads grouped by campaign with metrics and images",
    decisions: "Decision center (windmar-marketing): campaigns by CM, audiences→decision, creative health, log",
    products:  "Per-product P&L: Solar, Roofing, Anker, Water",
    market:    "Competition analysis and positioning",
    campaigns: "Per-campaign lead detail",
    tracking:  "Digital leads with missing or invalid UTMs",
    unknowns:  "Sales without Lead_Source, possible missed attribution",
    alerts:    "System alerts and notifications",
    google:    "Google Ads: reports, campaigns, ads, keywords and recommendations",
    agents:    "Live agents: runs, cost, discovered audiences (windmar-marketing)",
    knowledge: "RAG / knowledge: search the marketing brain (notes, doctrine, campaigns)",
    cro:       "Social CRO: hook, retention, fatigue and per-creative verdict (scale/fix/kill)",
    ejecutivos: "Executive analytics: CM by month, win/loss by channel, which product drove the movement",
    ejecuciones: "Lead Association: your unattributed (stolen) leads + associate lead↔deal in Zoho (handoff, lead-first attribution)",
  };
  const tt = lang==="es" ? tooltipsEs : tooltipsEn;
  const [open, setOpen] = useState(null); // grupo abierto

  // metadata por tab (icono + badges en vivo)
  const meta = {
    overview:   { icon: "layout-dashboard" },
    funnel:     { icon: "filter" },
    creatives:  { icon: "image", count: window.creatives.length },
    cro:        { icon: "flame" },
    ejecutivos: { icon: "briefcase" },
    ejecuciones:{ icon: "list-checks" },
    decisions:  { icon: "shield-check" },
    products:   { icon: "layers" },
    market:     { icon: "radar" },
    campaigns:  { icon: "list-tree", count: (window.leadsByCampaign||[]).length },
    tracking:   { icon: "tag", count: (window.leadsNoUtms||[]).length },
    unknowns:   { icon: "help-circle", count: (window.unknownSales||[]).length },
    google:     { icon: "search", count: (window.googleRaw?.recs||[]).length || null },
    alerts:     { icon: "bell", alertCount: window.alerts.filter(a=>a.sev==="critical"||a.sev==="high").length },
    agents:     { icon: "cpu" },
    knowledge:  { icon: "book-open" },
  };

  // grupos (condensa ~15 tabs en menús)
  const groups = [
    { id: "perf",     icon: "bar-chart-3", label: lang==="es"?"Performance":"Performance", keys: ["funnel","creatives","campaigns","google"] },
    { id: "cro",      icon: "flame",       label: "CRO",                                    keys: ["decisions","ejecuciones","ejecutivos"] },
    { id: "analisis", icon: "radar",       label: lang==="es"?"Análisis":"Analysis",        keys: ["products","market","tracking","unknowns"] },
    { id: "sistema",  icon: "cpu",         label: lang==="es"?"Sistema":"System",           keys: ["alerts","agents","knowledge"] },
  ];

  const Badge = ({ k }) => (<>
    {meta[k].count != null && <span className="tab-count">{meta[k].count}</span>}
    {meta[k].alertCount > 0 && <span className="tab-count is-alert">{meta[k].alertCount}</span>}
  </>);

  const pick = (k) => { setTab(k); setOpen(null); };

  return (
    <div className="tabbar" style={{ position: "relative", overflow: "visible", flexWrap: "wrap" }}>
      {/* Resumen standalone */}
      <button className={"tab " + (tab==="overview"?"is-active":"")} onClick={()=>pick("overview")} title={tt.overview}>
        <window.Icon name="layout-dashboard" size={14} /><span>{t.tabs.overview}</span>
      </button>

      {open != null && <div onClick={()=>setOpen(null)} style={{ position: "fixed", inset: 0, zIndex: 40 }} />}

      {groups.map(g => {
        const activeChild = g.keys.find(k => k === tab);
        const rollupCount = g.keys.reduce((s,k)=>s+(Number(meta[k].count)||0),0);
        const rollupAlert = g.keys.reduce((s,k)=>s+(Number(meta[k].alertCount)||0),0);
        const isOpen = open === g.id;
        return (
          <div key={g.id} style={{ position: "relative", zIndex: isOpen ? 50 : "auto" }}>
            <button
              className={"tab " + (activeChild ? "is-active" : "")}
              onClick={()=>setOpen(isOpen ? null : g.id)}
              title={g.label}
              style={{ display: "inline-flex", alignItems: "center", gap: 5 }}
            >
              <window.Icon name={g.icon} size={14} />
              <span>{g.label}{activeChild ? <span style={{ opacity: .65, fontWeight: 600 }}> · {t.tabs[activeChild]}</span> : null}</span>
              {rollupCount > 0 && !activeChild && <span className="tab-count">{rollupCount}</span>}
              {rollupAlert > 0 && <span className="tab-count is-alert">{rollupAlert}</span>}
              <window.Icon name="chevron-down" size={12} />
            </button>
            {isOpen && (
              <div style={{
                position: "absolute", top: "calc(100% + 4px)", left: 0, zIndex: 60, minWidth: 230,
                background: "var(--wm-card,#fff)", border: "1px solid var(--op-line,#e7ebf3)", borderRadius: 12,
                boxShadow: "0 10px 30px rgba(0,0,0,0.16)", padding: 6,
              }}>
                {g.keys.map(k => (
                  <button key={k} onClick={()=>pick(k)} title={tt[k]}
                    style={{
                      display: "flex", alignItems: "center", gap: 9, width: "100%", textAlign: "left",
                      padding: "8px 10px", borderRadius: 8, border: "none", cursor: "pointer", fontSize: 13, fontWeight: 700,
                      background: tab===k ? "var(--op-blue-50,#EAF0FB)" : "transparent",
                      color: tab===k ? "var(--wm-blue,#1D429B)" : "var(--wm-fg,#13203f)",
                    }}
                    onMouseEnter={e=>{ if(tab!==k) e.currentTarget.style.background="var(--op-hover,#f4f6fb)"; }}
                    onMouseLeave={e=>{ if(tab!==k) e.currentTarget.style.background="transparent"; }}
                  >
                    <window.Icon name={meta[k].icon} size={15} />
                    <span style={{ flex: 1 }}>{t.tabs[k]}</span>
                    <Badge k={k} />
                  </button>
                ))}
              </div>
            )}
          </div>
        );
      })}
    </div>
  );
}

// ---------- Brand rail (vertical strip on left) ----------
function BrandRail() {
  return (
    <aside style={{
      width: 36,
      background: "var(--wm-blue-900)",
      color: "var(--wm-white)",
      display: "flex", flexDirection: "column", alignItems: "center", justifyContent: "space-between",
      padding: "16px 0",
      flexShrink: 0,
      position: "sticky", top: 0, alignSelf: "stretch", height: "100vh",
    }}>
      <div style={{ width: 22, height: 22, borderRadius: 4, background: "var(--wm-orange)" }} />
      <div style={{ writingMode: "vertical-rl", transform: "rotate(180deg)", fontSize: 9, fontWeight: 900, letterSpacing: "0.30em", color: "rgba(255,255,255,0.55)" }}>
        PERFORMANCE · COMMAND CENTER
      </div>
      <div style={{ display: "flex", flexDirection: "column", alignItems: "center", gap: 6, fontSize: 9, fontWeight: 800, color: "rgba(255,255,255,0.45)" }}>
        <div style={{ width: 6, height: 6, borderRadius: "50%", background: "var(--op-green)" }} />
        <div style={{ writingMode: "vertical-rl", transform: "rotate(180deg)" }}>v2.4</div>
      </div>
    </aside>
  );
}

// ---------- Main app ----------
function App() {
  const [tweaks, setTweaks] = useState(TWEAKS_DEFAULTS);
  const [dateRange, setDateRange] = useState("30d");
  const [dashboardKey, setDashboardKey] = useState("general");  // top-level
  const [accountFilter, setAccountFilter] = useState("all");    // level 2
  const [objectiveFilter, setObjectiveFilter] = useState("leadgen"); // leadgen | awareness | all
  const sourceIsSupabase = window.__dataSource === "supabase";
  const [loading, setLoading] = useState(sourceIsSupabase);
  const [ready, setReady] = useState(!sourceIsSupabase);
  const [revision, setRevision] = useState(0);
  const [lastFetchAt, setLastFetchAt] = useState(null);
  const [fetchError, setFetchError] = useState(null);

  // Reset account filter when dashboard changes
  useEffect(() => {
    setAccountFilter("all");
  }, [dashboardKey]);

  const setTweak = (key, value) => {
    const next = typeof key === "object" ? { ...tweaks, ...key } : { ...tweaks, [key]: value };
    setTweaks(next);
    window.parent.postMessage({
      type: "__edit_mode_set_keys",
      edits: typeof key === "object" ? key : { [key]: value },
    }, "*");
  };

  // Fetch from Supabase on mount & when date range / dashboard / account filter / sales basis changes
  useEffect(() => {
    if (!sourceIsSupabase || !window.fetchFromSupabase) return;
    let cancelled = false;
    setLoading(true);
    setFetchError(null);
    // Fire alert re-evaluation in background — keeps the badge accurate without
    // waiting for the 5-min cron. Free since it's the user's own request.
    fetch("/api/cron/evaluate_alerts").catch(() => {});

    window.fetchFromSupabase(dateRange, dashboardKey, accountFilter, objectiveFilter)
      .then(() => {
        if (cancelled) return;
        setLastFetchAt(new Date());
        setRevision(r => r + 1);
        setReady(true);
        setLoading(false);
      })
      .catch((err) => {
        if (cancelled) return;
        console.error("[app] Supabase fetch failed:", err);
        setFetchError(err);
        setReady(true);
        setLoading(false);
      });
    return () => { cancelled = true; };
  }, [dateRange, dashboardKey, accountFilter, objectiveFilter, sourceIsSupabase]);

  // Refs so the realtime closure always sees the LATEST scope
  const scopeRef = useRef({ dateRange, dashboardKey, accountFilter, objectiveFilter });
  useEffect(() => {
    scopeRef.current = { dateRange, dashboardKey, accountFilter, objectiveFilter };
  }, [dateRange, dashboardKey, accountFilter, objectiveFilter]);

  // Realtime: subscribe once. Always pass current scope from ref to preserve filters.
  useEffect(() => {
    if (!sourceIsSupabase || !window.startSupabaseRealtime) return;
    window.startSupabaseRealtime(() => scopeRef.current);
    const onUpdate = () => {
      setLastFetchAt(new Date());
      setRevision(r => r + 1);
    };
    window.addEventListener("supabase-data-updated", onUpdate);
    return () => window.removeEventListener("supabase-data-updated", onUpdate);
  }, [sourceIsSupabase]);

  // Apply density to body
  useEffect(() => {
    document.body.classList.toggle("density-compact", tweaks.density === "compact");
  }, [tweaks.density]);

  // Refresh lucide icons after view change
  useEffect(() => {
    if (window.lucide) window.lucide.createIcons();
  }, [tweaks.tab, tweaks.lang, revision]);

  const setTab = (t) => setTweak("tab", t);
  const setLang = (l) => setTweak("lang", l);

  const ViewMap = {
    overview:   window.OverviewView,
    funnel:     window.FunnelView,
    creatives:  window.CreativesView,
    decisions:  window.CROView,  // unificado: Decisiones = la superficie CRO (windmar-marketing)
    products:   window.ProductsView,
    market:     window.MarketView,
    campaigns:  window.CampaignsView,
    tracking:   window.TrackingView,
    unknowns:   window.UnknownsView,
    alerts:     window.AlertsView,
    google:     window.GoogleView,
    agents:     window.AgentsView,
    knowledge:  window.KnowledgeView,
    ejecutivos: window.ExecView,
    ejecuciones: window.ExecutionsView,
  };
  const View = ViewMap[tweaks.tab] || window.OverviewView;

  return (
    <div className="app-shell" style={{ display: "flex", flexDirection: "row" }}>
      {tweaks.showBrandRail && <BrandRail />}
      <div className="app-main" style={{ flex: 1, minWidth: 0, display: "flex", flexDirection: "column" }}>
        <DashboardSwitcher
          dashboardKey={dashboardKey}
          setDashboardKey={setDashboardKey}
          lang={tweaks.lang}
        />
        <AccountFilter
          dashboardKey={dashboardKey}
          accountFilter={accountFilter}
          setAccountFilter={setAccountFilter}
          lang={tweaks.lang}
        />
        <TopBar
          lang={tweaks.lang}
          setLang={setLang}
          dateRange={dateRange}
          setDateRange={setDateRange}
          loading={loading}
          lastFetchAt={lastFetchAt}
          dataSource={window.__dataSource}
          objectiveFilter={objectiveFilter}
          setObjectiveFilter={setObjectiveFilter}
        />
        {!ready ? (
          <div style={{ flex: 1, display: "grid", placeItems: "center", color: "var(--wm-fg-muted)", fontSize: 13 }}>
            <div style={{ textAlign: "center" }}>
              <div style={{ fontWeight: 800, color: "var(--wm-blue)", fontSize: 16, marginBottom: 6 }}>
                {tweaks.lang === "es" ? "Cargando data en vivo…" : "Loading live data…"}
              </div>
              <div style={{ fontSize: 11, color: "var(--wm-fg-subtle)" }}>
                Supabase · {dateRange.toUpperCase()}
              </div>
            </div>
          </div>
        ) : (
          <React.Fragment>
            <CommandBar lang={tweaks.lang} accent={tweaks.accent} key={`cb-${revision}`} />
            <Tabs tab={tweaks.tab} setTab={setTab} lang={tweaks.lang} key={`tabs-${revision}`} />
            <main className="main" data-screen-label={tweaks.tab}>
              <View lang={tweaks.lang} onTab={setTab} dateRange={dateRange} key={`view-${tweaks.tab}-${revision}`} />
            </main>
            {fetchError && (
              <div style={{ padding: "6px 12px", background: "var(--op-amber-50)", color: "var(--op-amber)", fontSize: 11, fontWeight: 700, textAlign: "center" }}>
                ⚠ {tweaks.lang === "es" ? "Fallo al cargar data live · usando data en cache" : "Live data fetch failed · using cached"}
              </div>
            )}
          </React.Fragment>
        )}
      </div>
      <Tweaks tweaks={tweaks} setTweak={setTweak} />
      {window.ActionModal && <window.ActionModal />}
      {window.DrilldownPanel && <window.DrilldownPanel />}
    </div>
  );
}

const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(<App />);

// Refresh lucide on initial mount
setTimeout(() => { if (window.lucide) window.lucide.createIcons(); }, 100);
