<?php

declare(strict_types=1);

namespace App\Domain\Reader\Infrastructure\Persistence;

use App\Domain\Reader\Domain\Contract\Repository\ChapterRepositoryInterface;
use App\Domain\Reader\Domain\Exception\ChapterNotFoundException;
use App\Domain\Reader\Domain\Model\ChapterContext;
use App\Domain\Reader\Domain\Model\Page;
use App\Domain\Reader\Domain\ValueObject\ChapterId;
use App\Domain\Reader\Domain\ValueObject\PageNumber;
use App\Entity\Chapter as ChapterEntity;
use Doctrine\ORM\EntityManagerInterface;
use ZipArchive;
use App\Domain\Reader\Domain\Exception\PageNotFoundException;
use App\Domain\Reader\Domain\Model\PageContent;

readonly class LegacyChapterRepository implements ChapterRepositoryInterface
{
    public function __construct(
        private EntityManagerInterface $entityManager
    ) {}

    public function getPagesForChapter(ChapterId $chapterId, int $page = 1, int $itemsPerPage = 20): array
    {
        $chapter = $this->entityManager->getRepository(ChapterEntity::class)->findOneBy([
            'id' => $chapterId->getValue()
        ]);

        $cbzPath = $chapter->getCbzPath();

        if (!$cbzPath) {
            return [];
        }

        $zip = new ZipArchive();
        $zip->open($cbzPath);

        $pages = [];
        $start = ($page - 1) * $itemsPerPage;
        $end = min($start + $itemsPerPage, $zip->numFiles);

        for ($i = $start; $i < $end; $i++) {
            $stat = $zip->statIndex($i);
            if ($stat === false) {
                continue;
            }

            $imageContent = $zip->getFromIndex($i);
            if ($imageContent === false) {
                continue;
            }

            $imageSize = @getimagesizefromstring($imageContent);
            if ($imageSize === false) {
                continue;
            }

            $pages[] = new Page(
                $stat['name'],
                new PageNumber($i + 1),
                sprintf('/api/chapters/%s/pages/%d', $chapterId->getValue(), $i + 1),
                $imageSize[0],
                $imageSize[1]
            );
        }

        $zip->close();
        return $pages;
    }

    public function getChapterContext(ChapterId $chapterId): ChapterContext
    {
        /** @var ChapterEntity $chapter */
        $chapter = $this->entityManager->getRepository(ChapterEntity::class)->findOneBy([
            'id' => $chapterId->getValue()
        ]);

        if (!$chapter) {
            throw ChapterNotFoundException::forChapter($chapterId);
        }

        return new ChapterContext(
            id: $chapterId,
            previousChapterId: $this->getPreviousChapterId($chapterId),
            nextChapterId: $this->getNextChapterId($chapterId),
            mangaTitle: $chapter->getManga()->getTitle(),
            number: $chapter->getNumber(),
            chapterTitle: $chapter->getTitle(),
            cbzPath: $chapter->getCbzPath(),
            volume: $chapter->getVolume(),
            totalPages: 0,
            isVisible: $chapter->isVisible(),
            createdAt: new \DateTimeImmutable()
        );
    }

    public function getTotalPagesForChapter(ChapterId $chapterId): int
    {
        $chapter = $this->entityManager->getRepository(ChapterEntity::class)->findOneBy([
            'id' => $chapterId->getValue()
        ]);

        if (!$chapter) {
            throw ChapterNotFoundException::forChapter($chapterId);
        }

        $cbzPath = $chapter->getCbzPath();

        if (!$cbzPath) {
            return 0;
        }

        $zip = new ZipArchive();
        $zip->open($cbzPath);
        return $zip->numFiles;
    }

    public function getPreviousChapterId(ChapterId $chapterId): ?ChapterId
    {
        $currentChapter = $this->entityManager->getRepository(ChapterEntity::class)->findOneBy([
            'id' => $chapterId->getValue()
        ]);

        $qb = $this->entityManager->createQueryBuilder();
        $qb->select('c')
            ->from(ChapterEntity::class, 'c')
            ->where('c.manga = :manga')
            ->andWhere('c.number < :number')
            ->andWhere('c.visible = true')
            ->andWhere('c.cbzPath IS NOT NULL')
            ->orderBy('c.number', 'DESC')
            ->setMaxResults(1)
            ->setParameters([
                'manga' => $currentChapter->getManga(),
                'number' => $currentChapter->getNumber()
            ]);

        $previousChapter = $qb->getQuery()->getOneOrNullResult();

        return $previousChapter ? new ChapterId((string) $previousChapter->getId()) : null;
    }

    public function getNextChapterId(ChapterId $chapterId): ?ChapterId
    {
        $currentChapter = $this->entityManager->getRepository(ChapterEntity::class)->findOneBy([
            'id' => $chapterId->getValue()
        ]);

        $qb = $this->entityManager->createQueryBuilder();
        $qb->select('c')
            ->from(ChapterEntity::class, 'c')
            ->where('c.manga = :manga')
            ->andWhere('c.number > :number')
            ->andWhere('c.visible = true')
            ->andWhere('c.cbzPath IS NOT NULL')
            ->orderBy('c.number', 'ASC')
            ->setMaxResults(1)
            ->setParameters([
                'manga' => $currentChapter->getManga(),
                'number' => $currentChapter->getNumber()
            ]);

        $nextChapter = $qb->getQuery()->getOneOrNullResult();

        return $nextChapter ? new ChapterId((string) $nextChapter->getId()) : null;
    }

    public function getPageContent(ChapterId $chapterId, PageNumber $pageNumber): PageContent
    {
        $chapter = $this->entityManager->getRepository(ChapterEntity::class)->findOneBy([
            'id' => $chapterId->getValue()
        ]);

        if (!$chapter) {
            throw ChapterNotFoundException::forChapter($chapterId);
        }

        $cbzPath = $chapter->getCbzPath();
        if (!$cbzPath || !file_exists($cbzPath)) {
            throw ChapterNotFoundException::forChapter($chapterId);
        }

        $zip = new ZipArchive();
        $zip->open($cbzPath);

        if ($pageNumber->getValue() > $zip->numFiles) {
            $zip->close();
            throw PageNotFoundException::forPage($chapterId, $pageNumber);
        }

        $index = $pageNumber->getValue() - 1;
        $stat = $zip->statIndex($index);

        if ($stat === false) {
            $zip->close();
            throw PageNotFoundException::forPage($chapterId, $pageNumber);
        }

        $imageContent = $zip->getFromIndex($index);

        if ($imageContent === false) {
            $zip->close();
            throw PageNotFoundException::forPage($chapterId, $pageNumber);
        }

        $imageSize = @getimagesizefromstring($imageContent);

        if ($imageSize === false) {
            $zip->close();
            throw PageNotFoundException::forPage($chapterId, $pageNumber);
        }

        $mimeType = $imageSize['mime'] ?? 'image/jpeg';
        $zip->close();

        return new PageContent(
            $stat['name'],
            $pageNumber,
            base64_encode($imageContent),
            $mimeType,
            $imageSize[0],
            $imageSize[1]
        );
    }
}
