<?php

namespace App\Download\Action\Handler;

use Aimeos\Map;
use App\Base\Enum\MediaType;
use App\Base\Service\MediaFiles;
use App\Download\Action\Command\DownloadMediaCommand;
use App\Download\Action\Command\DownloadSeasonCommand;
use App\Download\Action\Result\DownloadMediaResult;
use App\Download\Action\Result\DownloadSeasonResult;
use App\Download\DownloadOptionEvaluator;
use App\Download\Framework\Entity\Download;
use App\Download\Framework\Repository\DownloadRepository;
use App\Tmdb\TmdbClient;
use App\Torrentio\Action\Command\GetTvShowOptionsCommand;
use App\Torrentio\Action\Handler\GetTvShowOptionsHandler;
use App\User\Dto\UserPreferencesFactory;
use App\User\Framework\Repository\UserRepository;
use App\Base\Util\PTN;
use OneToMany\RichBundle\Contract\CommandInterface;
use OneToMany\RichBundle\Contract\HandlerInterface;
use OneToMany\RichBundle\Contract\ResultInterface;
use Psr\Log\LoggerInterface;
use Symfony\Component\Messenger\MessageBusInterface;

/** @implements HandlerInterface<DownloadSeasonCommand, DownloadMediaResult> */
readonly class DownloadSeasonHandler implements HandlerInterface
{
    public function __construct(
        private MediaFiles              $mediaFiles,
        private LoggerInterface         $logger,
        private TmdbClient              $tmdb,
        private MessageBusInterface     $bus,
        private DownloadOptionEvaluator $downloadOptionEvaluator,
        private GetTvShowOptionsHandler $getTvShowOptionsHandler,
        private UserRepository          $userRepository, private DownloadRepository $downloadRepository,
    ) {}

    public function handle(CommandInterface $command): ResultInterface
    {
        $series = $this->tmdb->tvshowDetails($command->imdbId);

        $this->logger->info('> [DownloadTvSeasonHandler] Executing DownloadTvSeasonHandler for "' . $series->title . '" season ' . $command->season);

        $episodesInSeason = Map::from($series->episodes[$command->season]);
        $this->logger->info('> [DownloadTvSeasonHandler] ...Found ' . count($episodesInSeason) . ' episodes in season ' . $command->season);

        $downloadCommands = [];
        foreach ($episodesInSeason as $episode) {
            $this->logger->info('> [DownloadTvSeasonHandler] ...Evaluating episode ' . $episode->episodeNumber);

            $results = $this->getTvShowOptionsHandler->handle(
                new GetTvShowOptionsCommand(
                    $series->tmdbId,
                    $command->imdbId,
                    $command->season,
                    $episode->episodeNumber
                )
            );

            $this->logger->info('> [DownloadTvSeasonHandler] ......Found ' . count($results->results) . ' total download options, beginning evaluation');

            $userPreferences = UserPreferencesFactory::createFromUser(
                $this->userRepository->findOneBy(['id' => $command->userId])
            );

            $result = $this->downloadOptionEvaluator->evaluateOptions($results->results, $userPreferences);

            if (null !== $result) {
                $this->logger->info('> [DownloadTvSeasonHandler] ......Found 1 matching result');
                $this->logger->info('> [DownloadTvSeasonHandler] ......Dispatching DownloadMediaCommand for "' . $series->title . '" season ' . $command->season . ' episode ' . $episode->episodeNumber);
                $download = $this->createDownload($command, $result->url, $series->title, $result->filename, $episode->episodeNumber);
                $this->logger->info('> [DownloadTvSeasonHandler] ......Created Download entity with id ' . $download->getId());
                $downloadCommand = new DownloadMediaCommand(
                    $download->getUrl(),
                    $download->getTitle(),
                    $download->getFilename(),
                    $download->getMediaType(),
                    $download->getImdbId(),
                    $download->getUser()->getId(),
                    $download->getId()
                );
                $this->bus->dispatch($downloadCommand);
                $downloadCommands[] = $downloadCommand;
            } else {
                $this->logger->info('> [DownloadTvSeasonHandler] ......Found 0 matching results');
            }
        }

        return new DownloadSeasonResult(
            status: 200,
            message: 'Success',
            data: ['downloads' => $downloadCommands],
        );
    }

    private function createDownload(DownloadSeasonCommand $command, string $url, string $title, string $filename, int $episodeNumber): Download
    {
        $download = new Download();
        $download->setUrl($url);
        $download->setTitle($title);
        $download->setFilename($filename);
        $download->setImdbId($command->imdbId);
        $download->setMediaType(MediaType::TvShow->value);
        $download->setEpisodeId($this->getEpisodeNumber($command->season, $episodeNumber));
        $download->setUser($this->userRepository->find($command->userId));
        $this->downloadRepository->getEntityManager()->persist($download);
        $this->downloadRepository->getEntityManager()->flush();
        return $download;
    }

    private function getEpisodeNumber(int $season, int $episode): string
    {
        return sprintf(
            "S%sE%s",
            str_pad($season, 2, "0", STR_PAD_LEFT),
            str_pad($episode, 2, "0", STR_PAD_LEFT)
        );
    }
}
