<?php
declare(strict_types=1);

namespace App\Core\Health;

use App\Core\Database;
use App\Core\Migrations\MigrationManager;
use App\Core\Modules\ModuleManager;

final class SystemHealth
{
    public function __construct(
        private readonly ModuleManager $modules,
        private readonly MigrationManager $migrations,
        private readonly string $storagePath,
    ) {
    }

    /** @return array<string,array<string,mixed>> */
    public function report(): array
    {
        $checks = [];
        $checks['php'] = $this->check(version_compare(PHP_VERSION, '8.1.0', '>='), 'PHP ' . PHP_VERSION, 'PHP 8.1 minimum');
        $checks['pdo'] = $this->check(extension_loaded('pdo_mysql'), 'Extension PDO MySQL active', 'Extension pdo_mysql absente');
        $checks['zip'] = $this->check(class_exists(\ZipArchive::class), 'ZipArchive actif', 'ZipArchive absent : mises à jour indisponibles');
        $checks['database'] = $this->databaseCheck();
        $checks['storage'] = $this->pathCheck($this->storagePath, 'Stockage principal');
        $checks['backups'] = $this->pathCheck(BASE_PATH . '/storage/backups', 'Dossier des sauvegardes');
        $checks['updates'] = $this->pathCheck(BASE_PATH . '/storage/updates', 'Dossier des mises à jour');
        $modules = $this->modules->discover();
        $invalid = array_filter($modules, static fn(array $module): bool => ($module['status'] ?? '') !== 'ok');
        $checks['modules'] = $this->check(count($invalid) === 0, count($modules) . ' module(s) détecté(s)', count($invalid) . ' manifeste(s) invalide(s)');
        $pending = array_filter($this->migrations->status(), static fn(array $migration): bool => !$migration['applied']);
        $checks['migrations'] = $this->check(count($pending) === 0, 'Migrations à jour', count($pending) . ' migration(s) en attente');
        return $checks;
    }

    /** @return array<string,mixed> */
    private function databaseCheck(): array
    {
        try {
            Database::connection()->query('SELECT 1');
            return $this->check(true, 'Connexion SQL opérationnelle', '');
        } catch (\Throwable $exception) {
            return $this->check(false, '', 'Connexion SQL impossible : ' . $exception->getMessage());
        }
    }

    /** @return array<string,mixed> */
    private function pathCheck(string $path, string $label): array
    {
        if (!is_dir($path)) {
            @mkdir($path, 0775, true);
        }
        return $this->check(
            is_dir($path) && is_writable($path),
            $label . ' accessible en écriture',
            $label . ' non accessible en écriture'
        );
    }

    /** @return array<string,mixed> */
    private function check(bool $ok, string $success, string $failure): array
    {
        return ['status' => $ok ? 'ok' : 'warning', 'message' => $ok ? $success : $failure];
    }
}
