<?php

namespace Tests\Feature;

use App\Models\MetricSample;
use App\Models\Server;
use App\Models\User;
use App\Services\MetricHistory;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Support\Facades\Cache;
use Tests\TestCase;

class MetricHistoryTest extends TestCase
{
    use RefreshDatabase;

    private function server(): Server
    {
        return Server::create(['name' => 'box', 'ip' => '10.0.0.9', 'ssh_port' => 22, 'status' => 'online']);
    }

    public function test_sample_metrics_persists_a_row_from_the_cached_reading(): void
    {
        $s = $this->server();
        Cache::put("metrics:latest:{$s->id}", ['cpu' => 40, 'mem' => 55, 'disk' => 12, 'load' => 0.7], now()->addMinutes(5));

        $this->artisan('clusev:sample-metrics')->assertSuccessful();

        $this->assertDatabaseHas('metric_samples', ['server_id' => $s->id, 'cpu' => 40, 'mem' => 55, 'disk' => 12]);
        $this->assertSame(1, MetricSample::count());
    }

    public function test_sample_metrics_clamps_percentages_and_skips_unpolled_servers(): void
    {
        $polled = $this->server();
        $this->server(); // a second server with no cached reading → no sample
        Cache::put("metrics:latest:{$polled->id}", ['cpu' => 250, 'mem' => 60, 'disk' => 10, 'load' => 0.2], now()->addMinutes(5));

        $this->artisan('clusev:sample-metrics')->assertSuccessful();

        $this->assertSame(1, MetricSample::count());
        $this->assertSame(100, MetricSample::first()->cpu); // 250 clamped to 100
    }

    public function test_sample_metrics_prunes_beyond_the_retention_window(): void
    {
        $s = $this->server();
        MetricSample::create(['server_id' => $s->id, 'cpu' => 1, 'mem' => 1, 'disk' => 1, 'load' => 0, 'sampled_at' => now()->subDays(40)]);
        MetricSample::create(['server_id' => $s->id, 'cpu' => 2, 'mem' => 2, 'disk' => 2, 'load' => 0, 'sampled_at' => now()->subDays(5)]);

        $this->artisan('clusev:sample-metrics', ['--retention' => 30])->assertSuccessful();

        $this->assertDatabaseMissing('metric_samples', ['cpu' => 1]); // 40d old → pruned
        $this->assertDatabaseHas('metric_samples', ['cpu' => 2]);     // 5d old → kept
    }

    public function test_series_averages_samples_into_non_empty_buckets(): void
    {
        $s = $this->server();
        MetricSample::create(['server_id' => $s->id, 'cpu' => 20, 'mem' => 40, 'disk' => 10, 'load' => 0.5, 'sampled_at' => now()->subSeconds(90)]);
        MetricSample::create(['server_id' => $s->id, 'cpu' => 40, 'mem' => 60, 'disk' => 10, 'load' => 1.5, 'sampled_at' => now()->subSeconds(80)]);

        $series = app(MetricHistory::class)->series($s, 300);

        $this->assertSame(300, $series['window']);
        foreach (['from', 'now', 'bucket'] as $k) {
            $this->assertArrayHasKey($k, $series);
        }
        $this->assertNotEmpty($series['points']);
        // both samples land in the same bucket → averaged (20 + 40) / 2 = 30
        $this->assertSame(30, $series['points'][0]['cpu']);
        // only non-empty buckets are returned — no null gaps (the client connects them continuously)
        foreach ($series['points'] as $p) {
            $this->assertIsInt($p['cpu']);
        }
    }

    public function test_history_route_returns_the_series_json(): void
    {
        $this->actingAs(User::factory()->create(['must_change_password' => false]));
        $s = $this->server();
        MetricSample::create(['server_id' => $s->id, 'cpu' => 33, 'mem' => 44, 'disk' => 11, 'load' => 0.4, 'sampled_at' => now()->subMinutes(5)]);

        $this->getJson(route('servers.history', ['server' => $s, 'range' => '24h']))
            ->assertOk()
            ->assertJsonPath('window', 86400)
            ->assertJsonStructure(['window', 'from', 'now', 'bucket', 'points' => [['t', 'cpu', 'mem', 'disk', 'load']]]);
    }

    public function test_history_route_clamps_an_unknown_range(): void
    {
        $this->actingAs(User::factory()->create(['must_change_password' => false]));

        $this->getJson(route('servers.history', ['server' => $this->server(), 'range' => 'evil']))
            ->assertOk()
            ->assertJsonPath('window', 86400); // unknown → default 24h
    }
}
