<?php

namespace App\Download\Framework\Repository;

use App\Base\Util\EpisodeId;
use App\Base\Util\Paginator;
use App\Download\Framework\Entity\Download;
use App\User\Framework\Entity\User;
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
use Doctrine\Persistence\ManagerRegistry;
use App\Base\Util\PTN;
use Symfony\Component\Security\Core\User\UserInterface;

/**
 * @extends ServiceEntityRepository<Download>
 */
class DownloadRepository extends ServiceEntityRepository
{
    private ManagerRegistry $managerRegistry;

    private Paginator $paginator;

    public function __construct(ManagerRegistry $registry, ManagerRegistry $managerRegistry, Paginator $paginator)
    {
        parent::__construct($registry, Download::class);
        $this->managerRegistry = $managerRegistry;
        $this->paginator = $paginator;
    }

    public function getCompletePaginated(UserInterface $user, int $pageNumber = 1, int $perPage = 10, string $term = ""): Paginator
    {
        $query = $this->createQueryBuilder('d')
            ->andWhere('d.status IN (:statuses)')
            ->andWhere('d.user = :user')
            ->andWhere('(d.title LIKE :term OR d.filename LIKE :term OR d.imdbId LIKE :term OR d.status LIKE :term OR d.mediaType LIKE :term)')
            ->orderBy('d.id', 'DESC')
            ->setParameter('statuses', ['Complete'])
            ->setParameter('user', $user)
            ->setParameter('term', '%' . $term . '%')
            ->getQuery();

        return $this->paginator->paginate($query, $pageNumber, $perPage);
    }

    public function getActivePaginated(UserInterface $user, int $pageNumber = 1, int $perPage = 5, string $term = ""): Paginator
    {
        $query = $this->createQueryBuilder('d')
            ->andWhere('d.status IN (:statuses)')
            ->andWhere('d.user = :user')
            ->andWhere('(d.title LIKE :term OR d.filename LIKE :term OR d.imdbId LIKE :term OR d.status LIKE :term OR d.mediaType LIKE :term)')
            ->orderBy('d.id', 'ASC')
            ->setParameter('statuses', ['New', 'In Progress', 'Paused'])
            ->setParameter('user', $user)
            ->setParameter('term', '%' . $term . '%')
            ->getQuery();

        return $this->paginator->paginate($query, $pageNumber, $perPage);
    }

    public function insertNew(
        UserInterface $user,
        string $imdbId,
        string $mediaType,
        string $title,
        int|null $season = null,
        int|null $episode = null,
        string $status = 'New'
    ): Download {
        /** @var User $user */
        $download = (new Download())
            ->setUser($user)
            ->setImdbId($imdbId)
            ->setTitle($title)
            ->setMediaType($mediaType)
            ->setProgress(0)
            ->setStatus($status);

        if (null !== $season && null !== $episode) {
            $download->setEpisodeId(
                EpisodeId::fromSeasonEpisodeNumbers($season, $episode)
            );
        }

        $this->getEntityManager()->persist($download);
        $this->getEntityManager()->flush();

        return $download;
    }

    public function insert(
        UserInterface $user,
        string $url,
        string $title,
        string $filename,
        string $imdbId,
        string $mediaType,
        ?string $episodeId = null,
        string $status = 'New'
    ): Download {
        $ptn = (object) new Ptn()->parse($filename);
        if ($mediaType === "tvshows" &&
            !property_exists($ptn, 'episode') && !property_exists($ptn, 'season')
        ) {
            $filename = $episodeId . '_' . $filename;
        }
        /** @var User $user */
        $download = (new Download())
            ->setUser($user)
            ->setUrl($url)
            ->setTitle($title)
            ->setFilename($filename)
            ->setImdbId($imdbId)
            ->setMediaType($mediaType)
            ->setEpisodeId($episodeId)
            ->setProgress(0)
            ->setStatus($status);

        $this->getEntityManager()->persist($download);
        $this->getEntityManager()->flush();

        return $download;
    }

    public function updateStatus(int $id, string $status): Download
    {
        $download = $this->find($id);
        $download->setStatus($status);
        $this->getEntityManager()->flush();
        return $download;
    }

    public function updateProgress(int $id, int $progress): Download
    {
        $download = $this->find($id);
        $download->setProgress($progress);
        $this->getEntityManager()->flush();
        return $download;
    }

    public function delete(int $id)
    {
        $entity = $this->find($id);
        $this->getEntityManager()->remove($entity);
        $this->getEntityManager()->flush();
    }

    public function latest(int $limit = 1)
    {
        return $this->createQueryBuilder('d')
            ->andWhere('d.status IN (:statuses)')
            ->setParameter('statuses', ['Complete'])
            ->setMaxResults($limit)
            ->orderBy('d.id', 'DESC')
            ->getQuery()
            ->getResult();
    }

    public function badDownloadUrls()
    {
        return $this->createQueryBuilder('d')
            ->select('d.url')
            ->andWhere('d.status = :status')
            ->andWhere('d.progress = 100')
            ->setParameter('status', 'Failed')
            ->distinct()
            ->getQuery()
            ->getResult();
    }
}
