package store

import (
	"context"
	"fmt"
	"time"
)

// FleetSummary is the aggregated view that powers the dashboard's
// summary tiles. All numbers are point-in-time snapshots; the
// caller polls.
type FleetSummary struct {
	TotalHosts    int
	HostsOnline   int
	HostsDegraded int
	HostsOffline  int

	RepoBytesTotal int64
	SnapshotsTotal int
	OpenAlerts     int

	// Last-24h job rollup. JobsTotal includes every status; the
	// breakdown sums to it (modulo any in-flight queued/running).
	JobsLast24h          int
	JobsLast24hSucceeded int
	JobsLast24hFailed    int
	JobsLast24hCancelled int
}

// FleetSummary aggregates host + job stats in two queries. Cheap on
// SQLite at Phase 1 scale (12 hosts, a few hundred jobs/day) — no
// caching layer; the tile is regenerated on every dashboard render.
func (s *Store) FleetSummary(ctx context.Context) (FleetSummary, error) {
	var fs FleetSummary

	row := s.db.QueryRowContext(ctx, `
		SELECT
		  COUNT(*),
		  COALESCE(SUM(CASE WHEN status = 'online'   THEN 1 ELSE 0 END), 0),
		  COALESCE(SUM(CASE WHEN status = 'degraded' THEN 1 ELSE 0 END), 0),
		  COALESCE(SUM(CASE WHEN status = 'offline'  THEN 1 ELSE 0 END), 0),
		  COALESCE(SUM(repo_size_bytes), 0),
		  COALESCE(SUM(snapshot_count), 0),
		  COALESCE(SUM(open_alert_count), 0)
		FROM hosts`)
	if err := row.Scan(
		&fs.TotalHosts, &fs.HostsOnline, &fs.HostsDegraded, &fs.HostsOffline,
		&fs.RepoBytesTotal, &fs.SnapshotsTotal, &fs.OpenAlerts,
	); err != nil {
		return FleetSummary{}, fmt.Errorf("store: fleet summary hosts: %w", err)
	}

	cutoff := time.Now().Add(-24 * time.Hour).UTC().Format(time.RFC3339Nano)
	rows, err := s.db.QueryContext(ctx,
		`SELECT status, COUNT(*) FROM jobs WHERE created_at > ? GROUP BY status`,
		cutoff)
	if err != nil {
		return FleetSummary{}, fmt.Errorf("store: fleet summary jobs: %w", err)
	}
	defer func() { _ = rows.Close() }()
	for rows.Next() {
		var status string
		var n int
		if err := rows.Scan(&status, &n); err != nil {
			return FleetSummary{}, fmt.Errorf("store: fleet summary scan: %w", err)
		}
		fs.JobsLast24h += n
		switch status {
		case "succeeded":
			fs.JobsLast24hSucceeded = n
		case "failed":
			fs.JobsLast24hFailed = n
		case "cancelled": //nolint:misspell // matches the DB CHECK constraint and api.JobCancelled wire value
			fs.JobsLast24hCancelled = n
		}
	}
	return fs, rows.Err()
}
