<?php

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

use App\Domain\Manga\Application\Command\ImportChapter;
use App\Domain\Manga\Application\CommandHandler\ImportChapterHandler;
use App\Domain\Manga\Domain\Exception\MangaNotFoundException;
use App\Domain\Manga\Domain\Exception\ChapterNotFoundException;
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\InMemoryChapterRepository;
use App\Tests\Domain\Manga\Adapter\InMemoryMangaRepository;
use App\Tests\Domain\Manga\Adapter\InMemoryPathManager;
use PHPUnit\Framework\TestCase;

class ImportChapterHandlerTest extends TestCase
{
    private InMemoryMangaRepository $mangaRepository;
    private InMemoryChapterRepository $chapterRepository;
    private InMemoryPathManager $pathManager;
    private ImportChapterHandler $handler;

    protected function setUp(): void
    {
        $this->mangaRepository = new InMemoryMangaRepository();
        $this->chapterRepository = new InMemoryChapterRepository();
        $this->pathManager = new InMemoryPathManager();
        $this->handler = new ImportChapterHandler(
            $this->mangaRepository,
            $this->chapterRepository,
            $this->pathManager
        );
    }

    public function test_it_throws_exception_when_chapter_not_found(): void
    {
        // Arrange
        $mangaId = 'manga-123';
        $manga = new Manga(
            new MangaId($mangaId),
            new MangaTitle('One Piece'),
            new MangaSlug('one-piece'),
            'Description',
            'Eiichiro Oda',
            1997,
            ['action', 'adventure'],
            'ongoing'
        );
        $this->mangaRepository->save($manga);

        $cbzBinary = $this->createValidCbzBinary();
        $command = new ImportChapter(
            mangaId: $mangaId,
            chapterNumber: 1.5,
            fileBinary: $cbzBinary
        );

        // Assert
        $this->expectException(ChapterNotFoundException::class);

        // Act
        $this->handler->handle($command);
    }

    public function test_it_updates_existing_chapter_with_new_cbz(): void
    {
        // Arrange
        $mangaId = 'manga-123';
        $manga = new Manga(
            new MangaId($mangaId),
            new MangaTitle('One Piece'),
            new MangaSlug('one-piece'),
            'Description',
            'Eiichiro Oda',
            1997,
            ['action', 'adventure'],
            'ongoing'
        );
        $this->mangaRepository->save($manga);

        // Create an existing chapter without CBZ
        $existingChapter = new Chapter(
            new ChapterId('chapter-123'),
            $mangaId,
            1.5,
            'Chapter 1.5',
            1,
            true,
            null
        );
        $this->chapterRepository->save($existingChapter);

        // Import the same chapter with CBZ
        $cbzBinary = $this->createValidCbzBinary();
        $command = new ImportChapter(
            mangaId: $mangaId,
            chapterNumber: 1.5,
            fileBinary: $cbzBinary
        );

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

        // Assert
        $chapters = $this->chapterRepository->getAll();
        $this->assertCount(1, $chapters); // Still only one chapter

        $updatedChapter = $chapters[0];
        $this->assertEquals('chapter-123', $updatedChapter->getId());
        $this->assertEquals($mangaId, $updatedChapter->getMangaId());
        $this->assertEquals(1.5, $updatedChapter->getNumber());
        $this->assertEquals('Chapter 1.5', $updatedChapter->getTitle()); // Title preserved
        $this->assertEquals(1, $updatedChapter->getVolume()); // Volume preserved
        $this->assertTrue($updatedChapter->isVisible());
        $this->assertTrue($updatedChapter->isAvailable()); // Now has CBZ
        $this->assertStringContainsString('_vol1_ch1.5.cbz', $updatedChapter->getCbzPath());
    }

    public function test_it_throws_exception_when_manga_not_found(): void
    {
        // Arrange
        $cbzBinary = $this->createValidCbzBinary();
        $command = new ImportChapter(
            mangaId: 'non-existent-manga',
            chapterNumber: 1.0,
            fileBinary: $cbzBinary
        );

        // Assert
        $this->expectException(MangaNotFoundException::class);

        // Act
        $this->handler->handle($command);
    }

    public function test_it_throws_exception_when_file_is_not_valid_cbz(): void
    {
        // Arrange
        $mangaId = 'manga-123';
        $manga = new Manga(
            new MangaId($mangaId),
            new MangaTitle('One Piece'),
            new MangaSlug('one-piece'),
            'Description',
            'Eiichiro Oda',
            1997,
            ['action', 'adventure'],
            'ongoing'
        );
        $this->mangaRepository->save($manga);

        $invalidBinary = 'This is not a CBZ file';
        $command = new ImportChapter(
            mangaId: $mangaId,
            chapterNumber: 1.0,
            fileBinary: $invalidBinary
        );

        // Assert
        $this->expectException(\InvalidArgumentException::class);
        $this->expectExceptionMessage('The provided file is not a valid CBZ file');

        // Act
        $this->handler->handle($command);
    }

    /**
     * Create a minimal valid CBZ (ZIP) binary for testing
     */
    private function createValidCbzBinary(): string
    {
        $tmpFile = tempnam(sys_get_temp_dir(), 'cbz');

        // Delete the empty file created by tempnam
        unlink($tmpFile);

        $zip = new \ZipArchive();

        // Create a new ZIP archive (avoid opening empty file)
        if ($zip->open($tmpFile, \ZipArchive::CREATE | \ZipArchive::OVERWRITE) !== true) {
            throw new \RuntimeException('Cannot create test CBZ file');
        }

        // Add a dummy image file to the ZIP
        $zip->addFromString('image1.jpg', 'fake-image-data');
        $zip->close();

        $binaryContent = file_get_contents($tmpFile);
        unlink($tmpFile);

        return $binaryContent;
    }
}
