// BHEEM Sense — Live monitoring screen
//
// Two equal rings: temperature and humidity. The HACCP original had a probe
// ring and demoted humidity to a small tile; on a DHT11-only unit humidity is
// half the product, so both channels get the same weight.
const { useState, useEffect } = React;

function LiveScreen({ live, device, history, activeAlerts, broker, devices, selectedDevice, profiles }) {
  const [now, setNow] = useState(Date.now());
  useEffect(() => { const t = setInterval(() => setNow(Date.now()), 1000); return () => clearInterval(t); }, []);

  const alerts      = Object.values(activeAlerts);
  const breachCount = alerts.length;
  const stale       = live.lastReadingTs && now - live.lastReadingTs > 30000;

  const status = !live.online   ? 'unknown'
               : breachCount > 0 ? 'breach'
               : stale           ? 'stale'
               : 'safe';

  const sensors = live.sensors || {};
  const ambientOk = sensors.ambient === 'ok' || sensors.ambient == null;

  // Per-channel status, so a humidity breach colours only the humidity ring.
  const channelBreached = ch => alerts.some(a => a.channel === ch);
  const channelStatus   = ch => !live.online || !ambientOk ? 'unknown'
                              : (ch === 'temp' ? sensors.temp_stuck : sensors.rh_stuck) ? 'unknown'
                              : channelBreached(ch)        ? 'breach'
                              : stale                      ? 'stale'
                              : 'safe';

  const sensorColor = s => s === 'fault' ? 'oklch(0.55 0.2 25)' : 'oklch(0.6 0.02 260)';
  const sensorLabel = s => s === 'fault' ? 'Sensor fault' : s === 'absent' ? 'No sensor' : null;

  // A stuck channel keeps arriving as valid data but has not moved in 30 min,
  // which means the sensing element has failed. It must not read as a healthy
  // number: the backend has already stopped judging it against thresholds.
  const stuck = { temp: !!sensors.temp_stuck, rh: !!sensors.rh_stuck };
  const StuckChip = () => (
    <span style={{
      fontSize: 11, fontWeight: 600, padding: '3px 10px', borderRadius: 100,
      color: 'oklch(0.6 0.17 60)', background: 'oklch(0.6 0.17 60 / 0.15)',
    }}>
      Not responding
    </span>
  );

  // Threshold band for the active profile, shown under each ring so the
  // number on screen can be read against the limit it is judged by.
  const limits = profiles && live.profile && profiles.profiles
    ? profiles.profiles[live.profile] : null;
  const bandText = (ch, unit) => {
    const l = limits && limits[ch];
    if (!l) return null;
    const lo = l.min != null ? `${l.min}${unit}` : null;
    const hi = l.max != null ? `${l.max}${unit}` : null;
    if (lo && hi) return `${lo} – ${hi}`;
    if (hi)       return `max ${hi}`;
    if (lo)       return `min ${lo}`;
    return null;
  };

  // The device stamps every reading from its DS3231 in UTC. Readings arrive
  // every 5 s, so advance the stamp by the time elapsed since it landed —
  // otherwise the clock lurches in 5-second steps instead of ticking.
  //
  // Drift is the gap between the device's clock and this browser's at the moment
  // the reading arrived. It is the whole point of carrying an RTC: if the board
  // loses power or NTP, this number is what tells you the timestamps on the
  // audit trail can still be trusted.
  const rtc = (() => {
    if (live.rtcEpoch == null || live.rtcEpochAt == null) {
      return { time: '--', sub: device.rtcPresent === false ? 'no hardware clock' : 'waiting for reading' };
    }
    const deviceMs = live.rtcEpoch * 1000 + (now - live.rtcEpochAt);
    const driftS   = Math.round((live.rtcEpoch * 1000 - live.rtcEpochAt) / 1000);
    const d = new Date(deviceMs);
    const time = d.toLocaleTimeString('en-GB', { hour12: false });
    const date = d.toLocaleDateString('en-GB', { day: '2-digit', month: 'short' });
    const sub = Math.abs(driftS) <= 2
      ? `DS3231 · ${date} · in sync`
      : `DS3231 · ${date} · ${driftS > 0 ? '+' : ''}${driftS}s vs browser`;
    return { time, sub };
  })();

  const deviceList = Object.keys(devices || {}).sort();

  return (
    <div>
      {/* Header */}
      <div className="page-header">
        <div>
          <div className="page-eyebrow">Live Monitoring</div>
          <h1 className="page-title">{selectedDevice || 'Waiting for device…'}</h1>
          <div className="page-subtitle">
            {live.online
              ? `Last reading ${formatAge(live.lastReadingTs)} · ${device.msgCount} msgs`
              : broker === 'connected' ? 'Waiting for readings…' : `Broker: ${broker}`}
          </div>
        </div>
        <div className="row gap-8 items-center">
          {live.profile && <ProfileBadge profile={live.profile} />}
          <StatusBadge status={status} />
          {deviceList.length > 1 && (
            <select
              className="btn btn-sm"
              value={selectedDevice || ''}
              onChange={e => window.BHEEM_STORE.setSelectedDevice(e.target.value)}
              style={{ fontFamily: 'inherit' }}>
              {deviceList.map(id => <option key={id} value={id}>{id}</option>)}
            </select>
          )}
        </div>
      </div>

      {/* Active breaches banner */}
      {breachCount > 0 && (
        <div className="card" style={{ marginBottom: 24, borderColor: 'var(--status-breach)', background: 'oklch(0.55 0.2 25 / 0.05)' }}>
          <div className="card-title" style={{ color: 'var(--status-breach)', marginBottom: 12 }}>
            Active Breach{breachCount > 1 ? 'es' : ''} — {breachCount}
          </div>
          {alerts.map(a => (
            <div key={a.alert_type} className="alert-item">
              <span className="alert-dot" />
              <span className="alert-type">{a.alert_type}</span>
              <span className="alert-temp">{formatValue(a.value, a.unit)}</span>
              <span className="alert-threshold">limit {formatValue(a.threshold, a.unit)}</span>
              <span className="alert-since">{new Date(a.since).toLocaleTimeString('en-GB', { hour12: false })}</span>
            </div>
          ))}
        </div>
      )}

      {/* Temperature + humidity rings */}
      <div className="card" style={{ marginBottom: 16, padding: 40 }}>
        <div style={{ display: 'flex', justifyContent: 'space-around', alignItems: 'flex-start', flexWrap: 'wrap', gap: 32 }}>
          {/* Temperature */}
          <div className="col items-center gap-8">
            <TempRing
              tempC={live.ambientTempC}
              status={channelStatus('temp')}
              size={180}
              label="Temperature"
            />
            {stuck.temp ? <StuckChip /> : bandText('temp', '°C') && (
              <span className="text-xs text-ink-3">Range {bandText('temp', '°C')}</span>
            )}
            {history.ambientTempC.length >= 2 && (
              <div style={{ width: 180 }}>
                <Sparkline data={history.ambientTempC} height={36} color="var(--ccp-ambient)" fill />
              </div>
            )}
          </div>

          {/* Humidity */}
          <div className="col items-center gap-8">
            <TempRing
              tempC={live.ambientRh}
              status={channelStatus('rh')}
              size={180}
              label="Humidity"
              unit="%"
            />
            {stuck.rh ? <StuckChip /> : bandText('rh', '%') && (
              <span className="text-xs text-ink-3">Range {bandText('rh', '%')}</span>
            )}
            {history.ambientRh.length >= 2 && (
              <div style={{ width: 180 }}>
                <Sparkline data={history.ambientRh} height={36} color="var(--ccp-cold)" fill />
              </div>
            )}
          </div>
        </div>

        {/* One sensor, one health label — shown once rather than per ring */}
        {sensorLabel(sensors.ambient) && (
          <div className="row" style={{ justifyContent: 'center', marginTop: 20 }}>
            <span style={{ fontSize: 12, fontWeight: 500, color: sensorColor(sensors.ambient),
              background: sensorColor(sensors.ambient) + '18', padding: '4px 12px', borderRadius: 100 }}>
              {device.sensorModel || 'Sensor'} — {sensorLabel(sensors.ambient)}
            </span>
          </div>
        )}
      </div>

      {/* Detail metrics */}
      <div className="grid-4" style={{ marginBottom: 16 }}>
        {/* Power — this board is USB-fed with no divider, so report supply, not charge */}
        <div className="card-flat">
          <div className="card-title" style={{ marginBottom: 12 }}>Power</div>
          <div className="row items-baseline gap-4">
            <span className="num" style={{ fontSize: 28, fontWeight: 600 }}>
              {live.batteryMv != null ? (live.batteryMv / 1000).toFixed(2) : '--'}
            </span>
            {live.batteryMv != null && <span className="text-sm text-ink-3">V</span>}
          </div>
          <div className="text-xs text-ink-3" style={{ marginTop: 4 }}>USB supply</div>
        </div>

        {/* Signal */}
        <div className="card-flat">
          <div className="card-title" style={{ marginBottom: 12 }}>Signal</div>
          <div className="row items-baseline gap-4">
            <span className="num" style={{ fontSize: 28, fontWeight: 600 }}>
              {live.rssi != null ? live.rssi : '--'}
            </span>
            {live.rssi != null && <span className="text-sm text-ink-3">dBm</span>}
          </div>
          {device.ssid && (
            <div className="text-xs text-ink-3" style={{ marginTop: 4 }}>{device.ssid}</div>
          )}
        </div>

        {/* Clock — the DS3231's own time, not the browser's */}
        <div className="card-flat">
          <div className="card-title" style={{ marginBottom: 12 }}>Clock</div>
          <div className="num mono" style={{ fontSize: 26, fontWeight: 600, letterSpacing: '-0.02em' }}>
            {rtc.time}
          </div>
          <div className="text-xs text-ink-3" style={{ marginTop: 4 }}>{rtc.sub}</div>
        </div>
      </div>

      {/* Device strip */}
      {live.uptimeMs != null && (
        <div className="card-flat" style={{ marginBottom: 0 }}>
          <div className="row gap-32 items-center flex-wrap">
            <div className="col gap-4">
              <div className="strip-label">Uptime</div>
              <div className="mono text-sm">{formatUptime(Math.floor(live.uptimeMs / 1000))}</div>
            </div>
            {device.fwVersion && (
              <div className="col gap-4">
                <div className="strip-label">Firmware</div>
                <div className="mono text-sm">{device.fwVersion}</div>
              </div>
            )}
            {device.sensorModel && (
              <div className="col gap-4">
                <div className="strip-label">Sensor</div>
                <div className="mono text-sm">{device.sensorModel}</div>
              </div>
            )}
            {device.ip && (
              <div className="col gap-4">
                <div className="strip-label">IP</div>
                <div className="mono text-sm">{device.ip}</div>
              </div>
            )}
            <div className="col gap-4" style={{ marginLeft: 'auto' }}>
              <div className="strip-label">Last reading</div>
              <div className="mono text-sm">{live.lastReadingTs ? new Date(live.lastReadingTs).toLocaleTimeString('en-GB', { hour12: false }) : '--'}</div>
            </div>
          </div>
        </div>
      )}
    </div>
  );
}
