<?php

namespace App\Torrentio\Client;

use App\Torrentio\Client\Rule\DownloadOptionFilter\Resolution;
use App\Torrentio\Client\Rule\RuleEngine;
use App\Torrentio\Result\ResultFactory;
use Carbon\Carbon;
use App\Torrentio\Exception\TorrentioRateLimitException;
use GuzzleHttp\Client;
use Psr\Log\LoggerInterface;
use Symfony\Component\DependencyInjection\Attribute\Autowire;
use Symfony\Contracts\Cache\ItemInterface;
use Symfony\Contracts\Cache\TagAwareCacheInterface;

class Torrentio
{
    private string $baseUrl = 'https://torrentio.strem.fun/providers%253Dyts%252Ceztv%252Crarbg%252C1337x%252Cthepiratebay%252Ckickasstorrents%252Ctorrentgalaxy%252Cmagnetdl%252Chorriblesubs%252Cnyaasi%7Csort%253Dqualitysize%7Cqualityfilter%253D480p%252Cscr%252Ccam%7Crealdebrid={realDebridKey}/stream/movie';

    private string $searchUrl;

    private Client $client;

    public function __construct(
        #[Autowire(env: 'REAL_DEBRID_KEY')] private string $realDebridKey,
        private TagAwareCacheInterface $cache,
        private LoggerInterface $logger,
    ) {
        $this->searchUrl = str_replace('{realDebridKey}', $this->realDebridKey, $this->baseUrl);
        $this->client = new Client([
            'base_uri' => $this->searchUrl,
        ]);
    }

    public function search(string $imdbCode, string $type, bool $parseResults = true): array
    {
        $cacheKey = "torrentio.{$imdbCode}";

        $results = $this->cache->get($cacheKey, function (ItemInterface $item) use ($imdbCode, $type) {
            $item->expiresAt(Carbon::now()->addHour()->setMinute(0)->setSecond(0));
            $item->tag(['torrentio', $type, $imdbCode]);
            try {
                $response = $this->client->get("$this->searchUrl/$imdbCode.json");
                return json_decode(
                    $response->getBody()->getContents(),
                    true
                );
            } catch (\Throwable $exception) {
                if ($exception->getCode() === 429) {
                    $this->logger->warning("> [TorrentioClient] Rate limit exceeded");
                    return null;
                }
            }

            $this->logger->error("> [TorrentioClient] Request error: " . $response->getStatusCode() . " - " . $response->getBody()->getContents());
            return [];
        });

        if (true === $parseResults) {
            return $this->parse($results);
        }

        return $results;
    }

    public function fetchEpisodeResults(string $imdbId, int $season, int $episode, bool $parseResults = true): array
    {
        $cacheKey = "torrentio.$imdbId.$season.$episode";
        $results = $this->cache->get($cacheKey, function (ItemInterface $item) use ($imdbId, $season, $episode) {
            $item->expiresAt(Carbon::now()->addHour()->setMinute(0)->setSecond(0));
            $item->tag(['torrentio', 'tvshows', 'torrentio.tvshows', $imdbId, "torrentio.$imdbId", "$imdbId.$season", "torrentio.$imdbId.$season", "$imdbId.$season.$episode", "torrentio.$imdbId.$season.$episode"]);
            try {
                $response = $this->client->get("$this->searchUrl/$imdbId:$season:$episode.json");
                return json_decode(
                    $response->getBody()->getContents(),
                    true
                );
            } catch (\Throwable $exception) {
                if ($exception->getCode() === 429) {
                    $this->logger->warning("> [TorrentioClient] Rate limit exceeded");
                    return null;
                }
            }

            $this->logger->error("> [TorrentioClient] Request error: " . $response->getStatusCode() . " - " . $response->getBody()->getContents());
            return [];
        });

        if (null === $results) {
            throw new TorrentioRateLimitException();
        }

        if (true === $parseResults) {
            return $this->parse($results);
        }

        return $results;
    }

    public function parse(array $data): array
    {
        $results = [];
        foreach ($data['streams'] as $stream) {
            if (!str_starts_with($stream['url'], "https")) {
                continue;
            }

            if (
                array_key_exists('behaviorHints', $stream) &&
                array_key_exists('bingeGroup', $stream['behaviorHints'])
            ) {
                $bingeGroup = $stream['behaviorHints']['bingeGroup'];
            } else {
                $bingeGroup = '-';
            }

            $result = ResultFactory::map(
                $stream['url'],
                $stream['title'],
                $bingeGroup
            );

            $results[] = $result;
        }

        return $results;
    }
}
