<?php

namespace App\Search\Framework\Command;

use Aimeos\Map;
use App\Download\Action\Command\DownloadMediaCommand;
use App\Download\Action\Handler\DownloadMediaHandler;
use App\Download\Framework\Repository\DownloadRepository;
use App\Search\Action\Handler\SearchHandler;
use App\Search\Action\Command\SearchCommand as CommandInput;
use App\Search\Action\Result\SearchResult;
use App\Tmdb\TmdbResult;
use App\Torrentio\Action\Command\GetMovieOptionsCommand;
use App\Torrentio\Action\Command\GetTvShowOptionsCommand;
use App\Torrentio\Action\Handler\GetMovieOptionsHandler;
use App\Torrentio\Action\Handler\GetTvShowOptionsHandler;
use App\Torrentio\Result\TorrentioResult;
use App\User\Framework\Repository\UserRepository;
use Symfony\Component\Console\Attribute\AsCommand;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Helper\Table;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Question\ChoiceQuestion;
use Symfony\Component\Console\Question\Question;
use Symfony\Component\Console\Style\SymfonyStyle;
use Symfony\Component\Messenger\MessageBusInterface;

#[AsCommand('search')]
class SearchCommand extends Command
{
    private SearchHandler $searchHandler;
    private GetTvShowOptionsHandler $getTvShowOptionsHandler;
    private GetMovieOptionsHandler $getMovieOptionsHandler;
    private UserRepository $userRepository;
    private DownloadRepository $downloadRepository;
    private DownloadMediaHandler $downloadMediaHandler;
    private MessageBusInterface $bus;

    public function __construct(SearchHandler $searchHandler, GetTvShowOptionsHandler $getTvShowOptionsHandler,
        GetMovieOptionsHandler $getMovieOptionsHandler,
        UserRepository $userRepository,
        DownloadRepository $downloadRepository,
        DownloadMediaHandler $downloadMediaHandler,
        MessageBusInterface $bus,
        ?string $name = null
    ) {
        parent::__construct($name);
        $this->searchHandler = $searchHandler;
        $this->getTvShowOptionsHandler = $getTvShowOptionsHandler;
        $this->getMovieOptionsHandler = $getMovieOptionsHandler;
        $this->userRepository = $userRepository;
        $this->downloadRepository = $downloadRepository;
        $this->downloadMediaHandler = $downloadMediaHandler;
        $this->bus = $bus;
    }

    protected function configure()
    {
        $this->addArgument('term', InputArgument::REQUIRED);
        $this->addOption('local', 'l', InputOption::VALUE_NONE, 'Perform the download locally instead of submitting it to the queue.');
    }

    public function run(InputInterface $input, OutputInterface $output): int
    {
        $io = new SymfonyStyle($input, $output);
        $command = new CommandInput($input->getArgument('term'));

        // Perform search
        $mediaOptions = $this->searchHandler->handle($command);

        // Render results and ask the User to pick one
        $mediaChoice = $this->askToChooseMediaOption($io, $output, $mediaOptions);

        // Find download options based on the User's choice
        $downloadOptions = $this->fetchDownloadOptions($mediaChoice);

        // Render results and ask the User to pick one
        $downloadChoice = $this->askToChooseDownloadOption($io, $output, $downloadOptions);

        // Have user confirm download action
        $confirmation = $this->askToConfirmDownload($io, $output, $downloadChoice);

        // Begin download or submit to the queue
        if (true === $confirmation) {
            $downloadLocally = $input->getOption('local');
            $this->submitDownload($io, $mediaChoice, $downloadChoice, $downloadLocally);
        } else {
            $io->success('No results found.');
        }

        $io->success('Success!');

        return Command::SUCCESS;
    }

    private function askToChooseMediaOption(SymfonyStyle $io, OutputInterface $output, SearchResult $result): TmdbResult
    {
        $table = new Table($output)
            ->setHeaders(['ID', 'Title', 'Year', 'IMDb ID', 'Description'])
            ->setRows(
                Map::from($result->results)
                    ->map(fn ($result, $index) => [$index, $result->title, $result->year, $result->imdbId, substr($result->description, 0, 80) . '...'])
                    ->toArray()
            );

        $table->render();

        $question = new Question('Enter the ID of the correct result: ');
        $choiceId = $io->askQuestion($question);
        $choice = $result->results[$choiceId];

        $io->info('You chose: ' . $choice->title);
        $io->table(
            ['ID', 'Title', 'Year', 'IMDb ID', 'Description'],
            [
                [$choiceId, $choice->title, $choice->year, $choice->imdbId, substr($choice->description, 0, 80) . '...'],
            ]
        );

        return $choice;
    }

    private function fetchDownloadOptions(TmdbResult $result): array
    {
        $handlers = [
            'movies' => $this->getMovieOptionsHandler,
            'tvshows' => $this->getTvShowOptionsHandler,
        ];

        $handler = $handlers[$result->mediaType];

        if ("movies" === $result->mediaType) {
            $command = new GetMovieOptionsCommand(
                $result->tmdbId,
                $result->imdbId
            );
        } else {
            $command = new GetTvShowOptionsCommand(
                $result->tmdbId,
                $result->imdbId,
                1,
                1
            );
        }

        $result = $handler->handle($command);
        return $result->results;
    }

    private function askToChooseDownloadOption(SymfonyStyle $io, OutputInterface $output, array $options): TorrentioResult
    {
        $table = new Table($output)
            ->setHeaders(['ID', 'Size', 'Resolution', 'Codec', 'Seeders', 'Provider', 'Language'])
            ->setRows(
                Map::from($options)
                    ->map(fn (TorrentioResult $result, $index) => [$index, $result->size, $result->resolution, $result->codec, $result->seeders, $result->provider, implode(', ', $result->languages)])
                    ->toArray()
            );
        $table->render();

        $question = new Question('Enter the ID of the item to download: ');
        $choiceId = $io->askQuestion($question);
        $choice = $options[$choiceId];
        $io->info('You chose: ' . $choice->title);

        return $choice;
    }

    private function askToConfirmDownload(SymfonyStyle $io, OutputInterface $output, TorrentioResult $downloadOption): bool
    {
        $question = new ChoiceQuestion('Are you sure you want to download the above file?', ['yes', 'no']);
        $choice = $io->askQuestion($question);
        return $choice === 'yes';
    }

    private function submitDownload(SymfonyStyle $io, TmdbResult $mediaChoice, TorrentioResult $downloadOption, bool $downloadLocally = false): void
    {
        $io->writeln('> Adding download record');
        $user = $this->userRepository->find(1);
        $download = $this->downloadRepository->insert(
            $user,
            $downloadOption->url,
            $downloadOption->title,
            $downloadOption->filename,
            $mediaChoice->imdbId,
            $mediaChoice->mediaType,
        );

        $io->writeln('> Download record added: ' . $download->getId());
        $downloadCommand = new DownloadMediaCommand(
            $download->getUrl(),
            $download->getTitle(),
            $download->getFilename(),
            $download->getMEdiaType(),
            $download->getImdbId(),
            $download->getUser()->getId(),
            $download->getId()
        );

        if (true === $downloadLocally) {
            $io->writeln('> Beginning local download');
            $this->downloadMediaHandler->handle($downloadCommand);
        } else {
            $io->writeln('> Submitting download to queue');
            $this->bus->dispatch($downloadCommand);
        }

        $io->writeln('> Download added to queue');
    }
}
