// TAM Event Dashboard 2026 — React App
const { useState, useEffect, useMemo } = React;

const fmtNum = (n) => new Intl.NumberFormat('de-DE').format(Math.round(n || 0));
const fmtEur = (n) => '€' + new Intl.NumberFormat('de-DE', { maximumFractionDigits: 0 }).format(Math.round(n || 0));
const fmtDate = (iso) => iso ? new Date(iso).toLocaleDateString('de-DE', { day: '2-digit', month: '2-digit', year: '2-digit' }) : '';

const KPI_LABELS = {
  uniqueTouchpoints: { title: 'Unique Touchpoints', sub: 'Personen, die wirklich an ≥1 Event teilgenommen haben (ATTENDED)', isMoney: false },
  overallTouchpoints: { title: 'Overall Touchpoints', sub: 'Alle echten Teilnahmen (Person × Event mit ATTENDED-Status)', isMoney: false },
  newLeads: { title: 'Neue Leads', sub: 'Kontakte, die via Event neu ins CRM gekommen sind', isMoney: false },
  newMqls: { title: 'Neue MQLs', sub: 'Neue Leads mit Folge-Aktion (Lifecycle ≥ MQL)', isMoney: false },
  newOpportunities: { title: 'Neue Opportunities', sub: 'Deals erstellt innerhalb 6 Monate nach Event-Teilnahme', isMoney: false },
  directRevenue: { title: 'Direkter Umsatz', sub: 'HubSpot Payments + externe Quellen (LUMA)', isMoney: true },
  attributedRevenue: { title: 'Attributed Revenue', sub: 'Closed-Won mit Event-Touchpoint in 6 Monaten davor', isMoney: true },
};

const TRACK_LABEL = {
  on_track:  { label: 'On Track',  color: '#5cc4a5', bg: 'rgba(92,196,165,0.20)' },
  at_risk:   { label: 'At Risk',   color: '#f2c14e', bg: 'rgba(242,193,78,0.20)' },
  off_track: { label: 'Off-Track', color: '#e06bb5', bg: 'rgba(211,64,154,0.22)' },
};

function StatusPill({ trackStatus }) {
  const c = TRACK_LABEL[trackStatus] || TRACK_LABEL.off_track;
  return (
    <span style={{
      display: 'inline-block', padding: '3px 10px', borderRadius: 999,
      fontSize: 10, fontWeight: 700, letterSpacing: 0.08, textTransform: 'uppercase',
      background: c.bg, color: c.color, whiteSpace: 'nowrap',
    }}>
      {c.label}
    </span>
  );
}

function KpiTile({ id, kpi }) {
  const meta = KPI_LABELS[id];
  if (!meta) return null;
  const fmt = meta.isMoney ? fmtEur : fmtNum;
  const status = kpi.status;
  // Bar-Fill = % von Super-Wert (zeigt Position im Korridor)
  const fillPct = Math.min(100, (kpi.value / kpi.super) * 100);
  // Tick-Positionen
  const minPct = (kpi.min / kpi.super) * 100;
  const targetPct = (kpi.target / kpi.super) * 100;

  return (
    <div className={`kpi status-${status}`}>
      <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'flex-start', marginBottom: 6 }}>
        <div className="kpi__label">{meta.title}</div>
        <StatusPill trackStatus={kpi.trackStatus} />
      </div>
      <div>
        <span className="kpi__value">{fmt(kpi.value)}</span>
        <span className="kpi__pct">{kpi.pctOfTarget}% von Ziel</span>
      </div>
      <div style={{ fontSize: 11, color: 'rgba(255,255,255,0.45)', marginTop: 4 }}>{meta.sub}</div>
      <div className="kpi__corridor">
        <div className="kpi__bar" style={{ position: 'relative' }}>
          <div className="kpi__bar-fill" style={{ width: fillPct + '%' }} />
          <div style={{ position: 'absolute', left: `calc(${minPct}% - 1px)`, top: -2, bottom: -2, width: 2, background: 'rgba(255,255,255,0.4)' }} />
          <div style={{ position: 'absolute', left: `calc(${targetPct}% - 1px)`, top: -2, bottom: -2, width: 2, background: 'rgba(255,255,255,0.7)' }} />
        </div>
        <div className="kpi__ticks">
          <span>0</span>
          <span style={{ position: 'absolute', marginLeft: -10 }}> </span>
          <span>Min {fmt(kpi.min)}</span>
          <span>Ziel {fmt(kpi.target)}</span>
          <span>Super {fmt(kpi.super)}</span>
        </div>
      </div>
    </div>
  );
}

const FUNNEL_ORDER = ['Awareness', 'Interest', 'Desire', 'Action', 'Loyalty', 'Advocacy'];

function FunnelRow({ stage }) {
  return (
    <div className="funnel__row">
      <div className="funnel__stage">{stage.stage}</div>
      <div className="funnel__formats">{stage.formats.join(' · ')}</div>
      <div className="funnel__metric">
        <div className="funnel__metric-label">Personen</div>
        <div className="funnel__metric-value">{fmtNum(stage.uniqueContacts)}</div>
      </div>
      <div className="funnel__metric">
        <div className="funnel__metric-label">Opportunities</div>
        <div className="funnel__metric-value">{stage.opportunities}</div>
      </div>
      <div className="funnel__metric">
        <div className="funnel__metric-label">Att. Revenue</div>
        <div className="funnel__metric-value">{fmtEur(stage.attributedRevenue)}</div>
      </div>
    </div>
  );
}

function FormatCard({ fp }) {
  return (
    <div className="format-card">
      <div className="format-card__head">
        <div className="format-card__name">{fp.format}</div>
      </div>
      <div className="format-card__stats">
        <div>
          <div className="format-card__stat-label">Events</div>
          <div className="format-card__stat-value">{fp.eventCount}</div>
        </div>
        <div>
          <div className="format-card__stat-label">Anmeldungen (unique)</div>
          <div className="format-card__stat-value">{fmtNum(fp.registrants)}</div>
        </div>
        <div>
          <div className="format-card__stat-label">Vor Ort (Summe)</div>
          <div className="format-card__stat-value">{fp.actualAttendeesKnown > 0 ? fmtNum(fp.actualAttendees) : '—'}</div>
        </div>
        <div>
          <div className="format-card__stat-label">Teilnahmequote</div>
          <div className="format-card__stat-value">{fp.participationRate !== null ? fp.participationRate + '%' : '—'}</div>
        </div>
        <div>
          <div className="format-card__stat-label">Opportunities</div>
          <div className="format-card__stat-value">{fp.opportunities}</div>
        </div>
        <div>
          <div className="format-card__stat-label">Att. Revenue</div>
          <div className="format-card__stat-value" style={{ color: '#7dbcbe' }}>{fmtEur(fp.attributedRevenue)}</div>
        </div>
      </div>
    </div>
  );
}

function eventTypePill(format) {
  if (format.includes('Tribe')) return ['event-type-pill--tribe', 'Tribe'];
  if (format.includes('Webinar')) return ['event-type-pill--webinar', 'Webinar'];
  if (format.startsWith('Executive') || format.startsWith('L&D')) return ['event-type-pill--exec', format];
  return ['event-type-pill--other', format];
}

function EventTable({ events }) {
  const sorted = [...events].sort((a, b) => (a.startDate || '').localeCompare(b.startDate || ''));
  const now = Date.now();
  return (
    <table className="events-table">
      <thead>
        <tr>
          <th>Datum</th>
          <th>Event</th>
          <th>Format</th>
          <th className="num">Anmeldungen</th>
          <th className="num">Vor Ort</th>
        </tr>
      </thead>
      <tbody>
        {sorted.map(e => {
          const [pillCls, pillLabel] = eventTypePill(e.format);
          const isUpcoming = new Date(e.startDate || 0).getTime() > now;
          return (
            <tr key={e.id} style={isUpcoming ? { opacity: 0.55 } : null}>
              <td>{fmtDate(e.startDate)}</td>
              <td style={{ maxWidth: 420 }}>
                {e.name}
                {isUpcoming && (
                  <span style={{ fontSize: 10, color: '#f2c14e', marginLeft: 8, padding: '1px 7px', background: 'rgba(242,193,78,0.15)', borderRadius: 4, letterSpacing: 0.06, textTransform: 'uppercase', fontWeight: 700 }}>
                    Kommend
                  </span>
                )}
                {e.seededFromList && (
                  <span style={{ fontSize: 10, color: '#7dbcbe', marginLeft: 8, padding: '1px 6px', background: 'rgba(125,188,190,0.15)', borderRadius: 4, letterSpacing: 0.04, textTransform: 'uppercase' }}>
                    via Liste {e.seededFromList}
                  </span>
                )}
              </td>
              <td><span className={`event-type-pill ${pillCls}`}>{pillLabel}</span></td>
              <td className="num">{e.uniqueRegistrants > 0 ? e.uniqueRegistrants : '—'}</td>
              <td className="num" style={{ color: e.actualAttendees ? '#7dbcbe' : 'rgba(255,255,255,0.4)' }}>
                {e.actualAttendees > 0 ? e.actualAttendees : '—'}
              </td>
            </tr>
          );
        })}
      </tbody>
    </table>
  );
}

function OpenPipelineTable({ deals, accentColor = '#e06bb5', emptyText = 'Keine Deals.' }) {
  const [eventFilter, setEventFilter] = useState('all');

  if (!deals?.length) {
    return <div style={{ color: 'rgba(255,255,255,0.5)', padding: 20, textAlign: 'center', background: 'rgba(255,255,255,0.04)', borderRadius: 12 }}>
      {emptyText}
    </div>;
  }

  const eventStats = {};
  for (const d of deals) {
    const key = d.touchpointEvent || '—';
    if (!eventStats[key]) eventStats[key] = { count: 0, total: 0 };
    eventStats[key].count++;
    eventStats[key].total += (d.amount || 0);
  }
  const eventOptions = Object.entries(eventStats).sort((a, b) => b[1].total - a[1].total);

  const filtered = eventFilter === 'all' ? deals : deals.filter(d => d.touchpointEvent === eventFilter);
  const sorted = [...filtered].sort((a, b) => (b.enteredStageAt || '').localeCompare(a.enteredStageAt || ''));
  const total = filtered.reduce((s, d) => s + (d.amount || 0), 0);
  const totalAll = deals.reduce((s, d) => s + (d.amount || 0), 0);

  return (
    <div>
      <div style={{
        display: 'flex', flexWrap: 'wrap', alignItems: 'center', gap: 8, marginBottom: 12,
        padding: '12px 14px', background: 'rgba(255,255,255,0.04)', borderRadius: 10,
        border: '1px solid rgba(255,255,255,0.08)',
      }}>
        <span style={{ fontSize: 11, fontWeight: 700, letterSpacing: 0.08, textTransform: 'uppercase', color: 'rgba(255,255,255,0.55)' }}>Filter Touchpoint-Event:</span>
        <select value={eventFilter} onChange={(e) => setEventFilter(e.target.value)}
          style={{ background: 'rgba(255,255,255,0.08)', color: 'white', border: '1px solid rgba(255,255,255,0.15)', borderRadius: 8, padding: '6px 12px', fontFamily: 'inherit', fontSize: 13, outline: 'none', minWidth: 280, cursor: 'pointer' }}>
          <option value="all">Alle Events ({deals.length} Deals · {fmtEur(totalAll)})</option>
          {eventOptions.map(([name, stats]) => (
            <option key={name} value={name}>
              {name.length > 50 ? name.slice(0, 50) + '…' : name} ({stats.count} · {fmtEur(stats.total)})
            </option>
          ))}
        </select>
        {eventFilter !== 'all' && (
          <button onClick={() => setEventFilter('all')}
            style={{ background: 'rgba(211,64,154,0.2)', color: '#e06bb5', border: 'none', borderRadius: 999, padding: '4px 12px', fontSize: 11, fontWeight: 700, cursor: 'pointer', textTransform: 'uppercase', letterSpacing: 0.05 }}>
            Filter zurücksetzen
          </button>
        )}
      </div>

      <table className="events-table">
        <thead>
          <tr>
            <th>Deal-Erstellt</th>
            <th>Deal</th>
            <th>Touchpoint-Person</th>
            <th>Aktueller Stage</th>
            <th>Touchpoint-Event</th>
            <th className="num">Deal-Wert</th>
          </tr>
        </thead>
        <tbody>
          {sorted.map(d => {
            const [pillCls, pillLabel] = eventTypePill(d.touchpointFormat);
            return (
              <tr key={d.dealId}>
                <td>{fmtDate(d.enteredStageAt)}</td>
                <td style={{ maxWidth: 300 }}>{d.dealname || '—'}</td>
                <td style={{ fontSize: 12 }}>
                  <div style={{ fontWeight: 600 }}>{d.touchpointContactName || '—'}</div>
                  {d.touchpointContactCompany && (
                    <div style={{ color: 'rgba(255,255,255,0.45)', fontSize: 11 }}>{d.touchpointContactCompany}</div>
                  )}
                </td>
                <td style={{ fontSize: 12, color: 'rgba(255,255,255,0.7)' }}>{d.currentStageLabel || '—'}</td>
                <td style={{ color: 'rgba(255,255,255,0.6)', maxWidth: 220, fontSize: 12 }}>{d.touchpointEvent}</td>
                <td className="num" style={{ fontWeight: 700 }}>{fmtEur(d.amount)}</td>
              </tr>
            );
          })}
          <tr style={{ borderTop: '1px solid rgba(255,255,255,0.15)' }}>
            <td colSpan="5" style={{ fontWeight: 800, textTransform: 'uppercase', fontSize: 11, letterSpacing: '0.08em', color: 'rgba(255,255,255,0.7)' }}>
              {eventFilter === 'all' ? 'Gesamt-Pipeline' : 'Gefiltert'} — {filtered.length} Deal{filtered.length === 1 ? '' : 's'}
            </td>
            <td className="num" style={{ fontWeight: 800, fontSize: 16, color: accentColor }}>{fmtEur(total)}</td>
          </tr>
        </tbody>
      </table>
    </div>
  );
}

function UpcomingEventCard({ ev }) {
  const statusColors = {
    on_track:  { color: '#5cc4a5', bg: 'rgba(92,196,165,0.20)',  label: 'On Track' },
    at_risk:   { color: '#f2c14e', bg: 'rgba(242,193,78,0.20)',  label: 'At Risk' },
    off_track: { color: '#e06bb5', bg: 'rgba(211,64,154,0.22)',  label: 'Off-Track' },
  };
  const s = statusColors[ev.trackStatus] || statusColors.off_track;
  const reg = ev.uniqueRegistrants || 0;
  const fillPct = ev.anmeldungZiel ? Math.min(100, (reg / ev.anmeldungZiel) * 100) : 0;
  const noShowPct = Math.round((ev.noShowRate || 0) * 100);

  return (
    <div className="format-card" style={{ borderLeft: `3px solid ${s.color}` }}>
      <div className="format-card__head">
        <div>
          <div style={{ fontSize: 10, fontWeight: 700, letterSpacing: 0.08, textTransform: 'uppercase', color: '#7dbcbe' }}>
            in {ev.daysUntil} Tagen · {fmtDate(ev.startDate)}
          </div>
          <div className="format-card__name" style={{ marginTop: 4, fontSize: 14 }}>{ev.name}</div>
        </div>
        <span style={{ fontSize: 10, fontWeight: 700, letterSpacing: 0.06, textTransform: 'uppercase', padding: '3px 10px', borderRadius: 999, background: s.bg, color: s.color, whiteSpace: 'nowrap' }}>
          {s.label}
        </span>
      </div>

      {/* Progress: Anmeldungen vs. Anmeldungs-Ziel */}
      <div style={{ marginBottom: 14 }}>
        <div style={{ display: 'flex', alignItems: 'baseline', gap: 6, marginBottom: 4 }}>
          <span style={{ fontSize: 28, fontWeight: 800, color: s.color, letterSpacing: -0.02 }}>{reg}</span>
          <span style={{ fontSize: 13, color: 'rgba(255,255,255,0.45)' }}>/ {ev.anmeldungZiel} Anmeldungen</span>
          {ev.progressPct !== null && (
            <span style={{ fontSize: 13, fontWeight: 700, color: s.color, marginLeft: 'auto' }}>{ev.progressPct}%</span>
          )}
        </div>
        <div style={{ height: 6, borderRadius: 3, background: 'rgba(255,255,255,0.08)', overflow: 'hidden' }}>
          <div style={{ height: '100%', width: fillPct + '%', background: s.color, transition: 'width 0.6s' }} />
        </div>
      </div>

      {/* Detail-Grid */}
      <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 8, fontSize: 12, borderTop: '1px solid rgba(255,255,255,0.06)', paddingTop: 12 }}>
        <div>
          <div style={{ fontSize: 10, color: 'rgba(255,255,255,0.5)', textTransform: 'uppercase', letterSpacing: 0.06 }}>Vor-Ort-Ziel</div>
          <div style={{ fontWeight: 700, fontSize: 14 }}>{ev.vorOrtZiel}</div>
        </div>
        <div>
          <div style={{ fontSize: 10, color: 'rgba(255,255,255,0.5)', textTransform: 'uppercase', letterSpacing: 0.06 }}>Aktuell erwartet vor Ort</div>
          <div style={{ fontWeight: 700, fontSize: 14, color: ev.expectedAttendees >= ev.vorOrtZiel ? '#7dbcbe' : 'rgba(255,255,255,0.85)' }}>
            {ev.expectedAttendees}
          </div>
        </div>
        <div>
          <div style={{ fontSize: 10, color: 'rgba(255,255,255,0.5)', textTransform: 'uppercase', letterSpacing: 0.06 }}>Fehlende Anmeldungen</div>
          <div style={{ fontWeight: 700, fontSize: 14 }}>{ev.missingRegistrations}</div>
        </div>
        <div>
          <div style={{ fontSize: 10, color: 'rgba(255,255,255,0.5)', textTransform: 'uppercase', letterSpacing: 0.06 }}>Nötig / Tag</div>
          <div style={{ fontWeight: 700, fontSize: 14 }}>{ev.missingRegistrations === 0 ? '—' : ev.perDayNeeded.toFixed(1)}</div>
        </div>
        <div>
          <div style={{ fontSize: 10, color: 'rgba(255,255,255,0.5)', textTransform: 'uppercase', letterSpacing: 0.06 }}>No-Show Annahme</div>
          <div style={{ fontWeight: 700, fontSize: 14 }}>{noShowPct}%</div>
        </div>
        <div>
          <div style={{ fontSize: 10, color: 'rgba(255,255,255,0.5)', textTransform: 'uppercase', letterSpacing: 0.06 }}>Hypothese Opps</div>
          <div style={{ fontWeight: 700, fontSize: 14 }}>{ev.target?.opportunities ?? '—'}</div>
        </div>
      </div>
    </div>
  );
}

function ClosedWonTable({ deals }) {
  const [eventFilter, setEventFilter] = useState('all');

  if (!deals?.length) {
    return <div style={{ color: 'rgba(255,255,255,0.5)', padding: 20, textAlign: 'center', background: 'rgba(255,255,255,0.04)', borderRadius: 12 }}>
      Noch keine Closed-Won Deals 2026 mit Event-Touchpoint.
    </div>;
  }

  // Unique Events + Anzahl + Summe pro Event als Filter-Optionen
  const eventStats = {};
  for (const d of deals) {
    const key = d.touchpointEvent || '—';
    if (!eventStats[key]) eventStats[key] = { count: 0, total: 0, format: d.touchpointFormat };
    eventStats[key].count++;
    eventStats[key].total += (d.amount || 0);
  }
  const eventOptions = Object.entries(eventStats)
    .sort((a, b) => b[1].total - a[1].total);

  const filtered = eventFilter === 'all' ? deals : deals.filter(d => d.touchpointEvent === eventFilter);
  const sorted = [...filtered].sort((a, b) => (b.closedate || '').localeCompare(a.closedate || ''));
  const total = filtered.reduce((s, d) => s + (d.amount || 0), 0);

  return (
    <div>
      {/* Filter-Bar */}
      <div style={{
        display: 'flex', flexWrap: 'wrap', alignItems: 'center', gap: 8, marginBottom: 12,
        padding: '12px 14px', background: 'rgba(255,255,255,0.04)', borderRadius: 10,
        border: '1px solid rgba(255,255,255,0.08)',
      }}>
        <span style={{ fontSize: 11, fontWeight: 700, letterSpacing: 0.08, textTransform: 'uppercase', color: 'rgba(255,255,255,0.55)' }}>
          Filter Touchpoint-Event:
        </span>
        <select
          value={eventFilter}
          onChange={(e) => setEventFilter(e.target.value)}
          style={{
            background: 'rgba(255,255,255,0.08)', color: 'white',
            border: '1px solid rgba(255,255,255,0.15)', borderRadius: 8,
            padding: '6px 12px', fontFamily: 'inherit', fontSize: 13, outline: 'none',
            minWidth: 280, cursor: 'pointer',
          }}
        >
          <option value="all">Alle Events ({deals.length} Deals · {fmtEur(deals.reduce((s, d) => s + (d.amount || 0), 0))})</option>
          {eventOptions.map(([name, stats]) => (
            <option key={name} value={name}>
              {name.length > 50 ? name.slice(0, 50) + '…' : name} ({stats.count} · {fmtEur(stats.total)})
            </option>
          ))}
        </select>
        {eventFilter !== 'all' && (
          <button
            onClick={() => setEventFilter('all')}
            style={{
              background: 'rgba(211,64,154,0.2)', color: '#e06bb5',
              border: 'none', borderRadius: 999, padding: '4px 12px', fontSize: 11,
              fontWeight: 700, cursor: 'pointer', textTransform: 'uppercase', letterSpacing: 0.05,
            }}
          >
            Filter zurücksetzen
          </button>
        )}
      </div>

      <table className="events-table">
        <thead>
          <tr>
            <th>Close-Datum</th>
            <th>Deal</th>
            <th>Touchpoint-Person</th>
            <th>Touchpoint-Format</th>
            <th>Touchpoint-Event</th>
            <th className="num">Amount</th>
          </tr>
        </thead>
        <tbody>
          {sorted.map(d => {
            const [pillCls, pillLabel] = eventTypePill(d.touchpointFormat);
            return (
              <tr key={d.dealId}>
                <td>{fmtDate(d.closedate)}</td>
                <td style={{ maxWidth: 320 }}>{d.dealname || '—'}</td>
                <td style={{ fontSize: 12 }}>
                  <div style={{ fontWeight: 600 }}>{d.touchpointContactName || '—'}</div>
                  {d.touchpointContactCompany && (
                    <div style={{ color: 'rgba(255,255,255,0.45)', fontSize: 11 }}>{d.touchpointContactCompany}</div>
                  )}
                </td>
                <td><span className={`event-type-pill ${pillCls}`}>{pillLabel}</span></td>
                <td style={{ color: 'rgba(255,255,255,0.6)', maxWidth: 240, fontSize: 12 }}>{d.touchpointEvent}</td>
                <td className="num" style={{ fontWeight: 700 }}>{fmtEur(d.amount)}</td>
              </tr>
            );
          })}
          <tr style={{ borderTop: '1px solid rgba(255,255,255,0.15)' }}>
            <td colSpan="5" style={{ fontWeight: 800, textTransform: 'uppercase', fontSize: 11, letterSpacing: '0.08em', color: 'rgba(255,255,255,0.7)' }}>
              {eventFilter === 'all' ? 'Gesamt' : 'Gefiltert'} — {filtered.length} Deal{filtered.length === 1 ? '' : 's'}
            </td>
            <td className="num" style={{ fontWeight: 800, fontSize: 16, color: '#7dbcbe' }}>{fmtEur(total)}</td>
          </tr>
        </tbody>
      </table>
    </div>
  );
}

function StatusBadge({ status, stageLabel }) {
  const config = {
    won:  { bg: 'rgba(125,188,190,0.2)', color: '#7dbcbe', label: 'Closed Won' },
    lost: { bg: 'rgba(255,255,255,0.06)', color: 'rgba(255,255,255,0.45)', label: 'Closed Lost' },
    open: { bg: 'rgba(211,64,154,0.18)', color: '#e06bb5', label: 'Aktiv' },
  };
  const c = config[status] || config.open;
  return (
    <span style={{
      display: 'inline-block', padding: '2px 9px', borderRadius: 999, background: c.bg, color: c.color,
      fontSize: 10, fontWeight: 700, letterSpacing: 0.04, textTransform: 'uppercase',
    }} title={stageLabel}>
      {c.label}
    </span>
  );
}

function OpportunityTable({ deals }) {
  if (!deals?.length) {
    return <div style={{ color: 'rgba(255,255,255,0.5)', padding: 20, textAlign: 'center', background: 'rgba(255,255,255,0.04)', borderRadius: 12 }}>
      Noch keine neuen Opportunities 2026 mit Event-Touchpoint.
    </div>;
  }
  const sorted = [...deals].sort((a, b) => (b.enteredStageAt || '').localeCompare(a.enteredStageAt || ''));
  const open = deals.filter(d => d.status === 'open').length;
  const won  = deals.filter(d => d.status === 'won').length;
  const lost = deals.filter(d => d.status === 'lost').length;
  return (
    <div>
      <div style={{ display: 'flex', gap: 16, marginBottom: 12, fontSize: 12, color: 'rgba(255,255,255,0.7)' }}>
        <span>Aktiv: <strong style={{ color: '#e06bb5' }}>{open}</strong></span>
        <span>Closed Won: <strong style={{ color: '#7dbcbe' }}>{won}</strong></span>
        <span>Closed Lost: <strong style={{ color: 'rgba(255,255,255,0.6)' }}>{lost}</strong></span>
      </div>
      <table className="events-table">
        <thead>
          <tr>
            <th>Deal-Erstellt</th>
            <th>Deal</th>
            <th>Status heute</th>
            <th>Touchpoint-Format</th>
            <th>Touchpoint-Event</th>
          </tr>
        </thead>
        <tbody>
          {sorted.map(d => {
            const [pillCls, pillLabel] = eventTypePill(d.touchpointFormat);
            return (
              <tr key={d.dealId}>
                <td>{fmtDate(d.enteredStageAt)}</td>
                <td style={{ maxWidth: 380 }}>{d.dealname || '—'}</td>
                <td><StatusBadge status={d.status} stageLabel={d.currentStageLabel} /></td>
                <td><span className={`event-type-pill ${pillCls}`}>{pillLabel}</span></td>
                <td style={{ color: 'rgba(255,255,255,0.6)', maxWidth: 280, fontSize: 12 }}>{d.touchpointEvent}</td>
              </tr>
            );
          })}
        </tbody>
      </table>
    </div>
  );
}

// API-Base — der Cloudflare Worker
const API_BASE = window.location.hostname === 'localhost'
  ? 'http://localhost:8787'
  : 'https://tam-event-dashboard-api.calm-dew-0c5d.workers.dev';

function Dashboard() {
  const [data, setData] = useState(null);
  const [meta, setMeta] = useState(null);
  const [refreshing, setRefreshing] = useState(false);
  const attributionMonths = 6;

  async function load() {
    const r = await fetch(API_BASE + '/api/dashboard?attributionWindowMonths=' + attributionMonths, { credentials: 'include' });
    const d = await r.json();
    setData(d);
    const m = await fetch(API_BASE + '/api/meta', { credentials: 'include' }).then(r => r.json()).catch(() => null);
    setMeta(m);
  }

  useEffect(() => { load(); }, []);

  async function refreshData() {
    if (refreshing) return;
    setRefreshing(true);
    try {
      await fetch(API_BASE + '/api/refresh', { method: 'POST', credentials: 'include' });
      await load();
    } finally {
      setRefreshing(false);
    }
    return;
    // Polling (nur wenn Refresh async ist — Worker macht es synchron)
    const poll = setInterval(async () => {
      const s = await fetch(API_BASE + '/api/refresh/status', { credentials: 'include' }).then(r => r.json());
      if (!s.refreshing) {
        clearInterval(poll);
        await load();
        setRefreshing(false);
      }
    }, 2000);
  }

  if (!data) return <div className="loading">Lade Dashboard...</div>;

  const fetchedAgo = meta ? new Date(meta.fetchedAt).toLocaleString('de-DE', { dateStyle: 'medium', timeStyle: 'short' }) : '—';

  return (
    <div className="app">
      <div className="header">
        <div>
          <div className="header__title">TAM Akademie · Event Strategy 2026</div>
          <h1>Event Impact Dashboard</h1>
          <div className="header__sub">
            Letzte Aktualisierung: {fetchedAgo}
          </div>
        </div>
        <div className="header__actions">
          <button className="btn btn--ghost" onClick={refreshData} disabled={refreshing}>
            {refreshing ? 'Lädt …' : '↻  Daten neu laden'}
          </button>
        </div>
      </div>

      {/* EBENE 1: KPI-Cockpit */}
      <div className="section">
        <div className="section__head">
          <h2>Jahresziele 2026 — bisheriger Beitrag</h2>
          <div className="section__sub">Aggregiert über alle bereits durchgeführten Events · Korridor: Min · Ziel · Super</div>
        </div>
        <div className="kpi-grid">
          {Object.entries(data.kpis).map(([id, kpi]) => (
            <KpiTile key={id} id={id} kpi={kpi} />
          ))}
        </div>
      </div>

      {/* EBENE 2: Vorschau — Kommende Events */}
      {data.upcomingEvents?.length > 0 && (
        <div className="section">
          <div className="section__head">
            <h2>Vorschau — Kommende Events</h2>
            <div className="section__sub">Anmeldungen vs. Hypothesen-Ziel · sortiert nach Datum</div>
          </div>
          <div className="format-grid">
            {data.upcomingEvents.map(ev => <UpcomingEventCard key={ev.id} ev={ev} />)}
          </div>
        </div>
      )}

      {/* Trenn-Header: ab hier Rückblick */}
      <div style={{ borderTop: '1px solid rgba(255,255,255,0.08)', paddingTop: 32, marginTop: 12, marginBottom: 8 }}>
        <div style={{ fontSize: 11, fontWeight: 700, letterSpacing: 0.18, textTransform: 'uppercase', color: 'rgba(255,255,255,0.45)' }}>
          Rückblick — bisher durchgeführte Events
        </div>
      </div>

      {/* EBENE 3: Format-Performance */}
      <div className="section">
        <div className="section__head">
          <h2>Format-Performance</h2>
          <div className="section__sub">Welche Formate liefern</div>
        </div>
        <div className="format-grid">
          {data.formatPerformance.filter(f => f.eventCount > 0 || f.registrants > 0).map(fp => (
            <FormatCard key={fp.format} fp={fp} />
          ))}
        </div>
      </div>

      {/* EBENE 4: Einzel-Events */}
      <div className="section">
        <div className="section__head">
          <h2>Alle Events 2026</h2>
          <div className="section__sub">Drilldown · Frankfurt-Stammtisch (abgesagt) ist ausgeschlossen</div>
        </div>
        <EventTable events={data.events} />
      </div>

      {/* EBENE 5: Closed-Won Deals mit Event-Touchpoints */}
      <div className="section">
        <div className="section__head">
          <h2>Closed-Won Deals mit Event-Touchpoints</h2>
          <div className="section__sub">Realisierter Umsatz · Closed-Won 2026 · Event-Touchpoint in den 6 Monaten vor Close</div>
        </div>
        <ClosedWonTable deals={data.details.closedWon} />
      </div>

      {/* EBENE 6: Offene Pipeline-Deals mit Event-Touchpoints */}
      <div className="section">
        <div className="section__head">
          <h2>Offene Pipeline-Deals mit Event-Touchpoints</h2>
          <div className="section__sub">Aktive Opportunities · Deal erstellt innerhalb 6 Monate nach Event-Teilnahme</div>
        </div>
        <OpenPipelineTable deals={(data.details.opportunities || []).filter(d => d.status === 'open')} accentColor="#e06bb5" emptyText="Keine offenen Pipeline-Deals mit Event-Touchpoints." />
      </div>

      {/* EBENE 7: Closed-Lost Deals mit Event-Touchpoints */}
      <div className="section">
        <div className="section__head">
          <h2>Closed-Lost Deals mit Event-Touchpoints</h2>
          <div className="section__sub">Verlorene Opportunities · Attribution-Fenster 6 Monate</div>
        </div>
        <OpenPipelineTable deals={(data.details.opportunities || []).filter(d => d.status === 'lost')} accentColor="rgba(255,255,255,0.5)" emptyText="Keine Closed-Lost-Deals mit Event-Touchpoints." />
      </div>

      <div className="footer-note">
        Datenquelle: HubSpot · Marketing Events, CRM Deals, Commerce Payments · Cache: {meta?.fetchedAt ? new Date(meta.fetchedAt).toISOString() : '—'}
      </div>
    </div>
  );
}

ReactDOM.createRoot(document.getElementById('root')).render(<Dashboard />);
