<?php
declare(strict_types=1);

namespace App\Services;

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

final class UpdateValidationEngineService
{
    /**
     * @return array<string,mixed>
     */
    public function dashboard(): array
    {
        $registry = new UpdatePackageRegistryRepository();
        $snapshot = $registry->snapshot();

        $warnings = (array) ($snapshot['warnings'] ?? []);
        $runs = $this->runs($warnings);
        $stepsByRun = $this->steps($warnings);

        return [
            'packages' => (array) ($snapshot['packages'] ?? []),
            'runs' => $runs,
            'steps_by_run' => $stepsByRun,
            'generated_at' => date('Y-m-d H:i:s'),
            'warnings' => array_values(array_unique($warnings)),
            'runtime_database' => (string) ($snapshot['database'] ?? 'inconnue'),
            'runtime_package_count' => (int) ($snapshot['count'] ?? 0),
        ];
    }

    /**
     * @param array<int,string> $warnings
     * @return array<int,array<string,mixed>>
     */
    private function runs(array &$warnings): array
    {
        try {
            $statement = Database::connection()->query(
                "SELECT
                    id,
                    package_id,
                    package_name,
                    package_version,
                    archive_name,
                    run_type,
                    status,
                    decision,
                    score,
                    blocking_step,
                    duration_ms,
                    executed_by,
                    created_at,
                    finished_at
                 FROM system_package_pipeline_runs
                 ORDER BY id DESC
                 LIMIT 50"
            );

            $rows = $statement->fetchAll(PDO::FETCH_ASSOC) ?: [];
            $statement->closeCursor();

            return $rows;
        } catch (Throwable $error) {
            $warnings[] = 'Lecture des exécutions du pipeline indisponible.';
            $this->log('runs', $error);
            return [];
        }
    }

    /**
     * @param array<int,string> $warnings
     * @return array<int,array<int,array<string,mixed>>>
     */
    private function steps(array &$warnings): array
    {
        try {
            $statement = Database::connection()->query(
                "SELECT
                    id,
                    run_id,
                    step_code,
                    step_label,
                    step_order,
                    status,
                    decision,
                    score,
                    critical,
                    message,
                    issues_json,
                    warnings_json,
                    details_json,
                    duration_ms,
                    created_at
                 FROM system_package_pipeline_steps
                 ORDER BY run_id DESC, step_order ASC, id ASC
                 LIMIT 300"
            );

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

            return $stepsByRun;
        } catch (Throwable $error) {
            $warnings[] = 'Lecture des étapes du pipeline indisponible.';
            $this->log('steps', $error);
            return [];
        }
    }

    /**
     * @return array<string,mixed>
     */
    public function simulate(int $packageId, ?int $userId): array
    {
        $package = (new UpdatePackageRegistryRepository())->find($packageId);

        if ($package === null) {
            throw new RuntimeException('Package introuvable.');
        }

        $archive = (string) ($package['fichier_original'] ?? '');
        if ($archive === '') {
            throw new RuntimeException('Archive du package absente.');
        }

        return (new PipelineOrchestrator())->execute(
            BASE_PATH . '/storage/updates/packages/' . basename($archive),
            $packageId,
            $userId,
            'simulation',
            true
        );
    }

    private function log(string $section, 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',
            'section' => $section,
            '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
            );
        }
    }
}
