<?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\Event\ChapterScrapingStarted;
use App\Domain\Scraping\Domain\Model\Chapter;
use App\Domain\Scraping\Domain\Model\ScrapingJob;
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 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 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->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,
        );
    }

    public function testHandleSuccessfully(): void
    {
        $jobId = 'test-job-id';
        $job = new ScrapingJob($jobId, 'test-manga', 2);
        $this->jobRepository->save($job);

        $command = new ScrapeChapter(chapterId: '1', jobId: $jobId);
        $this->handler->handle($command);

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

        $dispatchedMessages = $this->eventBus->getDispatchedMessages();
        $this->assertCount(2, $dispatchedMessages);

        $this->assertInstanceOf(ChapterScrapingStarted::class, $dispatchedMessages[0]);
        $this->assertSame(2.0, $dispatchedMessages[0]->getChapterNumber());

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

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