<?php

namespace App\Http\Controllers\Api;

use App\Http\Controllers\Controller;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Queue;
use OpenApi\Attributes as OA;

class HealthController extends Controller
{
    #[OA\Get(
        path: '/health',
        operationId: 'healthCheck',
        tags: ['System'],
        summary: 'Application health check',
        description: 'Returns status of database, Redis, storage, and queue. Returns 503 if any check fails.',
        responses: [
            new OA\Response(
                response: 200,
                description: 'All systems healthy',
                content: new OA\JsonContent(properties: [
                    new OA\Property(property: 'status', type: 'string', example: 'healthy'),
                    new OA\Property(property: 'timestamp', type: 'string', format: 'date-time'),
                    new OA\Property(property: 'checks', type: 'object'),
                ])
            ),
            new OA\Response(response: 503, description: 'One or more checks degraded'),
        ]
    )]
    public function check()
    {
        $checks = [
            'database' => $this->checkDatabase(),
            'redis' => $this->checkRedis(),
            'storage' => $this->checkStorage(),
            'queue' => $this->checkQueue(),
        ];

        $hasFailure = collect($checks)->contains(fn ($c) => $c['status'] === 'fail');
        $allOk = collect($checks)->every(fn ($c) => $c['status'] === 'ok');

        return response()->json([
            'status' => $allOk ? 'healthy' : ($hasFailure ? 'degraded' : 'warn'),
            'timestamp' => now()->toIso8601String(),
            'checks' => $checks,
        ], $hasFailure ? 503 : 200);
    }

    private function checkDatabase(): array
    {
        try {
            DB::connection()->getPdo();

            return ['status' => 'ok', 'latency_ms' => $this->measure(fn () => DB::select('SELECT 1'))];
        } catch (\Throwable $e) {
            return ['status' => 'fail', 'error' => $e->getMessage()];
        }
    }

    private function checkRedis(): array
    {
        try {
            $latency = $this->measure(fn () => Cache::store('redis')->put('health_check', true, 5));

            return ['status' => 'ok', 'latency_ms' => $latency];
        } catch (\Throwable $e) {
            return ['status' => 'fail', 'error' => $e->getMessage()];
        }
    }

    private function checkStorage(): array
    {
        try {
            $free = disk_free_space(storage_path());
            $total = disk_total_space(storage_path());

            if ($free === false || $total === false || $total === 0.0) {
                return ['status' => 'fail', 'error' => 'Unable to read disk space.'];
            }

            $usedPct = round((($total - $free) / $total) * 100, 1);

            return [
                'status' => $usedPct < 90 ? 'ok' : 'warn',
                'used_pct' => $usedPct,
                'free_gb' => round($free / 1073741824, 2),
            ];
        } catch (\Throwable $e) {
            return ['status' => 'fail', 'error' => $e->getMessage()];
        }
    }

    private function checkQueue(): array
    {
        try {
            $size = Queue::size('default');

            return ['status' => 'ok', 'pending_jobs' => $size];
        } catch (\Throwable $e) {
            return ['status' => 'unknown', 'error' => $e->getMessage()];
        }
    }

    private function measure(callable $fn): int
    {
        $start = hrtime(true);
        $fn();

        return (int) round((hrtime(true) - $start) / 1_000_000);
    }
}
