<?php

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

use App\Domain\Manga\Application\Command\EditManga;
use App\Domain\Manga\Application\CommandHandler\EditMangaHandler;
use App\Domain\Manga\Domain\Exception\MangaNotFoundException;
use App\Domain\Manga\Domain\Model\Manga;
use App\Domain\Manga\Domain\Model\ValueObject\ExternalId;
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 EditMangaHandlerTest extends TestCase
{
    private InMemoryMangaRepository $repository;
    private EditMangaHandler $handler;

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

    public function testHandleEditMangaSuccess(): void
    {
        // Given - Create a manga first
        $manga = new Manga(
            new MangaId('manga-123'),
            new MangaTitle('One Piece'),
            new MangaSlug('one-piece'),
            'Original description',
            'Eiichiro Oda',
            1997,
            ['action', 'adventure'],
            'ongoing',
            new ExternalId('external-123'),
            'http://example.com/image.jpg',
            4.5,
            null,
            ['op']
        );

        $this->repository->save($manga);

        // When - Edit the manga
        $command = new EditManga(
            id: 'manga-123',
            title: 'One Piece Updated',
            description: 'Updated description',
            author: 'Eiichiro Oda Updated',
            publicationYear: 1998,
            genres: ['action', 'adventure', 'comedy'],
            status: 'completed',
            rating: 4.8,
            alternativeSlugs: ['onepiece', 'op', 'luffy']
        );

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

        // Then - Verify the manga was updated
        $updatedManga = $this->repository->findById('manga-123');
        $this->assertNotNull($updatedManga);
        $this->assertEquals('One Piece Updated', $updatedManga->getTitle()->getValue());
        $this->assertEquals('Updated description', $updatedManga->getDescription());
        $this->assertEquals('Eiichiro Oda Updated', $updatedManga->getAuthor());
        $this->assertEquals(1998, $updatedManga->getPublicationYear());
        $this->assertEquals(['action', 'adventure', 'comedy'], $updatedManga->getGenres());
        $this->assertEquals('completed', $updatedManga->getStatus());
        $this->assertEquals(4.8, $updatedManga->getRating());
        $this->assertEquals(['onepiece', 'op', 'luffy'], $updatedManga->getAlternativeSlugs());
    }

    public function testHandleEditMangaPartialUpdate(): void
    {
        // Given - Create a manga first
        $manga = new Manga(
            new MangaId('manga-123'),
            new MangaTitle('One Piece'),
            new MangaSlug('one-piece'),
            'Original description',
            'Eiichiro Oda',
            1997,
            ['action', 'adventure'],
            'ongoing',
            new ExternalId('external-123'),
            'http://example.com/image.jpg',
            4.5,
            null,
            ['op']
        );

        $this->repository->save($manga);

        // When - Edit only title and rating
        $command = new EditManga(
            id: 'manga-123',
            title: 'One Piece - Updated Title Only',
            rating: 4.9
        );

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

        // Then - Verify only specified fields were updated
        $updatedManga = $this->repository->findById('manga-123');
        $this->assertNotNull($updatedManga);
        $this->assertEquals('One Piece - Updated Title Only', $updatedManga->getTitle()->getValue());
        $this->assertEquals(4.9, $updatedManga->getRating());
        // Original values should remain unchanged
        $this->assertEquals('Original description', $updatedManga->getDescription());
        $this->assertEquals('Eiichiro Oda', $updatedManga->getAuthor());
        $this->assertEquals(1997, $updatedManga->getPublicationYear());
        $this->assertEquals(['action', 'adventure'], $updatedManga->getGenres());
        $this->assertEquals('ongoing', $updatedManga->getStatus());
        $this->assertEquals(['op'], $updatedManga->getAlternativeSlugs());
    }

    public function testHandleEditMangaNotFound(): void
    {
        // When - Try to edit non-existent manga
        $command = new EditManga(
            id: 'non-existent-id',
            title: 'Updated Title'
        );

        // Then
        $this->expectException(MangaNotFoundException::class);
        $this->handler->handle($command);
    }

    public function testHandleEditAlternativeSlugsSeparately(): void
    {
        // Given - Create a manga first
        $manga = new Manga(
            new MangaId('manga-123'),
            new MangaTitle('One Piece'),
            new MangaSlug('one-piece'),
            'Original description',
            'Eiichiro Oda',
            1997,
            ['action', 'adventure'],
            'ongoing',
            new ExternalId('external-123'),
            'http://example.com/image.jpg',
            4.5,
            null,
            ['op', 'onepiece']
        );

        $this->repository->save($manga);

        // When - Edit only alternativeSlugs
        $command = new EditManga(
            id: 'manga-123',
            alternativeSlugs: ['luffy-manga', 'pirate-king', 'one-piece-manga']
        );

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

        // Then - Verify only alternativeSlugs was updated
        $updatedManga = $this->repository->findById('manga-123');
        $this->assertNotNull($updatedManga);
        $this->assertEquals(['luffy-manga', 'pirate-king', 'one-piece-manga'], $updatedManga->getAlternativeSlugs());
        // All other fields should remain unchanged
        $this->assertEquals('One Piece', $updatedManga->getTitle()->getValue());
        $this->assertEquals('Original description', $updatedManga->getDescription());
    }
}
