/* global React */
// Dashboard / Cockpit — the hero screen
// Branche /api/metrics pour avoir des KPIs réels au lieu de mock METRICS.

function Dashboard({ onGoto, onOpenCopilot }) {
  const [live, setLive] = useState(null);
  useEffect(() => {
    const BASE = (window.AE_API && window.AE_API.BASE) || '';
    fetch(BASE + '/api/metrics')
      .then(r => r.json())
      .then(d => { if (d.ok) setLive(d); })
      .catch(() => {});
  }, []);

  // Valeurs réelles (fallback sur mock METRICS si /api/metrics indispo)
  const dossiersTotal = live?.dossiers?.total ?? METRICS.dossiersActifs;
  const kwhCumac      = live?.dossiers?.kwhCumacSum ?? METRICS.cumacPipeline;
  const primeSum      = live?.dossiers?.primeSum ?? METRICS.primesEngagees * 1000;
  const visites30     = live?.recent?.visites30 ?? 0;
  const opps30        = live?.recent?.opps30 ?? 0;
  const emailsPending = live?.emails?.byStatus?.find(s => s.key === 'pending')?.count ?? 0;
  const dossiersPending = live?.dossiers?.byStatus?.find(s => s.key === 'pending' || s.key === 'draft')?.count ?? 0;

  return (
    <div style={{ padding: '24px 24px 80px', maxWidth: 1680, margin: '0 auto' }}>
      {/* Hero band — four oversized KPIs in Bloomberg style */}
      <section style={{
        display: 'grid', gridTemplateColumns: 'repeat(4, 1fr)', gap: 0,
        border: '1px solid var(--line)', borderRadius: 8,
        background: 'var(--paper-2)', overflow: 'hidden',
        marginBottom: 20,
      }}>
        <HeroKpi
          label="Volume CEE en cours" sub={live ? 'pipeline réel · DB live' : 'pipeline actif'}
          value={kwhCumac} format={fmtMWh}
          delta={live ? `${visites30} visites 30j` : '+8,4 % vs. N-30'}
          spark={METRICS.cumacLast30}
          accent="var(--signal)"
        />
        <HeroKpi
          label="Primes engagées" sub={live ? 'somme dossiers DB' : 'signé non versé'}
          value={primeSum} format={fmtEur}
          delta={live ? `${opps30} opps créées 30j` : '+12 600 € cette semaine'}
          spark={METRICS.primesLast30}
          accent="var(--signal)"
          divider
        />
        <HeroKpi
          label="Dossiers actifs" sub={live ? 'live DB · all statuses' : 'tous statuts'}
          value={dossiersTotal} format={fmtInt}
          delta={dossiersPending > 0 ? `${dossiersPending} en attente d'action` : 'aucun en attente'}
          spark={[34, 35, 36, 38, 39, 40, 41, 42, 42, 42]}
          accent="var(--amber)"
          divider
        />
        <HeroKpi
          label="Score PNCEE moyen" sub="probabilité d'acceptation"
          value={METRICS.scoreMoyen} format={n => Math.round(n) + ' / 100'}
          delta={emailsPending > 0 ? `${emailsPending} emails à traiter` : '13 dossiers < 70'}
          spark={[74, 76, 75, 78, 79, 80, 81, 82, 82]}
          accent="var(--signal-deep)"
          divider
        />
      </section>

      {/* Three-column: Chart / Actions prioritaires / Activity */}
      <section style={{ display: 'grid', gridTemplateColumns: '1fr 340px 340px', gap: 16, marginBottom: 20 }}>
        <Panel title="Pipeline cumac" subtitle="30 derniers jours · par statut backend">
          <PipelineChart />
        </Panel>

        <Panel
          title="Actions prioritaires"
          subtitle="générées par le co-pilote · 07:42"
          accent={<Dot tone="plasma" pulse />}
        >
          <Priorities onGoto={onGoto} onOpenCopilot={onOpenCopilot} />
        </Panel>

        <Panel title="Feed temps réel" subtitle="SSE · vault + Odoo + PNCEE">
          <Activity />
        </Panel>
      </section>

      {/* Bottom: heatmap + FOST leaders */}
      <section style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 16 }}>
        <Panel title="Carte des dépôts PNCEE" subtitle="12 derniers mois · intensité = MWh cumac">
          <Heatmap />
        </Panel>
        <Panel title="Fiches FOST les plus actives" subtitle="répartition sectorielle">
          <FostLeaderboard />
        </Panel>
      </section>

      {/* Section Direction / PDG : KPIs stratégiques (visible uniquement si rôle direction) */}
      {(() => {
        const me = window.ME || {};
        const isDirection = ['pdg', 'direction'].includes(me.role);
        return isDirection ? <PdgDashboard /> : null;
      })()}
    </div>
  );
}

// ─── Dashboard Direction / PDG ──────────────────────────────────────────────
// Vue stratégique : revenue mensuel + pipeline + top clients + top collabs
// Visible uniquement pour les rôles 'pdg' / 'direction'.
function PdgDashboard() {
  const [data, setData]       = useState(null);
  const [loading, setLoading] = useState(true);

  useEffect(() => {
    const BASE = (window.AE_API && window.AE_API.BASE) || '';
    fetch(BASE + '/api/metrics/pdg')
      .then(r => r.json())
      .then(d => { if (d?.ok) setData(d); })
      .catch(() => {})
      .finally(() => setLoading(false));
  }, []);

  if (loading) return <div style={{ padding: 24, color: 'var(--ink-4)', fontSize: 12, marginTop: 20 }}>Chargement vue Direction…</div>;
  if (!data)   return null;

  const { synthesis, revenueMonthly, pipeline, topClients, topCollabs } = data;
  const fmtEur = (n) => new Intl.NumberFormat('fr-FR', { style: 'currency', currency: 'EUR', maximumFractionDigits: 0 }).format(n || 0);
  const fmtMonth = (m) => {
    const [y, mm] = m.split('-');
    const months = ['Jan','Fév','Mar','Avr','Mai','Juin','Juil','Août','Sep','Oct','Nov','Déc'];
    return `${months[parseInt(mm)-1]} ${y.slice(2)}`;
  };

  const maxRevenue = Math.max(...revenueMonthly.map(m => m.revenue), 1);
  const trendColor = synthesis.trend3m >= 0 ? 'var(--signal-deep)' : 'var(--rouge)';
  const trendIcon  = synthesis.trend3m >= 0 ? '↗' : '↘';

  return (
    <section style={{ marginTop: 28 }}>
      {/* Bandeau Direction */}
      <div style={{
        display: 'flex', alignItems: 'center', gap: 10, marginBottom: 16,
        padding: '12px 16px', borderRadius: 8,
        background: 'linear-gradient(90deg, rgba(14,16,16,0.92), rgba(14,16,16,0.85))',
        color: '#fff',
      }}>
        <span style={{ fontSize: 16 }}>👁️</span>
        <div style={{ flex: 1 }}>
          <div style={{ fontSize: 13, fontWeight: 600 }}>Vue Direction</div>
          <div style={{ fontSize: 10, color: 'rgba(255,255,255,.55)' }}>KPIs stratégiques · accès Direction / PDG uniquement</div>
        </div>
        <span style={{
          fontSize: 9, padding: '3px 8px', background: 'rgba(255,255,255,.1)',
          color: 'rgba(255,255,255,.7)', borderRadius: 999, fontFamily: 'var(--font-mono)',
          textTransform: 'uppercase', letterSpacing: 0.6,
        }}>Live · {new Date(data.ts).toLocaleTimeString('fr-FR', { hour: '2-digit', minute: '2-digit' })}</span>
      </div>

      {/* 4 KPI synthèse */}
      <div style={{
        display: 'grid', gridTemplateColumns: 'repeat(4, 1fr)', gap: 0,
        border: '1px solid var(--line)', borderRadius: 8,
        background: 'var(--paper-2)', overflow: 'hidden', marginBottom: 16,
      }}>
        <PdgKpi label="CA pipeline 12 mois" value={fmtEur(synthesis.totalRevenue)} sub={`${synthesis.totalDossiers} dossiers`} accent="var(--signal)" />
        <PdgKpi label="Tendance 3 mois" value={`${trendIcon} ${Math.abs(synthesis.trend3m)}%`} sub={synthesis.trend3m >= 0 ? 'En croissance' : 'En baisse'} color={trendColor} divider />
        <PdgKpi label="Taux conversion" value={`${synthesis.conversionRate}%`} sub="opp → signature" accent="var(--amber)" divider />
        <PdgKpi label="Signatures ce mois" value={String(synthesis.signaturesThisMonth)} sub={`Panier moyen : ${fmtEur(synthesis.avgRevenue)}`} accent="var(--plasma)" divider />
      </div>

      {/* Revenue mensuel — barchart */}
      <Panel title="Revenue mensuel" subtitle="primes estimées sur dossiers · 12 mois glissants">
        <div style={{ display: 'flex', alignItems: 'flex-end', gap: 6, height: 140, padding: '14px 4px 4px' }}>
          {revenueMonthly.length === 0 && <div style={{ flex: 1, textAlign: 'center', color: 'var(--ink-4)', fontSize: 11 }}>Aucune donnée disponible</div>}
          {revenueMonthly.map((m, i) => {
            const h = (m.revenue / maxRevenue) * 100;
            return (
              <div key={i} style={{ flex: 1, display: 'flex', flexDirection: 'column', alignItems: 'center', gap: 5 }}>
                <div style={{ fontSize: 9, fontFamily: 'var(--font-mono)', color: 'var(--ink-3)' }}>
                  {m.revenue >= 1000 ? `${Math.round(m.revenue / 1000)}k` : m.revenue}
                </div>
                <div title={`${m.dossiers} dossiers · ${fmtEur(m.revenue)}`} style={{
                  width: '100%', height: `${Math.max(h, 2)}%`,
                  background: 'linear-gradient(180deg, var(--signal) 0%, var(--signal-deep) 100%)',
                  borderRadius: '3px 3px 0 0', minHeight: 2,
                  transition: 'opacity .15s', cursor: 'help',
                }} />
                <div style={{ fontSize: 9, color: 'var(--ink-4)' }}>{fmtMonth(m.month)}</div>
              </div>
            );
          })}
        </div>
      </Panel>

      {/* Pipeline funnel + Top clients + Top collabs en 3 cols */}
      <section style={{ display: 'grid', gridTemplateColumns: '1fr 1fr 1fr', gap: 16, marginTop: 16 }}>
        <Panel title="Pipeline par stage" subtitle="opportunités CRM en cours">
          <div style={{ padding: '8px 14px 12px', display: 'flex', flexDirection: 'column', gap: 8 }}>
            {pipeline.length === 0 && <div style={{ color: 'var(--ink-4)', fontSize: 11 }}>Aucune opp</div>}
            {pipeline.sort((a,b) => b.count - a.count).map((p, i) => {
              const max = Math.max(...pipeline.map(x => x.count), 1);
              const w = (p.count / max) * 100;
              return (
                <div key={i} style={{ display: 'grid', gridTemplateColumns: '110px 1fr 50px', gap: 8, alignItems: 'center', fontSize: 11 }}>
                  <span style={{ color: 'var(--ink-3)', textTransform: 'capitalize' }}>{p.stage.replace(/_/g, ' ')}</span>
                  <div style={{ height: 8, background: 'var(--paper-3)', borderRadius: 2, overflow: 'hidden' }}>
                    <div style={{ width: `${w}%`, height: '100%', background: 'var(--signal)', transition: 'width .4s' }} />
                  </div>
                  <span className="mono" style={{ fontSize: 10, fontWeight: 600, textAlign: 'right' }}>{p.count}</span>
                </div>
              );
            })}
          </div>
        </Panel>

        <Panel title="Top 10 clients" subtitle="par revenue cumulé">
          <div style={{ padding: '6px 14px 10px', display: 'flex', flexDirection: 'column', gap: 4 }}>
            {topClients.length === 0 && <div style={{ color: 'var(--ink-4)', fontSize: 11, padding: 10 }}>Aucune donnée</div>}
            {topClients.map((c, i) => (
              <div key={i} style={{ display: 'grid', gridTemplateColumns: '20px 1fr auto', gap: 8, alignItems: 'center', padding: '5px 0', borderBottom: i < topClients.length - 1 ? '1px solid var(--hairline)' : 'none', fontSize: 11 }}>
                <span style={{ fontSize: 9, color: 'var(--ink-4)', fontFamily: 'var(--font-mono)' }}>#{i + 1}</span>
                <span style={{ overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }} title={c.client}>{c.client}</span>
                <span className="mono" style={{ fontSize: 10, color: 'var(--signal-deep)', fontWeight: 600 }}>{fmtEur(c.revenue)}</span>
              </div>
            ))}
          </div>
        </Panel>

        <Panel title="Top 5 collaborateurs" subtitle="par CA pipeline généré">
          <div style={{ padding: '6px 14px 10px', display: 'flex', flexDirection: 'column', gap: 4 }}>
            {topCollabs.length === 0 && <div style={{ color: 'var(--ink-4)', fontSize: 11, padding: 10 }}>Aucune donnée</div>}
            {topCollabs.map((c, i) => (
              <div key={i} style={{ display: 'grid', gridTemplateColumns: '24px 1fr auto', gap: 10, alignItems: 'center', padding: '6px 0', borderBottom: i < topCollabs.length - 1 ? '1px solid var(--hairline)' : 'none', fontSize: 11 }}>
                <div style={{
                  width: 22, height: 22, borderRadius: '50%',
                  background: ['var(--signal)', 'var(--amber)', 'var(--copper)', 'var(--plasma)', 'var(--ink-3)'][i],
                  color: '#fff', display: 'grid', placeItems: 'center',
                  fontSize: 10, fontWeight: 700,
                }}>{(c.owner[0] || '?').toUpperCase()}</div>
                <div style={{ overflow: 'hidden' }}>
                  <div style={{ fontWeight: 500, overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>{c.owner}</div>
                  <div style={{ fontSize: 9, color: 'var(--ink-4)' }}>{c.opps} opps</div>
                </div>
                <span className="mono" style={{ fontSize: 10, color: 'var(--signal-deep)', fontWeight: 600 }}>{fmtEur(c.revenue)}</span>
              </div>
            ))}
          </div>
        </Panel>
      </section>
    </section>
  );
}

// PdgKpi — KPI cell pour la vue Direction (style cohérent avec HeroKpi mais plus compact)
function PdgKpi({ label, value, sub, accent, color, divider }) {
  return (
    <div style={{
      padding: '14px 18px',
      borderLeft: divider ? '1px solid var(--line)' : 'none',
      display: 'flex', flexDirection: 'column', gap: 6, minHeight: 90,
    }}>
      <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between' }}>
        <div style={{ fontSize: 10, color: 'var(--ink-4)', textTransform: 'uppercase', letterSpacing: 0.6, fontWeight: 500 }}>{label}</div>
        {accent && <span style={{ width: 5, height: 5, borderRadius: '50%', background: accent, boxShadow: `0 0 8px ${accent}` }} />}
      </div>
      <div className="num" style={{ fontSize: 24, fontWeight: 700, letterSpacing: '-0.02em', lineHeight: 1.1, color: color || 'var(--ink)' }}>{value}</div>
      <div style={{ fontSize: 10, color: 'var(--ink-3)' }}>{sub}</div>
    </div>
  );
}

function HeroKpi({ label, sub, value, format, delta, spark, accent, divider }) {
  return (
    <div style={{
      padding: '18px 20px 16px',
      borderLeft: divider ? '1px solid var(--line)' : 'none',
      position: 'relative', minHeight: 140,
      display: 'flex', flexDirection: 'column', gap: 10,
    }}>
      <div style={{ display: 'flex', alignItems: 'baseline', justifyContent: 'space-between' }}>
        <div>
          <div style={{ fontSize: 11, color: 'var(--ink-4)', textTransform: 'uppercase', letterSpacing: 0.6, fontWeight: 500 }}>{label}</div>
          <div style={{ fontSize: 10, color: 'var(--ink-5)', marginTop: 2 }}>{sub}</div>
        </div>
        <span style={{ width: 6, height: 6, borderRadius: '50%', background: accent, boxShadow: `0 0 8px ${accent}` }} />
      </div>
      <div className="num" style={{ fontSize: 36, fontWeight: 600, letterSpacing: '-0.03em', lineHeight: 1, color: 'var(--ink)' }}>
        <AnimNum value={value} format={format} />
      </div>
      <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', gap: 10 }}>
        <span style={{ fontSize: 11, color: 'var(--ink-3)', fontWeight: 500 }}>{delta}</span>
        <Sparkline data={spark} width={80} height={24} stroke={accent} fill="var(--signal-tint)" strokeWidth={1.5} />
      </div>
    </div>
  );
}

function Panel({ title, subtitle, children, accent }) {
  return (
    <div style={{
      border: '1px solid var(--line)',
      borderRadius: 8,
      background: 'var(--paper-2)',
      overflow: 'hidden',
      display: 'flex', flexDirection: 'column',
    }}>
      <header style={{
        padding: '12px 16px',
        borderBottom: '1px solid var(--hairline)',
        display: 'flex', alignItems: 'center', gap: 8,
      }}>
        {accent}
        <div style={{ flex: 1, minWidth: 0 }}>
          <div style={{ fontSize: 13, fontWeight: 600, letterSpacing: '-0.01em' }}>{title}</div>
          {subtitle && <div style={{ fontSize: 11, color: 'var(--ink-4)', marginTop: 1 }}>{subtitle}</div>}
        </div>
      </header>
      <div style={{ padding: 0, flex: 1 }}>{children}</div>
    </div>
  );
}

function PipelineChart() {
  // Stacked area + the current stage column counts shown as columns
  const stages = STAGES;
  const counts = stages.map(s => DOSSIERS.filter(d => d.stage === s.key).length);
  const maxC = Math.max(...counts);
  return (
    <div style={{ padding: '18px 20px' }}>
      {/* Big stacked area */}
      <div style={{ position: 'relative', height: 180, marginBottom: 18 }}>
        <AreaChart />
      </div>
      {/* Stage columns */}
      <div style={{ display: 'grid', gridTemplateColumns: `repeat(${stages.length}, 1fr)`, gap: 8, borderTop: '1px solid var(--hairline)', paddingTop: 14 }}>
        {stages.map((s, i) => (
          <div key={s.key} style={{ display: 'flex', flexDirection: 'column', gap: 4 }}>
            <div style={{ height: 32, display: 'flex', alignItems: 'flex-end' }}>
              <div style={{
                width: '100%',
                height: `${(counts[i] / maxC) * 100}%`,
                background: `linear-gradient(180deg, var(--signal) 0%, var(--signal-soft) 100%)`,
                borderRadius: '2px 2px 0 0',
                minHeight: 2,
              }} />
            </div>
            <div style={{ display: 'flex', alignItems: 'baseline', gap: 4 }}>
              <span className="num" style={{ fontSize: 14, fontWeight: 600 }}>{counts[i]}</span>
            </div>
            <div style={{ fontSize: 10, color: 'var(--ink-2)', fontWeight: 500, lineHeight: 1.1 }}>{s.label}</div>
            <div className="mono" style={{ fontSize: 9, color: 'var(--ink-4)' }}>{s.sub}</div>
          </div>
        ))}
      </div>
    </div>
  );
}

function AreaChart() {
  const W = 800, H = 180;
  const data1 = METRICS.cumacLast30;
  const data2 = data1.map((v, i) => v * (0.5 + (i / 30) * 0.3)); // deposited curve grows
  const max = Math.max(...data1);
  const pts = (arr) => arr.map((v, i) => `${(i / (arr.length - 1)) * W},${H - (v / max) * (H - 20) - 10}`).join(' ');
  const area = (arr) => `M0,${H} L${pts(arr).split(' ').join(' L')} L${W},${H} Z`;
  return (
    <svg viewBox={`0 0 ${W} ${H}`} preserveAspectRatio="none" style={{ width: '100%', height: '100%', display: 'block' }}>
      <defs>
        <linearGradient id="g1" x1="0" x2="0" y1="0" y2="1">
          <stop offset="0%" stopColor="var(--signal)" stopOpacity="0.35" />
          <stop offset="100%" stopColor="var(--signal)" stopOpacity="0" />
        </linearGradient>
        <linearGradient id="g2" x1="0" x2="0" y1="0" y2="1">
          <stop offset="0%" stopColor="var(--ink)" stopOpacity="0.18" />
          <stop offset="100%" stopColor="var(--ink)" stopOpacity="0" />
        </linearGradient>
        <pattern id="grid" width="40" height="30" patternUnits="userSpaceOnUse">
          <path d="M 40 0 L 0 0 0 30" fill="none" stroke="var(--hairline)" strokeWidth="1"/>
        </pattern>
      </defs>
      <rect width={W} height={H} fill="url(#grid)" />
      <path d={area(data1)} fill="url(#g1)" />
      <polyline points={pts(data1)} fill="none" stroke="var(--signal-deep)" strokeWidth="1.5" />
      <path d={area(data2)} fill="url(#g2)" />
      <polyline points={pts(data2)} fill="none" stroke="var(--ink-3)" strokeWidth="1.3" strokeDasharray="3 3" />
      {/* Annotation */}
      <g transform={`translate(${W * 0.75},${H - (data1[22] / max) * (H - 20) - 10})`}>
        <circle r="4" fill="var(--signal)" stroke="var(--paper-2)" strokeWidth="2" />
        <line x1="0" y1="-6" x2="0" y2="-24" stroke="var(--ink-4)" strokeWidth="1" />
        <g transform="translate(-66,-46)">
          <rect width="132" height="22" rx="3" fill="var(--ink)" />
          <text x="8" y="14" fontSize="10" fill="var(--paper)" fontFamily="JetBrains Mono">pic J-8 · 612 MWh</text>
        </g>
      </g>
      {/* Legend */}
      <g transform="translate(12,12)" fontFamily="General Sans" fontSize="10">
        <rect x="0" y="0" width="10" height="2" fill="var(--signal-deep)" />
        <text x="14" y="4" fill="var(--ink-3)">Gisement en cours</text>
        <rect x="120" y="0" width="10" height="2" fill="var(--ink-3)" />
        <text x="134" y="4" fill="var(--ink-3)">Déposé PNCEE</text>
      </g>
    </svg>
  );
}

function Priorities({ onGoto, onOpenCopilot }) {
  const items = [
    { tone: 'rouge', title: '3 dossiers avec score < 60', detail: 'DOS-2026-031, -017, -005 · rejet PNCEE probable', action: 'Audit inversé' },
    { tone: 'amber', title: 'AH manquante · DOS-2026-024', detail: 'générer depuis modèle · bénéficiaire confirmé', action: 'Générer AH' },
    { tone: 'signal', title: '4 signatures en attente > 48 h', detail: 'relances automatiques proposées', action: 'Relancer' },
    { tone: 'plasma', title: 'Arrêté BAR-TH-104 mis à jour', detail: '18 dossiers à requalifier · gain estimé +4,2 k€', action: 'Requalifier' },
  ];
  return (
    <div style={{ display: 'flex', flexDirection: 'column' }}>
      {items.map((it, i) => (
        <div key={i} className="fade-up" style={{
          padding: '11px 16px',
          borderBottom: i < items.length - 1 ? '1px solid var(--hairline)' : 'none',
          display: 'flex', flexDirection: 'column', gap: 6,
          animationDelay: `${i * 60}ms`,
        }}>
          <div style={{ display: 'flex', alignItems: 'center', gap: 8 }}>
            <Dot tone={it.tone} size={6} />
            <span style={{ fontSize: 12, fontWeight: 600, flex: 1, lineHeight: 1.3 }}>{it.title}</span>
          </div>
          <div style={{ fontSize: 11, color: 'var(--ink-3)', lineHeight: 1.4, paddingLeft: 14 }}>{it.detail}</div>
          <div style={{ paddingLeft: 14, marginTop: 2 }}>
            <button onClick={onOpenCopilot} style={{
              fontSize: 11, color: 'var(--ink-2)', fontWeight: 500,
              padding: '2px 0', borderBottom: '1px dotted var(--ink-4)',
            }}>{it.action} →</button>
          </div>
        </div>
      ))}
      <div style={{ padding: '10px 16px', borderTop: '1px solid var(--hairline)' }}>
        <button onClick={onOpenCopilot} style={{ fontSize: 11, color: 'var(--plasma)', fontWeight: 600 }}>Ouvrir co-pilote ⌘K</button>
      </div>
    </div>
  );
}

function Activity() {
  // Branche /api/events pour avoir le vrai feed temps réel (DB + SSE).
  // Fallback sur ACTIVITY mock si endpoint indispo.
  const [events, setEvents] = useState(null);
  useEffect(() => {
    const BASE = (window.AE_API && window.AE_API.BASE) || '';
    fetch(BASE + '/api/events?limit=12')
      .then(r => r.json())
      .then(d => { if (d.ok && d.events) setEvents(d.events); })
      .catch(() => {});
  }, []);

  // Mappe niveau → tone Dot
  const levelTone = { error: 'rouge', warn: 'copper', info: 'signal', debug: 'muted' };
  const fmtAge = (iso) => {
    if (!iso) return '';
    const diff = (Date.now() - new Date(iso).getTime()) / 1000;
    if (diff < 60) return 'à l\'instant';
    if (diff < 3600) return `il y a ${Math.floor(diff / 60)} min`;
    if (diff < 86400) return `il y a ${Math.floor(diff / 3600)} h`;
    return `il y a ${Math.floor(diff / 86400)} j`;
  };

  if (events === null) {
    // Loading or fallback
    return (
      <div style={{ display: 'flex', flexDirection: 'column', maxHeight: 380, overflowY: 'auto' }}>
        {ACTIVITY.map((a, i) => (
          <div key={i} style={{
            padding: '10px 16px',
            borderBottom: i < ACTIVITY.length - 1 ? '1px solid var(--hairline)' : 'none',
            display: 'grid', gridTemplateColumns: '14px 1fr', gap: 10,
          }}>
            <div style={{ paddingTop: 4 }}><Dot tone={a.tone} size={6} /></div>
            <div>
              <div style={{ display: 'flex', alignItems: 'baseline', gap: 8 }}>
                <span style={{ fontSize: 12, fontWeight: 600 }}>{a.title}</span>
                <span className="mono" style={{ fontSize: 10, color: 'var(--ink-4)', marginLeft: 'auto' }}>{a.at}</span>
              </div>
              <div style={{ fontSize: 11, color: 'var(--ink-3)', marginTop: 2, lineHeight: 1.35 }}>{a.detail}</div>
              <div style={{ fontSize: 10, color: 'var(--ink-4)', marginTop: 3 }}>par <span style={{ color: 'var(--ink-2)' }}>{a.who}</span></div>
            </div>
          </div>
        ))}
      </div>
    );
  }

  if (events.length === 0) {
    return (
      <div style={{ padding: 24, textAlign: 'center', color: 'var(--ink-4)', fontSize: 12 }}>
        Aucun événement récent.
      </div>
    );
  }

  return (
    <div style={{ display: 'flex', flexDirection: 'column', maxHeight: 380, overflowY: 'auto' }}>
      {events.map((e, i) => (
        <div key={e.id || i} style={{
          padding: '10px 16px',
          borderBottom: i < events.length - 1 ? '1px solid var(--hairline)' : 'none',
          display: 'grid', gridTemplateColumns: '14px 1fr', gap: 10,
        }}>
          <div style={{ paddingTop: 4 }}><Dot tone={levelTone[e.level] || 'muted'} size={6} /></div>
          <div>
            <div style={{ display: 'flex', alignItems: 'baseline', gap: 8 }}>
              <span style={{ fontSize: 12, fontWeight: 600, lineHeight: 1.3 }}>{(e.title || e.type || '—').slice(0, 70)}</span>
              <span className="mono" style={{ fontSize: 10, color: 'var(--ink-4)', marginLeft: 'auto', flexShrink: 0 }}>{fmtAge(e.ts)}</span>
            </div>
            {e.message && <div style={{ fontSize: 11, color: 'var(--ink-3)', marginTop: 2, lineHeight: 1.35 }}>{e.message.slice(0, 120)}{e.message.length > 120 ? '…' : ''}</div>}
            <div style={{ fontSize: 10, color: 'var(--ink-4)', marginTop: 3 }}>
              <span className="mono">{e.source}</span>{e.entityKind ? <span> · <span style={{ color: 'var(--ink-3)' }}>{e.entityKind}</span></span> : null}
            </div>
          </div>
        </div>
      ))}
    </div>
  );
}

function Heatmap() {
  const months = ['Mai', 'Jui', 'Jul', 'Aoû', 'Sep', 'Oct', 'Nov', 'Déc', 'Jan', 'Fév', 'Mar', 'Avr'];
  const rng = seedRng('heatmap');
  const rows = ['Résidentiel', 'Tertiaire', 'Industrie'];
  return (
    <div style={{ padding: '18px 20px' }}>
      <div style={{ display: 'grid', gridTemplateColumns: '90px repeat(12, 1fr)', gap: 4, alignItems: 'center' }}>
        <div />
        {months.map(m => <div key={m} style={{ fontSize: 10, color: 'var(--ink-4)', textAlign: 'center' }}>{m}</div>)}
        {rows.map(r => (
          <React.Fragment key={r}>
            <div style={{ fontSize: 11, color: 'var(--ink-2)', fontWeight: 500 }}>{r}</div>
            {months.map((_, i) => {
              const v = rng();
              const op = 0.08 + v * 0.8;
              return <div key={i} style={{ aspectRatio: '1/1', background: `rgba(63,224,124,${op})`, borderRadius: 3, border: '1px solid var(--hairline)' }} title={`${Math.round(v * 840)} MWh`} />;
            })}
          </React.Fragment>
        ))}
      </div>
      <div style={{ marginTop: 16, display: 'flex', alignItems: 'center', gap: 10, fontSize: 10, color: 'var(--ink-4)' }}>
        <span>faible</span>
        {[0.1, 0.25, 0.4, 0.55, 0.7, 0.85].map(o => (
          <div key={o} style={{ width: 16, height: 10, background: `rgba(63,224,124,${o})`, border: '1px solid var(--hairline)', borderRadius: 2 }} />
        ))}
        <span>dense</span>
        <span style={{ marginLeft: 'auto' }} className="mono">total · 48,2 GWh cumac</span>
      </div>
    </div>
  );
}

function FostLeaderboard() {
  const top = [...FOST_CATALOG].sort((a, b) => b.count - a.count).slice(0, 6);
  const max = Math.max(...top.map(t => t.count));
  return (
    <div style={{ padding: '6px 0' }}>
      {top.map((f, i) => (
        <div key={f.code} style={{
          display: 'grid', gridTemplateColumns: '110px 1fr 60px 60px', gap: 12, alignItems: 'center',
          padding: '8px 20px',
          borderBottom: i < top.length - 1 ? '1px solid var(--hairline)' : 'none',
        }}>
          <span className="mono" style={{ fontSize: 11, fontWeight: 600 }}>{f.code}</span>
          <div>
            <div style={{ fontSize: 11, color: 'var(--ink-2)', whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis' }}>{f.name}</div>
            <div style={{ position: 'relative', height: 4, background: 'var(--paper-3)', borderRadius: 2, marginTop: 4 }}>
              <div style={{ position: 'absolute', inset: 0, width: `${(f.count / max) * 100}%`, background: 'var(--signal)', borderRadius: 2 }} />
            </div>
          </div>
          <span className="num" style={{ fontSize: 13, fontWeight: 600, textAlign: 'right' }}>{f.count}</span>
          <span className="num" style={{ fontSize: 11, color: 'var(--ink-3)', textAlign: 'right' }}>{fmtMWh(f.avgCumac * f.count)}</span>
        </div>
      ))}
    </div>
  );
}

Object.assign(window, { Dashboard });
