<?php

declare(strict_types=1);

namespace App\Scolarite\Infrastructure\Console;

use App\Scolarite\Application\Service\RecalculerMoyennesService;
use App\Scolarite\Domain\Repository\EvaluationRepository;
use App\Shared\Infrastructure\Tenant\TenantConfig;
use App\Shared\Infrastructure\Tenant\TenantContext;
use App\Shared\Infrastructure\Tenant\TenantDatabaseSwitcher;
use App\Shared\Infrastructure\Tenant\TenantId;
use App\Shared\Infrastructure\Tenant\TenantRegistry;

use function count;

use Override;

use function sprintf;

use Symfony\Component\Console\Attribute\AsCommand;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Style\SymfonyStyle;
use Throwable;

/**
 * Recalcule toutes les projections de moyennes à partir des données existantes.
 *
 * Usage:
 *   php bin/console app:recalculer-moyennes              # Tous les tenants
 *   php bin/console app:recalculer-moyennes --tenant=UUID # Un seul tenant
 */
#[AsCommand(
    name: 'app:recalculer-moyennes',
    description: 'Recalcule les statistiques évaluations et moyennes élèves depuis les notes publiées',
)]
final class RecalculerToutesMoyennesCommand extends Command
{
    public function __construct(
        private readonly EvaluationRepository $evaluationRepository,
        private readonly TenantRegistry $tenantRegistry,
        private readonly TenantContext $tenantContext,
        private readonly TenantDatabaseSwitcher $databaseSwitcher,
        private readonly RecalculerMoyennesService $service,
    ) {
        parent::__construct();
    }

    #[Override]
    protected function configure(): void
    {
        $this->addOption('tenant', 't', InputOption::VALUE_REQUIRED, 'Limiter à un tenant spécifique (UUID)');
    }

    #[Override]
    protected function execute(InputInterface $input, OutputInterface $output): int
    {
        $io = new SymfonyStyle($input, $output);
        $io->title('Recalcul des moyennes et statistiques');

        /** @var string|null $tenantOption */
        $tenantOption = $input->getOption('tenant');

        if ($tenantOption !== null) {
            $configs = [$this->tenantRegistry->getConfig(TenantId::fromString($tenantOption))];
        } else {
            $configs = $this->tenantRegistry->getAllConfigs();
        }

        $totalEvals = 0;
        $totalErrors = 0;

        foreach ($configs as $config) {
            [$processed, $errors] = $this->processTenant($config, $io);
            $totalEvals += $processed;
            $totalErrors += $errors;
        }

        if ($totalErrors > 0) {
            $io->warning(sprintf(
                '%d évaluation(s) traitée(s), %d erreur(s).',
                $totalEvals,
                $totalErrors,
            ));

            return Command::FAILURE;
        }

        $io->success(sprintf('%d évaluation(s) traitée(s) avec succès.', $totalEvals));

        return Command::SUCCESS;
    }

    /**
     * @return array{int, int} [processed, errors]
     */
    private function processTenant(TenantConfig $config, SymfonyStyle $io): array
    {
        $this->tenantContext->setCurrentTenant($config);
        $this->databaseSwitcher->useTenantDatabase($config->databaseUrl);

        $tenantId = \App\Shared\Domain\Tenant\TenantId::fromString((string) $config->tenantId);

        $evaluations = $this->evaluationRepository->findAllWithPublishedGrades($tenantId);

        if ($evaluations === []) {
            $io->text(sprintf('  Tenant %s : aucune évaluation publiée.', $config->subdomain));

            return [0, 0];
        }

        $io->text(sprintf('  Tenant %s : %d évaluation(s) publiée(s)', $config->subdomain, count($evaluations)));

        $processed = 0;
        $errors = 0;

        foreach ($evaluations as $evaluation) {
            try {
                $this->service->recalculerStatistiquesEvaluation($evaluation->id, $tenantId);
                $this->service->recalculerTousElevesPourEvaluation($evaluation->id, $tenantId);
                ++$processed;
            } catch (Throwable $e) {
                $io->error(sprintf('  Erreur évaluation %s : %s', $evaluation->id, $e->getMessage()));
                ++$errors;
            }
        }

        $io->text(sprintf('    → %d traitée(s), %d erreur(s)', $processed, $errors));

        return [$processed, $errors];
    }
}
