<?php

namespace Tests\Feature;

use App\Console\Commands\OperationsSchedulerHeartbeat;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Support\Facades\Artisan;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\File;
use Illuminate\Support\Str;
use Tests\TestCase;

class OperationsHealthCheckTest extends TestCase
{
    use RefreshDatabase;

    public function test_health_check_passes_when_operational_signals_are_healthy(): void
    {
        $backupRoot = storage_path('framework/testing/operations-health-backups');
        File::deleteDirectory($backupRoot);
        File::ensureDirectoryExists($backupRoot.'/healthy-backup');
        File::put($backupRoot.'/healthy-backup/manifest.json', json_encode([
            'format_version' => 1,
            'created_at' => now()->utc()->toIso8601String(),
            'database' => ['file' => 'database.sql', 'sha256' => str_repeat('a', 64)],
            'private_files' => [],
        ], JSON_THROW_ON_ERROR));

        config([
            'backups.schedule_enabled' => true,
            'backups.path' => $backupRoot,
            'operations.scheduler_max_age_seconds' => 180,
            'operations.failed_jobs_window_minutes' => 60,
            'operations.failed_jobs_max' => 0,
            'operations.disk_min_free_mb' => 0,
            'operations.backup_max_age_hours' => 26,
        ]);
        Cache::put(OperationsSchedulerHeartbeat::CACHE_KEY, now()->utc()->toIso8601String(), now()->addMinutes(10));

        try {
            $exitCode = Artisan::call('operations:health-check', ['--json' => true]);
            $report = json_decode(Artisan::output(), true, flags: JSON_THROW_ON_ERROR);

            $this->assertSame(0, $exitCode);
            $this->assertSame('healthy', $report['status']);
            $this->assertSame('pass', $report['checks']['database']['status']);
            $this->assertSame('pass', $report['checks']['failed_queue_jobs']['status']);
            $this->assertSame('pass', $report['checks']['scheduler']['status']);
            $this->assertSame('pass', $report['checks']['disk_space']['status']);
            $this->assertSame('pass', $report['checks']['backup']['status']);
        } finally {
            File::deleteDirectory($backupRoot);
            Cache::forget(OperationsSchedulerHeartbeat::CACHE_KEY);
        }
    }

    public function test_health_check_fails_for_recent_queue_failure_missing_scheduler_and_missing_backup(): void
    {
        $backupRoot = storage_path('framework/testing/operations-health-empty-backups');
        File::deleteDirectory($backupRoot);
        File::ensureDirectoryExists($backupRoot);
        Cache::forget(OperationsSchedulerHeartbeat::CACHE_KEY);
        config([
            'backups.schedule_enabled' => true,
            'backups.path' => $backupRoot,
            'operations.failed_jobs_window_minutes' => 60,
            'operations.failed_jobs_max' => 0,
            'operations.disk_min_free_mb' => 0,
        ]);
        DB::table('failed_jobs')->insert([
            'uuid' => (string) Str::uuid(),
            'connection' => 'database',
            'queue' => 'default',
            'payload' => '{}',
            'exception' => 'test failure',
            'failed_at' => now(),
        ]);

        try {
            $this->artisan('operations:health-check')
                ->expectsOutputToContain('FAIL: failed_queue_jobs')
                ->expectsOutputToContain('FAIL: scheduler')
                ->expectsOutputToContain('FAIL: backup')
                ->expectsOutputToContain('Operational health check failed.')
                ->assertFailed();
        } finally {
            File::deleteDirectory($backupRoot);
        }
    }

    public function test_disabled_backup_schedule_is_a_warning_not_a_runtime_outage(): void
    {
        config([
            'backups.schedule_enabled' => false,
            'operations.failed_jobs_max' => 0,
            'operations.disk_min_free_mb' => 0,
        ]);
        Cache::put(OperationsSchedulerHeartbeat::CACHE_KEY, now()->utc()->toIso8601String(), now()->addMinutes(10));

        try {
            $this->artisan('operations:health-check')
                ->expectsOutputToContain('WARN: backup - Scheduled backups are disabled')
                ->assertSuccessful();
        } finally {
            Cache::forget(OperationsSchedulerHeartbeat::CACHE_KEY);
        }
    }

    public function test_health_command_and_http_up_endpoint_are_available(): void
    {
        $this->assertArrayHasKey('operations:health-check', Artisan::all());
        $this->get('/up')->assertOk();
    }
}
