<?php

namespace App\Command;

use App\Entity\SargassumObservation;
use App\Service\Forecast\DriftSimulationService;
use App\Service\Forecast\ImpactScoreService;
use Doctrine\ORM\EntityManagerInterface;
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;

#[AsCommand(
    name: 'app:compute-forecasts',
    description: 'Calcule les forecasts de dérive et les ImpactScores pour les dernières observations',
)]
class ComputeForecastsCommand extends Command
{
    public function __construct(
        private DriftSimulationService $drift,
        private ImpactScoreService     $scores,
        private EntityManagerInterface $em,
    ) {
        parent::__construct();
    }

    protected function configure(): void
    {
        $this
            ->addOption('limit', 'l', InputOption::VALUE_OPTIONAL,
                'Nombre max d\'observations à traiter', 5)
            ->addOption('force', null, InputOption::VALUE_NONE,
                'Supprime les forecasts existants et les recalcule (utile après changement de modèle)');
    }

    protected function execute(InputInterface $input, OutputInterface $output): int
    {
        $io    = new SymfonyStyle($input, $output);
        $limit = (int) $input->getOption('limit');
        $force = (bool) $input->getOption('force');

        $io->title('Calcul forecasts + ImpactScores');

        // Charge les N observations cibles (avec ou sans forecasts existants)
        $qb = $this->em->getRepository(SargassumObservation::class)
            ->createQueryBuilder('o')
            ->orderBy('o.detectedAt', 'DESC')
            ->setMaxResults($limit);

        if (!$force) {
            $qb->leftJoin('o.forecasts', 'f')->where('f.id IS NULL');
        }

        $observations = $qb->getQuery()->getResult();

        if (empty($observations)) {
            $io->info('Aucune observation sans forecast trouvée.');
            return Command::SUCCESS;
        }

        if ($force) {
            $ids = array_map(fn($o) => (string) $o->getId(), $observations);
            $io->note(sprintf('--force : recalcul pour %d observation(s)...', count($ids)));

            $this->em->getConnection()->executeStatement(
                'DELETE FROM sargassum_forecast WHERE source_observation_id IN (:ids)',
                ['ids' => $ids],
                ['ids' => \Doctrine\DBAL\ArrayParameterType::STRING]
            );
            // Note : on ne supprime PAS les impact_scores existants.
            // computeForObservation créera de nouveaux scores ; les anciens restent
            // pour les spots où cette observation est trop loin (MAX_RELEVANT_M).
            $this->em->clear();

            // Recharge les observations détachées
            $observations = $this->em->getRepository(SargassumObservation::class)
                ->createQueryBuilder('o')
                ->where('o.id IN (:ids)')
                ->setParameter('ids', $ids)
                ->getQuery()
                ->getResult();
        }

        $io->progressStart(count($observations));
        $errors = 0;

        foreach ($observations as $observation) {
            try {
                $forecasts = $this->drift->computeForecasts($observation);
                $io->text(sprintf(
                    'Observation %s → %d forecast(s)',
                    $observation->getId(),
                    count($forecasts)
                ));

                $this->scores->computeForObservation($observation, $forecasts);
            } catch (\Throwable $e) {
                $io->error('Erreur sur ' . $observation->getId() . ' : ' . $e->getMessage());
                $errors++;
            }

            $io->progressAdvance();
        }

        $io->progressFinish();

        $io->success(sprintf(
            '%d observation(s) traitée(s), %d erreur(s).',
            count($observations) - $errors,
            $errors
        ));

        return $errors === 0 ? Command::SUCCESS : Command::FAILURE;
    }
}
