/* Overview - the at-a-glance page. One hero figure, supporting tiles, trends. */

import { store, get, num, duration, batteryTone, tempTone } from '../core.js';
import { el, card, stat, meter, note, pageHead, button, kv } from '../ui.js';
import { timeSeries, sparkline, attitude, compass } from '../charts.js';

export default {
  id: 'overview',
  label: 'Overview',
  icon: 'overview',

  async render() {
    const root = el('div.stack');

    /* -- Hero ------------------------------------------------------------- */

    const heroNumber = el('span', { text: '—' });
    const heroValue = el('div.hero', {}, heroNumber, el('span.unit', { text: '%' }));
    const meterHost = el('div');
    const heroSub = el('div.stat-sub', { text: 'Battery remaining' });
    const heroSpark = el('div', { style: { marginTop: '8px', height: '40px' } });

    /* -- Tiles ------------------------------------------------------------ */

    const tiles = el('div.grid.cols-3');
    const tileRefs = {};
    for (const [key, label] of [
      ['mode', 'Motion mode'],
      ['transport', 'Link'],
      ['uptime', 'Bridge uptime'],
      ['speed', 'Ground speed'],
      ['heading', 'Heading'],
      ['temp', 'Battery temp'],
    ]) {
      const node = stat(label, '—', { sub: ' ' });
      tileRefs[key] = node;
      tiles.appendChild(node);
    }

    function setTile(key, value, sub, tone = 'default', unit = '') {
      const node = tileRefs[key];
      if (!node) return;
      node.dataset.tone = tone;
      const valueNode = node.querySelector('.stat-value');
      valueNode.textContent = String(value);
      if (unit) valueNode.appendChild(el('span.unit', { text: unit }));
      node.querySelector('.stat-sub').textContent = sub || '';
    }

    /* -- Attitude --------------------------------------------------------- */

    const attitudeHost = el('div.attitude');
    const compassHost = el('div.attitude');
    const attitudeReadout = el('div');

    /* -- Charts ----------------------------------------------------------- */

    const velChart = timeSeries([
      { key: 'vel_forward', label: 'Forward', points: [], colorIndex: 0, unit: ' m/s' },
      { key: 'vel_lateral', label: 'Lateral', points: [], colorIndex: 1, unit: ' m/s' },
      { key: 'vel_angular', label: 'Yaw rate', points: [], colorIndex: 2, unit: ' rad/s' },
    ], { height: 176, precision: 2, zeroLine: true });

    const batteryChart = timeSeries(
      [{ key: 'battery_pct', label: 'Battery', points: [], colorIndex: 0, unit: '%' }],
      { height: 168, precision: 1 },
    );

    const tempChart = timeSeries([
      { key: 'battery_temp', label: 'Battery', points: [], colorIndex: 1, unit: ' °C' },
      { key: 'pmu_temp', label: 'PMU', points: [], colorIndex: 3, unit: ' °C' },
    ], { height: 168, precision: 1 });

    /* -- Assemble --------------------------------------------------------- */

    root.appendChild(pageHead(
      'Overview',
      'Live condition of the robot. Everything here is read-only — use Control to act.',
      [button('Refresh trends', () => refreshCharts(), { iconName: 'refresh', size: 'sm', style: 'ghost' })],
    ));

    root.appendChild(el('div.grid.split', {},
      card('Battery', { sub: 'PMU · 0.2 Hz' },
        el('div.stack', { style: { gap: '10px' } }, heroValue, meterHost, heroSub, heroSpark),
      ),
      tiles,
    ));

    root.appendChild(el('div.grid.cols-2', {},
      card('Attitude', { sub: 'Chest IMU' },
        el('div.row', { style: { justifyContent: 'space-around', alignItems: 'center', gap: '16px' } },
          attitudeHost, compassHost,
        ),
        el('div', { style: { marginTop: '14px' } }, attitudeReadout),
      ),
      card('Velocity', { sub: 'Measured, last 2 minutes' }, velChart),
    ));

    root.appendChild(el('div.grid.cols-2', {},
      card('Battery trend', { sub: 'Last 2 minutes' }, batteryChart),
      card('Thermals', { sub: 'Battery cell and PMU' }, tempChart),
    ));

    const safety = (store.spec?.safety_notes || []).filter((n) => n.level !== 'info');
    if (safety.length) {
      root.appendChild(card('Before you operate', {},
        el('div.stack', { style: { gap: '8px' } },
          ...safety.map((n) => note(n.text, n.level, n.level === 'critical' ? '⚠' : 'ⓘ')),
        ),
      ));
    }

    /* -- Painting --------------------------------------------------------- */

    let meterNode = meter(0);
    meterHost.appendChild(meterNode);

    function paint() {
      const s = store.state;
      if (!s) return;

      const pct = s.battery_pct;
      const tone = batteryTone(pct);

      heroNumber.textContent = pct === null || pct === undefined ? '—' : num(pct, 1);
      heroValue.style.color = tone === 'critical' ? 'var(--critical)'
        : tone === 'warning' ? 'var(--warning)' : 'var(--text)';

      const nextMeter = meter((pct || 0) / 100, tone);
      meterNode.replaceWith(nextMeter);
      meterNode = nextMeter;

      const { battery_voltage: voltage, battery_current: current } = s;
      heroSub.textContent = [
        voltage != null ? `${num(voltage, 1)} V` : null,
        current != null ? `${num(Math.abs(current), 1)} A ${current > 0 ? 'charging' : 'draw'}` : null,
        s.battery_cycles ? `${s.battery_cycles} cycles` : null,
      ].filter(Boolean).join(' · ') || 'Battery remaining';

      const modeSpec = store.spec?.modes?.find((m) => m.id === s.mode);
      setTile('mode', modeSpec?.label || s.mode || '—', modeSpec?.desc || '',
              s.mode === 'PASSIVE_DEFAULT' ? 'critical' : 'default');

      const online = s.connection?.online;
      const agent = s.custom?.agent;
      setTile('transport',
              s.connection?.simulated ? 'Simulated' : online ? 'Live' : 'Robot off',
              s.connection?.simulated
                ? 'No robot attached'
                : online
                  ? `${agent?.hostname || 'agent'} · ROS domain ${s.connection?.ros_domain_id ?? 0}`
                  : 'Agent unreachable',
              online ? (s.connection.simulated ? 'warning' : 'good') : 'critical');

      setTile('uptime', duration(s.connection?.uptime_s), s.connection?.host || '');

      const speed = Math.hypot(s.velocity?.forward || 0, s.velocity?.lateral || 0);
      setTile('speed', num(speed, 2), 'linear, from odometry', 'default', ' m/s');

      const yawDeg = ((s.odom?.yaw || 0) * 180 / Math.PI + 360) % 360;
      setTile('heading', num(yawDeg, 0),
              `x ${num(s.odom?.x, 2)} m · y ${num(s.odom?.y, 2)} m`, 'default', '°');

      setTile('temp', num(s.battery_temp, 1), 'cell temperature',
              tempTone(s.battery_temp, 45, 55), ' °C');

      const chest = s.imu?.chest || {};
      attitudeHost.replaceChildren(attitude(chest.roll || 0, chest.pitch || 0, { size: 148 }));
      compassHost.replaceChildren(compass(s.odom?.yaw || 0, { size: 148 }));

      attitudeReadout.replaceChildren(kv([
        ['Roll', `${num((chest.roll || 0) * 180 / Math.PI, 2)}°`],
        ['Pitch', `${num((chest.pitch || 0) * 180 / Math.PI, 2)}°`],
        ['Yaw', `${num((chest.yaw || 0) * 180 / Math.PI, 2)}°`],
        ['Vertical accel', `${num(chest.accel_z, 2)} m/s²`],
      ]));
    }

    async function refreshCharts() {
      try {
        const keys = 'battery_pct,battery_temp,pmu_temp,vel_forward,vel_lateral,vel_angular';
        const data = await get(`/api/series?keys=${keys}&limit=400`);

        batteryChart.update([
          { key: 'battery_pct', label: 'Battery', points: data.battery_pct || [], colorIndex: 0, unit: '%' },
        ]);
        tempChart.update([
          { key: 'battery_temp', label: 'Battery', points: data.battery_temp || [], colorIndex: 1, unit: ' °C' },
          { key: 'pmu_temp', label: 'PMU', points: data.pmu_temp || [], colorIndex: 3, unit: ' °C' },
        ]);
        velChart.update([
          { key: 'vel_forward', label: 'Forward', points: data.vel_forward || [], colorIndex: 0, unit: ' m/s' },
          { key: 'vel_lateral', label: 'Lateral', points: data.vel_lateral || [], colorIndex: 1, unit: ' m/s' },
          { key: 'vel_angular', label: 'Yaw rate', points: data.vel_angular || [], colorIndex: 2, unit: ' rad/s' },
        ]);

        const history = data.battery_pct || [];
        heroSpark.replaceChildren(
          history.length > 2 ? sparkline(history, { height: 40, colorIndex: 0 }) : el('span'),
        );
      } catch {
        // A failed trend refresh is not worth interrupting the operator over -
        // the live tiles above are still updating from the WebSocket.
      }
    }

    paint();
    refreshCharts();

    // Twice a second reads as live without repainting SVG on every frame.
    let lastPaint = 0;
    const unsubscribe = store.on('state', () => {
      const now = Date.now();
      if (now - lastPaint < 500) return;
      lastPaint = now;
      paint();
    });
    const timer = setInterval(refreshCharts, 3000);

    return { node: root, dispose: () => { unsubscribe(); clearInterval(timer); } };
  },
};
