<?php

namespace App\Services;

use App\Models\MetricSample;
use App\Models\Server;

/**
 * Turns persisted per-minute metric samples into a downsampled cpu/mem/disk (% average) + load
 * series for the Server-Details history chart. Bucketing is done in PHP (DB-agnostic, like the
 * WireGuard traffic series) — each returned point is the AVERAGE of the samples in a bucket, and
 * ONLY non-empty buckets are returned (the chart connects consecutive points and the client breaks
 * the line only on a real time gap, so normal sampling renders a continuous curve, not dropouts).
 */
class MetricHistory
{
    /**
     * @return array{window:int, from:int, now:int, bucket:int,
     *               points:array<int,array{t:int,cpu:int,mem:int,disk:int,load:float}>}
     */
    public function series(Server $server, int $windowSeconds): array
    {
        $windowSeconds = max(300, $windowSeconds);
        // Bucket width is at least the 1/min sample interval, so a bucket holds ~one sample and the
        // line stays continuous instead of zig-zagging between half-empty buckets.
        $width = max(60, (int) floor($windowSeconds / 120));
        $buckets = (int) ceil($windowSeconds / $width);
        $now = time();
        $from = $now - $windowSeconds;

        $rows = MetricSample::query()
            ->where('server_id', $server->id)
            ->where('sampled_at', '>=', now()->subSeconds($windowSeconds))
            ->orderBy('sampled_at')
            ->get(['cpu', 'mem', 'disk', 'load', 'sampled_at']);

        $sum = [];
        foreach ($rows as $r) {
            $idx = (int) floor(($r->sampled_at->getTimestamp() - $from) / $width);
            if ($idx < 0) {
                continue;
            }
            if ($idx >= $buckets) {
                $idx = $buckets - 1;
            }
            $sum[$idx] ??= ['cpu' => 0, 'mem' => 0, 'disk' => 0, 'load' => 0.0, 'n' => 0];
            $sum[$idx]['cpu'] += (int) $r->cpu;
            $sum[$idx]['mem'] += (int) $r->mem;
            $sum[$idx]['disk'] += (int) $r->disk;
            $sum[$idx]['load'] += (float) $r->load;
            $sum[$idx]['n']++;
        }
        ksort($sum);

        $points = [];
        foreach ($sum as $i => $b) {
            $n = $b['n'];
            $points[] = [
                't' => (int) round($from + ($i + 0.5) * $width),
                'cpu' => (int) round($b['cpu'] / $n),
                'mem' => (int) round($b['mem'] / $n),
                'disk' => (int) round($b['disk'] / $n),
                'load' => round($b['load'] / $n, 2),
            ];
        }

        return ['window' => $windowSeconds, 'from' => $from, 'now' => $now, 'bucket' => $width, 'points' => $points];
    }
}
