<?php
declare(strict_types=1);

namespace App\Services;

use App\Core\Database;
use App\Services\Pipeline\PipelineOrchestrator;
use PDO;
use RuntimeException;
use Throwable;

final class UpdateValidationEngineService
{
    /**
     * @return array{
     *     packages: array<int,array<string,mixed>>,
     *     runs: array<int,array<string,mixed>>,
     *     steps_by_run: array<int,array<int,array<string,mixed>>>,
     *     generated_at: string,
     *     warning?: string
     * }
     */
    public function dashboard(): array
    {
        try {
            $packagesStatement = Database::connection()->prepare(
                "SELECT
                    id,
                    nom_package,
                    version_package,
                    statut,
                    importe_at,
                    checksum_sha256,
                    message
                 FROM cda_update_packages
                 ORDER BY id DESC
                 LIMIT 60"
            );
            $packagesStatement->execute();

            $runsStatement = Database::connection()->prepare(
                "SELECT *
                 FROM system_package_pipeline_runs
                 ORDER BY id DESC
                 LIMIT 50"
            );
            $runsStatement->execute();

            /*
             * Compatibilité MySQL/MariaDB :
             * ne pas utiliser LIMIT dans une sous-requête IN(...).
             * La table dérivée est statique, préparée et ne reçoit aucune
             * donnée utilisateur.
             */
            $stepsStatement = Database::connection()->prepare(
                "SELECT s.*
                 FROM system_package_pipeline_steps AS s
                 INNER JOIN (
                     SELECT id
                     FROM system_package_pipeline_runs
                     ORDER BY id DESC
                     LIMIT 15
                 ) AS recent_runs
                     ON recent_runs.id = s.run_id
                 ORDER BY
                     s.run_id DESC,
                     s.step_order ASC,
                     s.id ASC"
            );
            $stepsStatement->execute();

            $stepsByRun = [];
            foreach ($stepsStatement->fetchAll(PDO::FETCH_ASSOC) ?: [] as $step) {
                $stepsByRun[(int) $step['run_id']][] = $step;
            }

            return [
                'packages' => $packagesStatement->fetchAll(PDO::FETCH_ASSOC) ?: [],
                'runs' => $runsStatement->fetchAll(PDO::FETCH_ASSOC) ?: [],
                'steps_by_run' => $stepsByRun,
                'generated_at' => date('Y-m-d H:i:s'),
            ];
        } catch (Throwable $error) {
            $this->writeDiagnostic($error);

            return [
                'packages' => [],
                'runs' => [],
                'steps_by_run' => [],
                'generated_at' => date('Y-m-d H:i:s'),
                'warning' => 'Le Validation Center est temporairement indisponible. Consultez le journal technique.',
            ];
        }
    }

    /**
     * @return array<string,mixed>
     */
    public function simulate(int $packageId, ?int $userId): array
    {
        if ($packageId <= 0) {
            throw new RuntimeException('Identifiant de package invalide.');
        }

        $statement = Database::connection()->prepare(
            "SELECT fichier_original
             FROM cda_update_packages
             WHERE id = :id
             LIMIT 1"
        );
        $statement->execute(['id' => $packageId]);

        $archive = $statement->fetchColumn();
        if (!is_string($archive) || $archive === '') {
            throw new RuntimeException('Package introuvable.');
        }

        $zipPath = BASE_PATH . '/storage/updates/packages/' . basename($archive);

        return (new PipelineOrchestrator())->execute(
            $zipPath,
            $packageId,
            $userId,
            'simulation',
            true
        );
    }

    private function writeDiagnostic(Throwable $error): void
    {
        $directory = BASE_PATH . '/storage/logs';
        if (!is_dir($directory)) {
            @mkdir($directory, 0770, true);
        }

        $entry = json_encode([
            'at' => gmdate('c'),
            'event' => 'validation_center.error',
            'type' => get_class($error),
            'message' => $error->getMessage(),
            'file' => $error->getFile(),
            'line' => $error->getLine(),
        ], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);

        if (is_string($entry)) {
            @file_put_contents(
                $directory . '/validation_center.log',
                $entry . PHP_EOL,
                FILE_APPEND | LOCK_EX
            );
        }
    }
}
