<?php

namespace App\Tests\Monitor;

use App\Base\Service\MediaFiles;
use App\Monitor\Action\Command\MonitorTvShowCommand;
use App\Monitor\Action\Handler\MonitorTvEpisodeHandler;
use App\Monitor\Action\Handler\MonitorTvShowHandler;
use App\Monitor\Action\Result\MonitorTvShowResult;
use App\Monitor\Framework\Entity\Monitor;
use App\Monitor\Framework\Repository\MonitorRepository;
use App\Tmdb\TmdbClient;
use Doctrine\ORM\EntityManagerInterface;
use PHPUnit\Framework\TestCase;
use Psr\Log\LoggerInterface;

class MonitorTvShowHandlerTest extends TestCase
{
    private MonitorTvShowHandler $handler;
    private MonitorRepository $monitorRepository;
    private EntityManagerInterface $entityManager;
    private MonitorTvEpisodeHandler $episodeHandler;
    private MediaFiles $mediaFiles;
    private LoggerInterface $logger;
    private TmdbClient $tmdb;

    protected function setUp(): void
    {
        $this->monitorRepository = $this->createMock(MonitorRepository::class);
        $this->entityManager = $this->createMock(EntityManagerInterface::class);
        $this->episodeHandler = $this->createMock(MonitorTvEpisodeHandler::class);
        $this->mediaFiles = $this->createMock(MediaFiles::class);
        $this->logger = $this->createMock(LoggerInterface::class);
        $this->tmdb = $this->createMock(TmdbClient::class);

        $this->handler = new MonitorTvShowHandler(
            $this->monitorRepository,
            $this->entityManager,
            $this->episodeHandler,
            $this->mediaFiles,
            $this->logger,
            $this->tmdb
        );
    }

    public function testEpisodeExists(): void
    {
        // Arrange
        $monitor = $this->createMock(Monitor::class);
        $monitor->method('getId')->willReturn(1);
        $monitor->method('getTmdbId')->willReturn('63770');
        $monitor->method('getSeason')->willReturn(10);
        $monitor->method('getTitle')->willReturn('The Late Show with Stephen Colbert');

        $this->monitorRepository->expects($this->once())
            ->method('find')
            ->with(1)
            ->willReturn($monitor);

        $this->tmdb->expects($this->once())
            ->method('seasonDetails')
            ->with(63770, 10)
            ->willReturn((object)['episodes' => [$this->getTmdbEpisode()]]);

        $this->mediaFiles->expects($this->once())
            ->method('findEpisodes')
            ->willReturn($this->getDownloadedEpisodes());

        // Act
        $command = new MonitorTvShowCommand(1);
        $result = $this->handler->handle($command);

        // Assert
        $this->assertInstanceOf(MonitorTvShowResult::class, $result);
        $this->assertEquals('OK', $result->status);
        $this->assertTrue(isset($result->result['monitor']));
        $this->assertSame($monitor, $result->result['monitor']);
    }

    public function testEpisodeDoesNotExist(): void
    {
        // Arrange
        $monitor = $this->createMock(Monitor::class);
        $monitor->method('getId')->willReturn(1);
        $monitor->method('getTmdbId')->willReturn(63770);
        $monitor->method('getSeason')->willReturn(10);
        $monitor->method('getTitle')->willReturn('The Late Show with Stephen Colbert');

        $this->monitorRepository->expects($this->once())
            ->method('find')
            ->with(1)
            ->willReturn($monitor);

        $this->tmdb->expects($this->once())
            ->method('seasonDetails')
            ->with(63770, 10)
            ->willReturn((object)['episodes' => []]);

        $this->mediaFiles->expects($this->once())
            ->method('findEpisodes')
            ->willReturn([]);

        // Act
        $command = new MonitorTvShowCommand(1);
        $result = $this->handler->handle($command);

        // Assert
        $this->assertInstanceOf(MonitorTvShowResult::class, $result);
        $this->assertEquals('OK', $result->status);
        $this->assertTrue(isset($result->result['monitor']));
        $this->assertSame($monitor, $result->result['monitor']);
    }

    public function testHandleWithInvalidMonitorId(): void
    {
        // Arrange
        $this->monitorRepository->expects($this->once())
            ->method('find')
            ->with(999)
            ->willReturn(null);

        // Act & Assert
        $this->expectException(\RuntimeException::class);
        $this->expectExceptionMessage('Monitor not found');

        $command = new MonitorTvShowCommand(999);
        $this->handler->handle($command);
    }

    private function getTmdbEpisode(): array
    {
        return [
            "air_date" => "2016-05-13",
            "episode_number" => 142,
            "episode_type" => "standard",
            "id" => 1192242,
            "name" => "Matt Bomer, Zach Woods, Nick Griffin",
            "overview" => "",
            "production_code" => "",
            "runtime" => 45,
            "season_number" => 1,
            "show_id" => 63770,
            "still_path" => null,
            "vote_average" => 0.0,
            "vote_count" => 0,
            "crew" => [],
            "guest_stars" => [],
            "poster" => null,
        ];
    }

    private function getDownloadedEpisodes(): array
    {
        $episode1 = (object)[
            "season" => 10,
            "episode" => 142,
            "resolution" => "1080p",
            "codec" => "h264",
            "group" => "jebaited.mkv",
            "container" => "mkv",
            "title" => "42 stephen colbert 2025 07 21 sandra oh",
            "episodeName" => "E1 web",
        ];

        $episode2 = (object)[
            'season' => 10,
            'episode' => 142,
            'resolution' => '1080p',
            'codec' => 'x265',
            'group' => 'MeGusta[EZTVx.to].mkv',
            'container' => 'mkv',
            'title' => '42 Stephen Colbert 2025 07 21 Sandra Oh',
            'episodeName' => 'E1 HEVC'
        ];

        return [$episode1, $episode2];
    }
}
