<?php

namespace App\Tests\Domain\Scraping\Application\CommandHandler;

use App\Domain\Scraping\Application\Command\ScrapeChapter;
use App\Domain\Scraping\Application\CommandHandler\ScrapeChapterHandler;
use App\Domain\Scraping\Domain\Event\ChapterScrapingFailed;
use App\Domain\Scraping\Domain\Model\Chapter;
use App\Domain\Shared\Domain\Event\ChapterScraped;
use App\Tests\Domain\Scraping\Adapter\InMemoryChapterRepository;
use App\Tests\Domain\Scraping\Adapter\InMemoryEventBus;
use App\Tests\Domain\Scraping\Adapter\InMemoryImageDownloader;
use App\Tests\Domain\Scraping\Adapter\InMemoryImageStorage;
use App\Tests\Domain\Scraping\Adapter\InMemoryMangaRepository;
use App\Tests\Domain\Scraping\Adapter\InMemoryScraperFactory;
use App\Tests\Domain\Scraping\Adapter\InMemorySourceRepository;
use App\Tests\Domain\Shared\Adapter\InMemoryJobRepository;
use Doctrine\ORM\EntityManagerInterface;
use PHPUnit\Framework\MockObject\MockObject;
use PHPUnit\Framework\TestCase;

class ScrapeChapterHandlerTest extends TestCase
{
    private InMemoryScraperFactory $scraperFactory;
    private InMemoryImageDownloader $imageDownloader;
    private InMemoryImageStorage $imageStorage;
    private InMemoryJobRepository $jobRepository;
    private InMemoryChapterRepository $chapterRepository;
    private InMemoryMangaRepository $mangaRepository;
    private InMemorySourceRepository $sourceRepository;
    private InMemoryEventBus $eventBus;
    private EntityManagerInterface|MockObject $entityManager;
    private ScrapeChapterHandler $handler;

    protected function setUp(): void
    {
        $this->scraperFactory = new InMemoryScraperFactory();
        $this->imageDownloader = new InMemoryImageDownloader();
        $this->imageStorage = new InMemoryImageStorage();
        $this->jobRepository = new InMemoryJobRepository();
        $this->chapterRepository = new InMemoryChapterRepository();
        $this->mangaRepository = new InMemoryMangaRepository();
        $this->sourceRepository = new InMemorySourceRepository();
        $this->eventBus = new InMemoryEventBus();
        $this->entityManager = $this->createMock(EntityManagerInterface::class);

        $this->entityManager->method('beginTransaction')->willReturn(null);
        $this->entityManager->method('commit')->willReturn(null);
        $this->entityManager->method('rollback')->willReturn(null);

        $this->chapterRepository->save(new Chapter(
            id: '1',
            mangaId: 'test-manga',
            chapterNumber: 2,
            volumeNumber: 1,
        ));

        $this->handler = new ScrapeChapterHandler(
            $this->scraperFactory,
            $this->imageDownloader,
            $this->imageStorage,
            $this->jobRepository,
            $this->chapterRepository,
            $this->mangaRepository,
            $this->sourceRepository,
            $this->eventBus,
            $this->entityManager
        );
    }

    public function testHandleSuccessfully(): void
    {
        $command = new ScrapeChapter(
            chapterId: '1'
        );

        $this->handler->handle($command);

        $job = $this->jobRepository->findByType('scraping_job');
        $this->assertCount(1, $job);
        $job = array_values($job)[0];

        $dispatchedMessages = $this->eventBus->getDispatchedMessages();
        $this->assertCount(1, $dispatchedMessages);
        $this->assertInstanceOf(ChapterScraped::class, $dispatchedMessages[0]);
        $this->assertEquals($job->id, $dispatchedMessages[0]->getJobId());
        $this->assertEquals('1', $dispatchedMessages[0]->chapterId);
        $this->assertEquals('/fake/pages/1', $dispatchedMessages[0]->pagesDirectory);
        $this->assertNotNull($this->imageStorage->stored['1'] ?? null);
    }

    protected function tearDown(): void
    {
        $this->jobRepository->clear();
        $this->chapterRepository->clear();
        $this->mangaRepository->clear();
        $this->sourceRepository->clear();
    }
}
