/* eslint-disable */
/* =========================================================
   TEO IoT Dashboard — Overview screen.
   ========================================================= */

// =============================================================
// Portfolio map — stylised Manchester city-centre map showing every
// site as a pin. Coloured by health, shaped by cohort. Tap a pin
// to switch to that site. Designed in the TEO architectural style.
// =============================================================
const SITE_COORDS = {
  // Spinningfields cluster (centre-west)
  'FORM-LCF-01':  { x: 460, y: 232, district:'Spinningfields' },
  'FORM-XYZ-01':  { x: 514, y: 250, district:'Spinningfields' },
  'FORM-NO1-01':  { x: 532, y: 218, district:'Spinningfields' },
  'FORM-SPN-01':  { x: 490, y: 280, district:'Spinningfields' },
  // Castlefield / St Johns
  'FORM-BW-01':   { x: 432, y: 318, district:'Castlefield' },
  'FORM-STJ-01':  { x: 396, y: 268, district:'St Johns' },
  // Outliers
  'FORM-RIV-01':  { x: 280, y: 180, district:'Salford Quays' },
  'FORM-ECO-01':  { x: 380, y: 412, district:'Chorlton' },
};

const PortfolioMap = ({ sites, currentSite, onSiteChange, devices, alerts }) => {
  const isMobile = useIsMobile();
  // Pin health per site
  const health = {};
  const alertCount = {};
  sites.forEach(s => {
    const sd = devices.filter(d => d.site === s.id);
    const warn = sd.filter(d => d.state === 'warn').length;
    const down = sd.filter(d => d.state === 'down').length;
    health[s.id] = down > 0 ? 'down' : warn > 0 ? 'warn' : 'ok';
    alertCount[s.id] = alerts.filter(a => !a.ack && a.site === s.id).length;
  });
  const STATE_C = { ok:'#3A6FF8', warn:'#E0A13B', down:'#C44A4A' };
  const totalDevices = devices.length;
  const totalAlerts = alerts.filter(a => !a.ack).length;

  return (
    <Card pad={0}>
      <div style={{
        padding: isMobile ? '14px 16px' : '18px 24px',
        borderBottom:'1px solid #EDEDED',
        display:'flex', justifyContent:'space-between', alignItems:'flex-start', gap:12, flexWrap:'wrap'
      }}>
        <div>
          <Eyebrow accent>Portfolio</Eyebrow>
          <div style={{ fontSize:18, color:'#0A0F1F', marginTop:4 }}>{sites.length} buildings · Manchester area</div>
          <div style={{ fontSize:12, color:'#5A5E6E', marginTop:4 }}>Tap a pin to open that twin</div>
        </div>
        <div style={{ display:'flex', gap:18, alignItems:'baseline' }}>
          <div>
            <div style={{ fontSize:22, fontWeight:300, letterSpacing:'-0.04em', color:'#0A0F1F', fontVariantNumeric:'tabular-nums' }}>{totalDevices}</div>
            <div style={{ fontSize:10, color:'#5A5E6E', letterSpacing:'0.08em', textTransform:'uppercase' }}>devices</div>
          </div>
          <div>
            <div style={{ fontSize:22, fontWeight:300, letterSpacing:'-0.04em', color: totalAlerts > 0 ? '#E0A13B' : '#0A0F1F', fontVariantNumeric:'tabular-nums' }}>{totalAlerts}</div>
            <div style={{ fontSize:10, color:'#5A5E6E', letterSpacing:'0.08em', textTransform:'uppercase' }}>open alerts</div>
          </div>
        </div>
      </div>

      <div style={{ position:'relative', background:'#FAFBFC', borderBottom:'1px solid #EDEDED' }}>
        <svg viewBox="0 0 800 500" preserveAspectRatio="xMidYMid meet" style={{ display:'block', width:'100%', height:'auto', background:'#FAFBFC' }}>
          {/* Background grid */}
          <defs>
            <pattern id="mapGrid20" width="20" height="20" patternUnits="userSpaceOnUse">
              <path d="M20 0 L0 0 0 20" fill="none" stroke="#EBEEF2" strokeWidth="0.3"/>
            </pattern>
            <pattern id="mapGrid100" width="100" height="100" patternUnits="userSpaceOnUse">
              <path d="M100 0 L0 0 0 100" fill="none" stroke="#DCE0E7" strokeWidth="0.4"/>
            </pattern>
          </defs>
          <rect width="800" height="500" fill="url(#mapGrid20)" />
          <rect width="800" height="500" fill="url(#mapGrid100)" />

          {/* River Irwell — flowing curve */}
          <path d="M 60 80 Q 180 140 240 200 Q 290 240 310 320 Q 330 400 360 480"
                fill="none" stroke="#7A9CFF" strokeWidth="22" opacity="0.14" strokeLinecap="round" />
          <path d="M 60 80 Q 180 140 240 200 Q 290 240 310 320 Q 330 400 360 480"
                fill="none" stroke="#3A6FF8" strokeWidth="3" opacity="0.35" strokeLinecap="round" strokeDasharray="6 4" />
          <text x="140" y="115" style={{ font:"500 9px 'IBM Plex Sans'", fill:'#5A5E6E', letterSpacing:'0.16em', textTransform:'uppercase' }} transform="rotate(28 140 115)">River Irwell</text>

          {/* Major roads — light grey ghosts */}
          <line x1="120" y1="220" x2="780" y2="280" stroke="#E0E4ED" strokeWidth="3" />
          <line x1="240" y1="60" x2="640" y2="500" stroke="#E0E4ED" strokeWidth="3" />
          <line x1="80" y1="380" x2="780" y2="420" stroke="#E0E4ED" strokeWidth="3" />
          <line x1="450" y1="40" x2="500" y2="500" stroke="#E0E4ED" strokeWidth="2" />

          {/* District labels — architectural style */}
          {[
            ['SPINNINGFIELDS', 495, 192],
            ['CASTLEFIELD',    442, 360],
            ['ST JOHNS',       380, 240],
            ['NORTHERN QTR',   650, 240],
            ['ANCOATS',        680, 360],
            ['SALFORD QUAYS',  220, 150],
            ['CHORLTON',       380, 460],
          ].map(([label, x, y]) => (
            <text key={label} x={x} y={y} textAnchor="middle" style={{
              font:"500 10px 'IBM Plex Sans'", fill:'#878787',
              letterSpacing:'0.14em', textTransform:'uppercase'
            }}>{label}</text>
          ))}

          {/* Compass rose */}
          <g transform="translate(740, 70)">
            <circle cx="0" cy="0" r="14" fill="#FFF" stroke="#0A0F1F" strokeWidth="0.8" />
            <path d="M 0 -10 L 4 5 L 0 2 L -4 5 Z" fill="#0A0F1F" />
            <text x="0" y="30" textAnchor="middle" style={{ font:"500 9px 'IBM Plex Sans'", fill:'#0A0F1F', letterSpacing:'0.1em' }}>N</text>
          </g>

          {/* Site pins */}
          {sites.map(s => {
            const c = SITE_COORDS[s.id];
            if (!c) return null;
            const active = s.id === currentSite.id;
            const h = health[s.id];
            const color = STATE_C[h] || STATE_C.ok;
            const isEco = s.cohort === 'Eco housing';
            const count = alertCount[s.id] || 0;
            const r = active ? 14 : 11;
            return (
              <g key={s.id} onClick={() => onSiteChange(s)} style={{ cursor:'pointer' }}>
                {/* Pulse halo for active */}
                {active && (
                  <circle cx={c.x} cy={c.y} r={r}>
                    <animate attributeName="r" values={`${r};${r+12};${r}`} dur="2.4s" repeatCount="indefinite" />
                    <animate attributeName="opacity" values="0.45;0;0.45" dur="2.4s" repeatCount="indefinite" />
                    <set attributeName="fill" to="none" />
                    <set attributeName="stroke" to={color} />
                    <set attributeName="stroke-width" to="2" />
                  </circle>
                )}
                {/* Pin background */}
                <circle cx={c.x} cy={c.y} r={r} fill="#FFF" stroke={color} strokeWidth={active ? 2.5 : 1.5} />
                {/* Shape: triangle for eco, dot for commercial */}
                {isEco ? (
                  <path
                    d={`M ${c.x} ${c.y - 5} L ${c.x + 4.5} ${c.y + 3} L ${c.x - 4.5} ${c.y + 3} Z`}
                    fill={color}
                  />
                ) : (
                  <circle cx={c.x} cy={c.y} r="4" fill={color} />
                )}
                {/* Alert badge */}
                {count > 0 && (
                  <g>
                    <circle cx={c.x + r - 2} cy={c.y - r + 2} r="6" fill="#C44A4A" />
                    <text x={c.x + r - 2} y={c.y - r + 5} textAnchor="middle" style={{
                      font:"600 8px 'IBM Plex Mono', monospace", fill:'#FFF'
                    }}>{count > 9 ? '9+' : count}</text>
                  </g>
                )}
                {/* Building label */}
                <text x={c.x} y={c.y + r + 14} textAnchor="middle" style={{
                  font: active ? "500 12px 'IBM Plex Sans'" : "400 11px 'IBM Plex Sans'",
                  fill:'#0A0F1F',
                  letterSpacing:'0.02em',
                  paintOrder:'stroke',
                  stroke:'#FAFBFC',
                  strokeWidth:'3px'
                }}>
                  {s.name}
                </text>
              </g>
            );
          })}
        </svg>
      </div>

      {/* Legend */}
      <div style={{
        padding: isMobile ? '10px 16px' : '12px 24px',
        display:'flex', gap:16, fontSize:11, color:'#5A5E6E',
        flexWrap:'wrap', alignItems:'center'
      }}>
        <span style={{ display:'flex', alignItems:'center', gap:6 }}><StatusDot state="ok" />Operational</span>
        <span style={{ display:'flex', alignItems:'center', gap:6 }}><StatusDot state="warn" />Degraded</span>
        <span style={{ display:'flex', alignItems:'center', gap:6 }}><StatusDot state="down" />Offline</span>
        <Hairline vertical />
        <span style={{ display:'flex', alignItems:'center', gap:6 }}>
          <span style={{ width:8, height:8, borderRadius:'50%', background:'#3A6FF8' }} /> Commercial
        </span>
        <span style={{ display:'flex', alignItems:'center', gap:6 }}>
          <span style={{ width:0, height:0, borderLeft:'5px solid transparent', borderRight:'5px solid transparent', borderBottom:'8px solid #3A6FF8' }} /> Eco housing
        </span>
      </div>
    </Card>
  );
};

// Mobile-only hero card above the KPI strip. Punches up the
// above-the-fold with site identity + overall status + a fleet
// state mix bar. Hidden on desktop where the KPI strip leads.
const MobileSiteHero = ({ site, devices, alerts }) => {
  const siteDevices = devices.filter(d => d.site === site.id);
  const counts = { ok:0, warn:0, down:0, idle:0 };
  siteDevices.forEach(d => { counts[d.state] = (counts[d.state]||0)+1; });
  const total = siteDevices.length;
  const openAlerts = alerts.filter(a => !a.ack && a.site === site.id).length;
  const overall = counts.down > 0 ? 'down' : counts.warn > 0 ? 'warn' : 'ok';
  const statusLabel = overall === 'ok' ? 'All systems operational'
                    : overall === 'warn' ? 'Degraded · attention needed'
                    : 'Critical · action required';
  const onlinePct = total ? Math.round((counts.ok / total) * 100) : 0;

  return (
    <div style={{
      background:'#0A0F1F', color:'#FFF',
      padding:'18px 16px 20px', borderBottom:'1px solid rgba(255,255,255,.12)',
      position:'relative', overflow:'hidden'
    }}>
      {/* Subtle radial accent in the background */}
      <div style={{
        position:'absolute', top:-40, right:-40, width:180, height:180,
        background:'radial-gradient(circle at center, rgba(58,111,248,.22), transparent 70%)',
        pointerEvents:'none'
      }} />
      <div style={{ position:'relative', zIndex:1 }}>
        <div style={{
          fontSize:10, letterSpacing:'0.14em', textTransform:'uppercase',
          color:'rgba(255,255,255,.55)', fontWeight:500
        }}>
          {site.customer || site.sector || 'Workspace'} · {site.id}
        </div>
        <div style={{
          fontSize:24, fontWeight:300, letterSpacing:'-0.04em',
          color:'#FFF', margin:'8px 0 16px', lineHeight:1.2
        }}>
          {site.name.replace(/^[^·]+·\s/, '')}
        </div>

        {/* Status row */}
        <div style={{ display:'flex', alignItems:'center', gap:10, marginBottom:14 }}>
          <span style={{
            width:8, height:8, borderRadius:'50%',
            background: overall==='ok'?'#3A6FF8':overall==='warn'?'#E0A13B':'#C44A4A',
            boxShadow:`0 0 0 4px ${overall==='ok'?'rgba(58,111,248,.25)':overall==='warn'?'rgba(224,161,59,.25)':'rgba(196,74,74,.25)'}`
          }} />
          <span style={{ fontSize:13, color:'#FFF', fontWeight:400 }}>{statusLabel}</span>
        </div>

        {/* Fleet state mix bar */}
        <div style={{ display:'flex', height:6, background:'rgba(255,255,255,.08)', marginBottom:10 }}>
          {counts.ok   > 0 && <div style={{ flex:counts.ok,   background:'#3A6FF8' }} />}
          {counts.warn > 0 && <div style={{ flex:counts.warn, background:'#E0A13B' }} />}
          {counts.down > 0 && <div style={{ flex:counts.down, background:'#C44A4A' }} />}
          {counts.idle > 0 && <div style={{ flex:counts.idle, background:'#878787' }} />}
        </div>

        {/* Stat row */}
        <div style={{ display:'flex', justifyContent:'space-between', alignItems:'center', gap:8, fontFamily:'IBM Plex Mono, monospace', fontSize:11, color:'rgba(255,255,255,.65)' }}>
          <span>
            <strong style={{ color:'#FFF', fontWeight:500, marginRight:4 }}>{onlinePct}%</strong>
            online
          </span>
          <span>{counts.ok}/{total} devices</span>
          <span style={{ color: openAlerts > 0 ? '#E0A13B' : 'rgba(255,255,255,.65)' }}>
            {openAlerts} alert{openAlerts !== 1 ? 's' : ''}
          </span>
        </div>
      </div>
    </div>
  );
};

const KPIStrip = ({ devices, alerts }) => {
  const total = devices.length;
  const online = devices.filter(d => d.state === 'ok').length;
  const warn   = devices.filter(d => d.state === 'warn').length;
  const down   = devices.filter(d => d.state === 'down').length;
  const openAlerts = alerts.filter(a => !a.ack).length;
  return (
    <div data-kpi-strip style={{ display:'grid', gridTemplateColumns:'repeat(4,1fr)', borderTop:'1px solid #EDEDED', borderBottom:'1px solid #EDEDED', background:'#FFF' }}>
      <MetricTile label="Devices online"    value={`${online}`} unit={`/ ${total}`} delta={down ? `-${down}` : '+0'} sub={down ? `${down} offline · ${warn} degraded` : 'All operational'} />
      <MetricTile label="Telemetry rate"    value="842"  unit="msg/s" delta="+8.1%" sub="vs prior hour" />
      <MetricTile label="Open alerts"       value={`${openAlerts}`} delta={openAlerts ? `+${openAlerts}` : '+0'} sub="0 critical pending ack" />
      <MetricTile label="Mean time to ack"  value="1m"   unit="12s"   delta="-34%" sub="30-day rolling" />
    </div>
  );
};

const FleetStatusDonut = ({ devices }) => {
  const counts = { ok:0, warn:0, down:0, idle:0 };
  devices.forEach(d => { counts[d.state] = (counts[d.state]||0)+1; });
  const slices = [
    { label:'Online',   value:counts.ok,   color:'#3A6FF8' },
    { label:'Degraded', value:counts.warn, color:'#E0A13B' },
    { label:'Offline',  value:counts.down, color:'#C44A4A' },
    { label:'Idle',     value:counts.idle, color:'#878787' },
  ].filter(s => s.value > 0);
  return (
    <Donut
      title="Fleet status"
      total={devices.length}
      totalLabel="devices"
      slices={slices}
    />
  );
};

const SectorBreakdown = ({ devices }) => {
  // Aggregate by kind, take top 6
  const byKind = {};
  devices.forEach(d => { byKind[d.kind] = (byKind[d.kind]||0)+1; });
  const palette = ['#0A0F1F','#3A6FF8','#7A9CFF','#23459E','#5A5E6E','#878787'];
  const slices = Object.entries(byKind)
    .sort((a,b) => b[1]-a[1])
    .slice(0, 6)
    .map(([k,v], i) => ({ label: k.charAt(0).toUpperCase()+k.slice(1).replace('-',' '), value:v, color:palette[i % palette.length] }));
  return (
    <Donut
      title="Devices by class"
      total={devices.length}
      totalLabel="total"
      slices={slices}
    />
  );
};

// Compact "today's snapshot" card — sector-aware so workspace sites
// show occupancy / air quality, energy sites show throughput / cost, etc.
// Fills the visual gap next to the Fleet Status donut.
const TodaySnapshot = ({ site, devices }) => {
  const siteDevices = devices.filter(d => d.site === site.id);

  // Compute relevant stats from real device data
  const occupancy = siteDevices.filter(d => d.kind === 'occupancy');
  const co2 = siteDevices.filter(d => d.kind === 'sensor' && (d.metric.label === 'CO₂' || d.metric.unit === 'ppm'));
  const meters = siteDevices.filter(d => d.kind === 'meter');
  const meetings = siteDevices.filter(d => d.kind === 'meeting-room');
  const hotDesks = siteDevices.filter(d => d.kind === 'hot-desk');

  const totalPeople = Math.round(occupancy.reduce((s, d) => s + d.metric.value, 0));
  const peakPpl = occupancy.length ? Math.max(...occupancy.map(d => d.metric.value)) : 0;
  const avgCo2 = co2.length ? Math.round(co2.reduce((s, d) => s + d.metric.value, 0) / co2.length) : null;
  const totalKw = Math.round(meters.reduce((s, d) => s + d.metric.value, 0));
  const avgBooked = meetings.length ? Math.round(meetings.reduce((s, d) => s + d.metric.value, 0) / meetings.length) : null;
  const deskTotal = hotDesks.reduce((s, d) => s + d.metric.value, 0);
  const deskCap = hotDesks.reduce((s, d) => s + d.metric.max, 0);

  const isWorkspace = site.sector === 'Workspace' || site.customer === 'Form Property';

  // Pick 4 relevant stats based on sector
  let stats;
  if (isWorkspace) {
    stats = [
      { label:'People on site',  value:totalPeople,  unit:'',     hint:'across occupancy sensors' },
      { label:'Air quality',     value:avgCo2 || '—', unit:'ppm', hint:`avg CO₂ · ${avgCo2 > 1000 ? 'elevated' : 'good'}` },
      { label:'Energy use',      value:totalKw,      unit:'kW',   hint:'current draw' },
      ...(meetings.length
        ? [{ label:'Meeting rooms', value:avgBooked,  unit:'%',   hint:'avg utilisation today' }]
        : hotDesks.length
        ? [{ label:'Hot desks',     value:deskTotal,  unit:`/${deskCap}`, hint:'occupied · capacity' }]
        : [{ label:'Active sensors', value:siteDevices.length, unit:'', hint:'across the site' }]
      ),
    ];
  } else {
    stats = [
      { label:'Devices',  value:siteDevices.length, unit:'', hint:'on this site' },
      { label:'Energy',   value:totalKw || '—',     unit:'kW', hint:'current draw' },
      { label:'Avg CO₂',  value:avgCo2 || '—',      unit:'ppm', hint:avgCo2 > 1000 ? 'review ventilation' : 'within range' },
      { label:'Active',   value:siteDevices.filter(d => d.state==='ok').length, unit:'', hint:'online now' },
    ];
  }

  return (
    <Card pad={0}>
      <div style={{ padding:'18px 24px', borderBottom:'1px solid #EDEDED' }}>
        <Eyebrow>Today · live snapshot</Eyebrow>
        <div style={{ fontSize:16, color:'#0A0F1F', marginTop:4 }}>{isWorkspace ? 'Workspace activity' : 'Site activity'}</div>
      </div>
      <div style={{ display:'grid', gridTemplateColumns:'1fr 1fr' }}>
        {stats.map((s, i) => (
          <div key={i} style={{
            padding:'18px 24px',
            borderRight: i % 2 === 0 ? '1px solid #EDEDED' : 'none',
            borderBottom: i < 2 ? '1px solid #EDEDED' : 'none'
          }}>
            <Eyebrow>{s.label}</Eyebrow>
            <div style={{
              fontSize:30, fontWeight:300, letterSpacing:'-0.04em', color:'#0A0F1F',
              marginTop:8, fontVariantNumeric:'tabular-nums', lineHeight:1
            }}>
              {s.value}{s.unit && <span style={{ fontSize:13, color:'#5A5E6E', marginLeft:4, letterSpacing:0 }}>{s.unit}</span>}
            </div>
            <div style={{ fontSize:12, color:'#5A5E6E', marginTop:8 }}>{s.hint}</div>
          </div>
        ))}
      </div>
    </Card>
  );
};

// Synthetic 24h x 7d alert-volume heatmap
const AlertHeatmap = () => {
  // deterministic pseudo-random based on (row, col) so it doesn't jitter every render
  const cols = ['00','03','06','09','12','15','18','21'];
  const rows = ['Mon','Tue','Wed','Thu','Fri','Sat','Sun'];
  const matrix = rows.map((_, r) => cols.map((_, c) => {
    // peak around 09-15 weekdays
    const peakHour = c >= 3 && c <= 5 ? 1 : 0.3;
    const weekday  = r < 5 ? 1 : 0.4;
    const noise = ((r * 7 + c * 13) % 7) / 10;
    return Math.round(peakHour * weekday * (3 + noise * 5));
  }));
  return <Heatmap title="Alert volume · last 7 days" matrix={matrix} rows={rows} cols={cols} palette="warn" />;
};

const LiveEventStream = ({ events }) => {
  const isMobile = useIsMobile();
  return (
    <Card dark pad={isMobile ? 16 : 24} style={{ height:'100%' }}>
      <div style={{ display:'flex', justifyContent:'space-between', alignItems:'center', marginBottom:16 }}>
        <div>
          <div style={{ fontSize:11, letterSpacing:'0.08em', textTransform:'uppercase', color:'rgba(255,255,255,.5)' }}>Event stream</div>
          <div style={{ fontSize:16, fontWeight:400, marginTop:4, display:'flex', alignItems:'center', gap:8 }}>
            <span style={{ width:6, height:6, borderRadius:'50%', background:'#7A9CFF', boxShadow:'0 0 0 4px rgba(122,156,255,.25)' }} />
            Following
          </div>
        </div>
        <Button kind="subtle" style={{ background:'transparent', borderColor:'rgba(255,255,255,.25)', color:'#FFF' }}>Pause</Button>
      </div>
      <div style={{ fontFamily:'IBM Plex Mono, monospace', fontSize:12, lineHeight:1.7 }}>
        {events.map(([t,lvl,src,msg],i) => {
          if (isMobile) {
            return (
              <div key={i} style={{
                padding:'10px 0', borderBottom:'1px solid rgba(255,255,255,.08)',
                display:'flex', flexDirection:'column', gap:4
              }}>
                <div style={{ display:'flex', justifyContent:'space-between', alignItems:'center', fontSize:11 }}>
                  <span style={{ color: lvl==='crit'?'#C44A4A':lvl==='warn'?'#E0A13B':'#7A9CFF' }}>{lvl.toUpperCase()}</span>
                  <span style={{ color:'rgba(255,255,255,.4)' }}>{t}</span>
                </div>
                <div style={{ color:'rgba(255,255,255,.92)', fontSize:12, lineHeight:1.4 }}>{msg}</div>
                <div style={{ color:'rgba(255,255,255,.55)', fontSize:11 }}>{src}</div>
              </div>
            );
          }
          return (
            <div key={i} style={{
              display:'grid', gridTemplateColumns:'70px 50px 110px 1fr', gap:12,
              padding:'8px 0', borderBottom:'1px solid rgba(255,255,255,.08)'
            }}>
              <span style={{ color:'rgba(255,255,255,.4)' }}>{t}</span>
              <span style={{ color: lvl==='crit'?'#C44A4A':lvl==='warn'?'#E0A13B':'rgba(255,255,255,.6)' }}>{lvl.toUpperCase()}</span>
              <span style={{ color:'rgba(255,255,255,.7)' }}>{src}</span>
              <span style={{ color:'rgba(255,255,255,.92)' }}>{msg}</span>
            </div>
          );
        })}
      </div>
    </Card>
  );
};

const HealthTrend = () => {
  const series = [
    { label:'Throughput', unit:'msg/s', color:'#3A6FF8', data: MOCK.makeSeries(800, 60) },
    { label:'Errors',     unit:'/min',  color:'#C44A4A', data: MOCK.makeSeries(2, 1) },
  ];
  const [range, setRange] = React.useState('24h');
  return <TimeSeriesChart series={series} height={200} range={range} onRange={setRange} />;
};

// Connected systems panel — shows every subsystem the platform brings
// into one dashboard. The pitch: instead of 6+ separate vendor apps
// (BMS, lighting, access, HVAC, energy, fire), it's one TEO view.
const ConnectedSystems = ({ devices, site }) => {
  const isMobile = useIsMobile();
  const siteDevices = devices.filter(d => d.site === site.id);
  // Categorise devices into BMS subsystems by kind
  const SYSTEMS = [
    { key:'hvac',     label:'HVAC',     kinds:['ahu','chiller','heat-pump','vrf-outdoor','vrf-indoor'], icon:'wind',          vendor:'Carrier · Mitsubishi · Trane' },
    { key:'lighting', label:'Lighting', kinds:['lighting'],                                              icon:'lightbulb',     vendor:'Casambi' },
    { key:'access',   label:'Access',   kinds:['smart-lock'],                                            icon:'lock',          vendor:'Salto' },
    { key:'energy',   label:'Energy',   kinds:['meter','smart-meter','solar-pv'],                        icon:'plug',          vendor:'Schneider · Landis+Gyr' },
    { key:'air',      label:'Air quality',kinds:['sensor','damp-sensor'],                                icon:'leaf',          vendor:'TEO sensors' },
    { key:'space',    label:'Workspace',kinds:['occupancy','hot-desk','meeting-room','ap'],              icon:'users',         vendor:'TEO · Aruba' },
    { key:'mobility', label:'Mobility', kinds:['lift','ev-charger'],                                     icon:'arrow-up-down', vendor:'KONE · Pod Point' },
  ];
  const rows = SYSTEMS.map(s => {
    const matched = siteDevices.filter(d => s.kinds.includes(d.kind));
    const ok = matched.filter(d => d.state === 'ok' || d.state === 'idle').length;
    const total = matched.length;
    const issues = matched.filter(d => d.state === 'warn' || d.state === 'down').length;
    return { ...s, total, ok, issues, status: issues > 0 ? 'warn' : total > 0 ? 'ok' : 'idle' };
  });
  const totalDevices = siteDevices.length;
  const totalSystems = rows.filter(r => r.total > 0).length;
  return (
    <Card pad={0}>
      <div style={{
        padding: isMobile ? '14px 16px' : '18px 24px',
        borderBottom:'1px solid #EDEDED',
        display:'flex', justifyContent:'space-between', alignItems:'flex-start', gap:12, flexWrap:'wrap'
      }}>
        <div>
          <Eyebrow accent>One platform</Eyebrow>
          <div style={{ fontSize:16, color:'#0A0F1F', marginTop:4 }}>Connected systems</div>
          <div style={{ fontSize:12, color:'#5A5E6E', marginTop:4, maxWidth:380, lineHeight:1.4 }}>
            All seven subsystems on this site report into one dashboard — replacing the patchwork of vendor apps.
          </div>
        </div>
        <div style={{ textAlign:'right' }}>
          <div style={{ fontSize:24, fontWeight:300, letterSpacing:'-0.04em', color:'#0A0F1F', fontVariantNumeric:'tabular-nums' }}>{totalSystems}<span style={{ fontSize:13, color:'#5A5E6E', marginLeft:4 }}>/ 7</span></div>
          <div style={{ fontSize:11, color:'#5A5E6E', letterSpacing:'0.08em', textTransform:'uppercase' }}>integrated</div>
        </div>
      </div>
      <div style={{ display:'grid', gridTemplateColumns: isMobile ? '1fr' : 'repeat(2, 1fr)' }}>
        {rows.map((r, i) => (
          <div key={r.key} style={{
            padding:'14px 16px',
            borderRight: (i % 2 === 0 && !isMobile) ? '1px solid #EDEDED' : 'none',
            borderBottom: '1px solid #EDEDED',
            display:'grid', gridTemplateColumns:'34px 1fr auto', gap:12, alignItems:'center',
            opacity: r.total === 0 ? 0.5 : 1
          }}>
            <div style={{ width:30, height:30, background:'#F6F7F9', border:'1px solid #EDEDED', display:'flex', alignItems:'center', justifyContent:'center' }}>
              <i data-lucide={r.icon} style={{ width:14, height:14, color:'#0A0F1F' }} />
            </div>
            <div style={{ minWidth:0 }}>
              <div style={{ fontSize:14, color:'#0A0F1F' }}>{r.label}</div>
              <div style={{ fontSize:11, color:'#5A5E6E', fontFamily:'IBM Plex Mono, monospace', overflow:'hidden', textOverflow:'ellipsis', whiteSpace:'nowrap' }}>{r.vendor}</div>
            </div>
            <div style={{ textAlign:'right' }}>
              {r.total === 0
                ? <span style={{ fontSize:11, color:'#5A5E6E' }}>—</span>
                : <StatusPill state={r.status}>{r.total}</StatusPill>}
            </div>
          </div>
        ))}
      </div>
      <div style={{ padding:'12px 16px', display:'flex', justifyContent:'space-between', alignItems:'center', fontSize:11, color:'#5A5E6E', fontFamily:'IBM Plex Mono, monospace' }}>
        <span>{totalDevices} devices across {totalSystems} subsystems</span>
        <span style={{ color:'#3A6FF8' }}>→ unified</span>
      </div>
    </Card>
  );
};

const ZoneSummary = ({ devices, site, onZoneClick }) => {
  const isMobile = useIsMobile();
  const siteDevices = devices.filter(d => d.site === site.id);
  const zones = {};
  siteDevices.forEach(d => {
    if (!zones[d.zone]) zones[d.zone] = { total:0, ok:0, warn:0, down:0, idle:0 };
    zones[d.zone].total++;
    zones[d.zone][d.state]++;
  });
  return (
    <Card pad={0}>
      <div style={{
        padding: isMobile ? '14px 16px' : '18px 24px',
        borderBottom:'1px solid #EDEDED',
        display:'flex', justifyContent:'space-between', alignItems:'center', gap:12, flexWrap:'wrap'
      }}>
        <div>
          <Eyebrow>Site rollup</Eyebrow>
          <div style={{ fontSize:16, color:'#0A0F1F', marginTop:4 }}>{site.name}</div>
        </div>
        <Button kind="subtle" onClick={() => onZoneClick && onZoneClick()}>Open twin</Button>
      </div>
      {Object.entries(zones).map(([zone, s]) => (
        <div key={zone} style={{
          padding: isMobile ? '14px 16px' : '16px 24px',
          borderBottom:'1px solid #EDEDED'
        }}>
          <div style={{ display:'flex', justifyContent:'space-between', alignItems:'center', gap:12 }}>
            <div style={{ minWidth:0, flex:1 }}>
              <div style={{ fontSize:14, color:'#0A0F1F' }}>{zone}</div>
              <div style={{ fontSize:12, color:'#5A5E6E', marginTop:2 }}>{s.total} devices</div>
            </div>
            <div style={{ display:'flex', gap:12, fontSize:12, fontVariantNumeric:'tabular-nums', fontFamily:'IBM Plex Mono, monospace' }}>
              {s.ok>0   && <span style={{ display:'inline-flex', alignItems:'center', gap:4 }}><StatusDot state="ok" />{s.ok}</span>}
              {s.warn>0 && <span style={{ display:'inline-flex', alignItems:'center', gap:4 }}><StatusDot state="warn" />{s.warn}</span>}
              {s.down>0 && <span style={{ display:'inline-flex', alignItems:'center', gap:4 }}><StatusDot state="down" />{s.down}</span>}
              {s.idle>0 && <span style={{ display:'inline-flex', alignItems:'center', gap:4 }}><StatusDot state="idle" />{s.idle}</span>}
            </div>
          </div>
        </div>
      ))}
    </Card>
  );
};

const Overview = ({ site, devices, alerts, events, onNav, sites, onSite }) => {
  const isMobile = useIsMobile();
  const [scope, setScope] = React.useState('This building');

  // If on a customer with multiple sites, expose a scope toggle for aggregation.
  const groupSites = site.customer
    ? sites.filter(s => s.customer === site.customer)
    : [];
  const showAggregateToggle = groupSites.length > 1;
  const scopedSiteIds = (showAggregateToggle && scope === 'All buildings')
    ? new Set(groupSites.map(s => s.id))
    : new Set([site.id]);
  const scopedDevices = devices.filter(d => scopedSiteIds.has(d.site));
  const scopedAlerts  = alerts.filter(a => scopedSiteIds.has(a.site));
  const scopedEvents  = events; // events feed not site-scoped

  return (
    <div>
      {isMobile && <MobileSiteHero site={site} devices={devices} alerts={alerts} />}
      {showAggregateToggle && (
        <div style={{
          padding: isMobile ? '12px 16px' : '14px 32px',
          background:'#FFF', borderBottom:'1px solid #EDEDED',
          display:'flex', justifyContent:'space-between', alignItems:'center', gap:12, flexWrap:'wrap'
        }}>
          <div style={{ display:'flex', alignItems:'center', gap:10, minWidth:0 }}>
            <i data-lucide="layers" style={{ width:14, height:14, color:'#3A6FF8' }} />
            <span style={{ fontSize:12, color:'#5A5E6E', letterSpacing:'0.04em' }}>
              <strong style={{ color:'#0A0F1F', fontWeight:500 }}>{site.customer}</strong> · {groupSites.length} buildings
            </span>
          </div>
          <SegmentedControl
            value={scope}
            onChange={setScope}
            options={['This building','All buildings']}
          />
        </div>
      )}
      <KPIStrip devices={scopedDevices} alerts={scopedAlerts} />
      <div data-collapse-md style={{ display:'grid', gridTemplateColumns:'2fr 1fr', borderBottom:'1px solid #EDEDED' }}>
        <HealthTrend />
        <SectorBreakdown devices={scopedDevices} />
      </div>
      <div data-pad-md style={{ padding:32, background:'#F6F7F9', display:'flex', flexDirection:'column', gap:24 }}>
        {/* Portfolio map — full-width — always visible above the rest */}
        {sites && sites.length > 1 && (
          <PortfolioMap
            sites={sites}
            currentSite={site}
            onSiteChange={(s) => { onSite && onSite(s); }}
            devices={devices}
            alerts={alerts}
          />
        )}
        {/* Row 1: donut (1/3) | heatmap (2/3) */}
        <div data-collapse-md style={{ display:'grid', gridTemplateColumns:'1fr 2fr', gap:24, alignItems:'stretch' }}>
          <FleetStatusDonut devices={scopedDevices} />
          <AlertHeatmap />
        </div>
        {/* Row 2: snapshot (1/2) | connected systems (1/2) */}
        <div data-collapse-md style={{ display:'grid', gridTemplateColumns:'1fr 1fr', gap:24, alignItems:'start' }}>
          <TodaySnapshot site={site} devices={scopedDevices} />
          <ConnectedSystems site={site} devices={scopedDevices} />
        </div>
        {/* Row 3: zone summary full-width */}
        <ZoneSummary site={site} devices={devices} onZoneClick={() => onNav('twin')} />
        {/* Row 4: live event stream full-width */}
        <LiveEventStream events={scopedEvents} />
      </div>
    </div>
  );
};

Object.assign(window, { Overview });
