// Real metrics for the Home page sparklines.
//
// Buckets the last N hours into N points, counting rows in each window.
// Returns a flat shape that's easy for the React Sparkline to consume.

import express from 'express';
import pool from '../db/pool.js';

const router = express.Router();

const DEFAULT_HOURS = 24;
const DEFAULT_POINTS = 13;

function bucketCountSQL(table, statusFilter) {
  // Use date_trunc + generate_series so we always return `points` buckets
  // (even hours with no rows show up as 0). All times are UTC.
  return `
    WITH series AS (
      SELECT generate_series(
        date_trunc('hour', NOW() - ($1 || ' hours')::interval),
        date_trunc('hour', NOW()),
        ('1 hour')::interval
      ) AS bucket
    )
    SELECT s.bucket,
           COALESCE(COUNT(t.created_at), 0)::int AS count
    FROM series s
    LEFT JOIN ${table} t
      ON date_trunc('hour', t.created_at) = s.bucket
      ${statusFilter ? ` AND ${statusFilter}` : ''}
    GROUP BY s.bucket
    ORDER BY s.bucket ASC
  `;
}

async function bucketSeries(table, hours, statusFilter = null) {
  const result = await pool.query(bucketCountSQL(table, statusFilter), [hours]);
  return result.rows.map(r => ({ t: r.bucket, v: r.count }));
}

router.get('/home', async (req, res, next) => {
  try {
    const hours = Math.min(parseInt(req.query.hours || DEFAULT_HOURS, 10), 168); // cap at 1 week

    const [assets, jobsDone, jobsFailed, recordersTotal, recordersLive, jobsRunning, jobsDoneTotal, jobsFailedTotal] = await Promise.all([
      bucketSeries('assets',            hours),
      bucketSeries('jobs',              hours, `t.status = 'complete'`),
      bucketSeries('jobs',              hours, `t.status = 'failed'`),
      pool.query(`SELECT COUNT(*)::int AS n FROM recorders`),
      pool.query(`SELECT COUNT(*)::int AS n FROM recorders WHERE status = 'recording'`),
      pool.query(`SELECT COUNT(*)::int AS n FROM jobs WHERE status IN ('queued','processing')`),
      pool.query(`SELECT COUNT(*)::int AS n FROM jobs WHERE status = 'complete'`),
      pool.query(`SELECT COUNT(*)::int AS n FROM jobs WHERE status = 'failed'`),
    ]);

    // Cluster snapshot — heartbeat freshness drives online/offline
    const cluster = await pool.query(
      `SELECT id, hostname, role,
              EXTRACT(EPOCH FROM (NOW() - last_seen))::int AS stale_seconds
       FROM cluster_nodes`
    );
    const nodes = cluster.rows.map(n => ({
      id: n.id, hostname: n.hostname, role: n.role,
      online: n.stale_seconds != null && n.stale_seconds < 120,
    }));

    res.json({
      hours,
      generated_at: new Date().toISOString(),
      cards: {
        assets: {
          total:  (await pool.query(`SELECT COUNT(*)::int AS n FROM assets`)).rows[0].n,
          series: assets,
        },
        recorders: {
          total:  recordersTotal.rows[0].n,
          live:   recordersLive.rows[0].n,
          // No historical "active" metric yet — synthesize as the live count
          // replayed across the window so the card has *something* to graph.
          series: assets.map(p => ({ t: p.t, v: recordersLive.rows[0].n })),
        },
        jobs: {
          running:    jobsRunning.rows[0].n,
          done_total: jobsDoneTotal.rows[0].n,
          failed_total: jobsFailedTotal.rows[0].n,
          series_done:   jobsDone,
          series_failed: jobsFailed,
        },
        cluster: {
          total:  nodes.length,
          online: nodes.filter(n => n.online).length,
          nodes,
          // Heartbeat liveness is binary — emit a 1/0 across the window keyed
          // to current state so the sparkline shows a sensible bar shape.
          series: assets.map(p => ({ t: p.t, v: nodes.filter(n => n.online).length })),
        },
      },
    });
  } catch (err) { next(err); }
});

export default router;
