<?php

namespace App\Tests\Domain\Manga\Application\QueryHandler;

use App\Domain\Manga\Application\Query\GetMangaChapters;
use App\Domain\Manga\Application\QueryHandler\GetMangaChaptersHandler;
use App\Domain\Manga\Domain\Exception\MangaNotFoundException;
use App\Domain\Manga\Domain\Model\Chapter;
use App\Domain\Manga\Domain\Model\Manga;
use App\Domain\Manga\Domain\Model\ValueObject\ChapterId;
use App\Domain\Manga\Domain\Model\ValueObject\MangaId;
use App\Domain\Manga\Domain\Model\ValueObject\MangaSlug;
use App\Domain\Manga\Domain\Model\ValueObject\MangaTitle;
use App\Tests\Domain\Manga\Adapter\InMemoryMangaRepository;
use PHPUnit\Framework\TestCase;

class GetMangaChaptersHandlerTest extends TestCase
{
    private InMemoryMangaRepository $repository;
    private GetMangaChaptersHandler $handler;

    protected function setUp(): void
    {
        $this->repository = new InMemoryMangaRepository();
        $this->handler = new GetMangaChaptersHandler($this->repository);
    }

    public function testHandleThrowsExceptionWhenMangaNotFound(): void
    {
        $this->expectException(MangaNotFoundException::class);
        
        $query = new GetMangaChapters('non-existent-id');
        $this->handler->handle($query);
    }

    public function testHandleReturnsEmptyListWhenNoChapters(): void
    {
        // Arrange
        $this->givenMangaExists('123');
        
        // Act
        $query = new GetMangaChapters('123');
        $response = $this->handler->handle($query);
        
        // Assert
        $this->assertEmpty($response->chapters);
        $this->assertEquals(0, $response->total);
        $this->assertEquals(1, $response->page);
        $this->assertEquals(20, $response->limit);
        $this->assertFalse($response->hasNextPage());
        $this->assertFalse($response->hasPreviousPage());
    }

    public function testHandleReturnsPaginatedChapters(): void
    {
        // Arrange
        $this->givenMangaExistsWithChapters('123', 25);
        
        // Act
        $query = new GetMangaChapters('123', page: 2, limit: 10);
        $response = $this->handler->handle($query);
    
        // Assert
        $this->assertCount(10, $response->chapters);
        $this->assertEquals(25, $response->total);
        $this->assertEquals(2, $response->page);
        $this->assertEquals(10, $response->limit);
        $this->assertTrue($response->hasNextPage());
        $this->assertTrue($response->hasPreviousPage());
    }

    public function testGroupsVolumeChaptersWithSharedPagesDirectory(): void
    {
        // Arrange
        $this->givenMangaExists('1');
        $sharedDir = '/manga/vol1/';
        foreach ([1, 2, 3] as $num) {
            $this->repository->addChapter('1', new Chapter(
                id: new ChapterId((string) $num),
                mangaId: new MangaId('1'),
                number: (float) $num,
                title: null,
                volume: 1,
                isVisible: true,
                pagesDirectory: $sharedDir,
            ));
        }

        // Act
        $response = $this->handler->handle(new GetMangaChapters('1'));

        // Assert
        $this->assertCount(1, $response->chapters);
        $this->assertEquals(1, $response->total);
        $item = $response->chapters[0];
        $this->assertTrue($item->isVolumeGroup);
        $this->assertEquals('1-3', $item->volumeChaptersRange);
        $this->assertEquals(3, $item->volumeChapterCount);
        $this->assertEquals(1, $item->volume);
        $this->assertEquals(1.0, $item->number);
    }

    public function testGroupsSingleVolumeChapter(): void
    {
        // Arrange
        $this->givenMangaExists('1');
        $this->repository->addChapter('1', new Chapter(
            id: new ChapterId('10'),
            mangaId: new MangaId('1'),
            number: 5.0,
            title: null,
            volume: 2,
            isVisible: true,
            pagesDirectory: '/manga/vol2/',
        ));

        // Act
        $response = $this->handler->handle(new GetMangaChapters('1'));

        // Assert
        $this->assertCount(1, $response->chapters);
        $item = $response->chapters[0];
        $this->assertTrue($item->isVolumeGroup);
        $this->assertEquals('5', $item->volumeChaptersRange);
        $this->assertEquals(1, $item->volumeChapterCount);
    }

    public function testDoesNotGroupChaptersWithDistinctPagesDirectory(): void
    {
        // Arrange — 3 chapitres scrapés avec pagesDirectory distinctes, pas de volume
        $this->givenMangaExists('1');
        foreach ([1, 2, 3] as $num) {
            $this->repository->addChapter('1', new Chapter(
                id: new ChapterId((string) $num),
                mangaId: new MangaId('1'),
                number: (float) $num,
                title: null,
                volume: null,
                isVisible: true,
                pagesDirectory: '/manga/ch' . $num . '/',
            ));
        }

        // Act
        $response = $this->handler->handle(new GetMangaChapters('1'));

        // Assert — 3 items distincts, aucun groupe
        $this->assertCount(3, $response->chapters);
        $this->assertEquals(3, $response->total);
        foreach ($response->chapters as $item) {
            $this->assertFalse($item->isVolumeGroup);
        }
    }

    public function testMixedNormalAndVolumeChapters(): void
    {
        // Arrange — 2 chapitres scrapés + 3 chapitres de volume importé
        $this->givenMangaExists('1');

        // Chapitres scrapés (pagesDirectory individuel, pas de volume)
        foreach ([1, 2] as $num) {
            $this->repository->addChapter('1', new Chapter(
                id: new ChapterId((string) $num),
                mangaId: new MangaId('1'),
                number: (float) $num,
                title: null,
                volume: null,
                isVisible: true,
                pagesDirectory: '/manga/ch' . $num . '/',
            ));
        }

        // Volume importé — 3 chapitres avec même pagesDirectory
        $sharedDir = '/manga/vol1/';
        foreach ([3, 4, 5] as $num) {
            $this->repository->addChapter('1', new Chapter(
                id: new ChapterId((string) ($num + 10)),
                mangaId: new MangaId('1'),
                number: (float) $num,
                title: null,
                volume: 1,
                isVisible: true,
                pagesDirectory: $sharedDir,
            ));
        }

        // Act
        $response = $this->handler->handle(new GetMangaChapters('1', sortOrder: 'asc'));

        // Assert — 2 chapitres normaux + 1 groupe = 3 items
        $this->assertCount(3, $response->chapters);
        $this->assertEquals(3, $response->total);

        // Les 2 premiers sont des chapitres normaux
        $this->assertFalse($response->chapters[0]->isVolumeGroup);
        $this->assertFalse($response->chapters[1]->isVolumeGroup);

        // Le 3e est un groupe de volume
        $volumeItem = $response->chapters[2];
        $this->assertTrue($volumeItem->isVolumeGroup);
        $this->assertEquals('3-5', $volumeItem->volumeChaptersRange);
        $this->assertEquals(3, $volumeItem->volumeChapterCount);
    }

    protected function tearDown(): void
    {
        $this->repository->clear();
    }

    private function givenMangaExists(string $id): void
    {
        $manga = $this->createManga($id);
        $this->repository->save($manga);
    }

    private function givenMangaExistsWithChapters(string $id, int $chapterCount): void
    {
        $this->givenMangaExists($id);
        // Note: We'll need to implement this in InMemoryMangaRepository
        $this->repository->addChaptersToManga($id, $chapterCount);
    }

    private function createManga(string $id): Manga
    {
        return new Manga(
            id: new MangaId($id),
            title: new MangaTitle('Test Manga'),
            slug: new MangaSlug('test-manga'),
            description: 'This is a test manga',
            author: 'Test Author',
            publicationYear: 2024,
            genres: ['Action', 'Adventure'],
            status: 'Ongoing',
            externalId: null,
            imageUrl: null,
            rating: null
        );
    }
} 