<?php

namespace App\Download\Downloader;

use App\Base\Service\Broadcaster;
use App\Base\Service\MediaFiles;
use App\Download\DownloadEvents;
use App\Download\Framework\Entity\Download;
use App\EventLog\Action\Command\AddEventLogCommand;
use App\Search\Action\Command\GetMediaInfoCommand;
use App\Search\Action\Handler\GetMediaInfoHandler;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Component\Cache\Adapter\RedisAdapter;
use Symfony\Component\Messenger\MessageBusInterface;
use Symfony\Component\Process\Exception\ProcessFailedException;
use Symfony\Component\Process\Process;
use Symfony\Contracts\Cache\CacheInterface;

class ProcessDownloader implements DownloaderInterface
{
    /**
     * @var RedisAdapter $cache
     */
    public function __construct(
        private MessageBusInterface $bus,
        private EntityManagerInterface $entityManager,
        private CacheInterface $cache,
        private readonly Broadcaster $broadcaster,
    ) {}

    /**
     * @inheritDoc
     */
    public function download(string $mediaType, string $url, string $downloadPath, ?int $downloadId): void
    {
        /** @var Download $downloadEntity */
        $downloadEntity = $this->entityManager->getRepository(Download::class)->find($downloadId);
        $this->entityManager->flush();

        $processArgs = ['wget', '-O', $downloadEntity->getFilename(), $url];

        if ($downloadEntity->getStatus() === 'Paused') {
            $downloadEntity->setStatus('In Progress');
            $processArgs = ['wget', '-c', '-O', $downloadEntity->getFilename(), $url];
        } else {
            $downloadEntity->setProgress(0);
        }

        $process = (new Process($processArgs))->setWorkingDirectory($downloadPath);
        $process->setTimeout(1800); // 30 min
        $process->setIdleTimeout(600); // 10 min
        $process->start();

        try {
            $progress = 0;
            $this->entityManager->flush();

            $process->wait(function ($type, $buffer) use ($progress, $downloadEntity, $process): void {
                // The PauseDownloadHandler will set this to 'true'
                $doPause = $this->cache->getItem('download.pause.' . $downloadEntity->getId());

                if (true === $doPause->isHit() && true === $doPause->get()) {
                    $downloadEntity->setStatus('Paused');
                    $this->cache->deleteItem('download.pause.' . $downloadEntity->getId());
                    $this->entityManager->flush();
                    $process->stop();
                }

                if (Process::ERR === $type) {
                    $pregMatchOutput = [];
                    preg_match('/[\d]+%/', $buffer, $pregMatchOutput);

                    if (!empty($pregMatchOutput)) {
                        if ($pregMatchOutput[0] !== $progress) {
                            $progress = (int) $pregMatchOutput[0];
                            $downloadEntity->setProgress($progress);
                            $this->entityManager->flush();
                        }
                    }
                }
                fwrite(STDOUT, $buffer);
            });
            if ($downloadEntity->getStatus() !== 'Paused') {
                $downloadEntity->setProgress(100);
                $this->alertComplete($downloadEntity);
            }
        } catch (ProcessFailedException $exception) {
            $downloadEntity->setStatus('Failed');
            $this->bus->dispatch(new AddEventLogCommand(
                $downloadEntity->getUser(),
                DownloadEvents::DOWNLOAD_ERROR->type(),
                DownloadEvents::DOWNLOAD_ERROR->message() . ': ' . $exception->getMessage(),
                (array) $downloadEntity
            ));
        }

        $this->entityManager->flush();
    }

    private function alertComplete(Download $download): void
    {
        if ("tvshows" === $download->getMediaType()) {
            $message = '"' . $download->getTitle() . '" - ' . $download->getEpisodeId() . ' has finished downloading.';
        } else {
            $message = '"' . $download->getTitle() . '" has finished downloading.';
        }

        $this->broadcaster->alert('Success', $message, sendPush: true);
    }
}
