<?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;

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()
        ]);

        $pagesDirectory = $chapter->getPagesDirectory();
        if ($pagesDirectory && is_dir($pagesDirectory)) {
            return $this->getPagesFromDirectory($chapterId, $pagesDirectory, $page, $itemsPerPage);
        }

        return [];
    }

    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),
            mangaId: (string) $chapter->getManga()->getId(),
            mangaTitle: $chapter->getManga()->getTitle(),
            number: $chapter->getNumber(),
            chapterTitle: $chapter->getTitle(),
            cbzPath: $chapter->getCbzPath(),
            volume: $chapter->getVolume(),
            totalPages: 0,
            isVisible: $chapter->isVisible(),
            createdAt: new \DateTimeImmutable(),
            pagesDirectory: $chapter->getPagesDirectory(),
        );
    }

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

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

        $pagesDirectory = $chapter->getPagesDirectory();
        if ($pagesDirectory && is_dir($pagesDirectory)) {
            return count($this->getImageFiles($pagesDirectory));
        }

        return 0;
    }

    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.pagesDirectory IS NOT NULL OR 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.pagesDirectory IS NOT NULL OR 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;
    }

    private function getImageFiles(string $pagesDirectory): array
    {
        $files = glob($pagesDirectory . '/*.{jpg,jpeg,png,webp,gif}', GLOB_BRACE) ?: [];
        sort($files);

        return $files;
    }

    private function getPagesFromDirectory(ChapterId $chapterId, string $pagesDirectory, int $page, int $itemsPerPage): array
    {
        $files = $this->getImageFiles($pagesDirectory);
        $start = max(0, ($page - 1) * $itemsPerPage);
        $end = min($start + $itemsPerPage, count($files));
        $pages = [];

        for ($i = $start; $i < $end; $i++) {
            $imageSize = @getimagesize($files[$i]);
            if ($imageSize === false) {
                continue;
            }

            $pages[] = new Page(
                basename($files[$i]),
                new PageNumber($i + 1),
                sprintf('/images/pages/%s/%s', basename($pagesDirectory), basename($files[$i])),
                $imageSize[0],
                $imageSize[1]
            );
        }

        return $pages;
    }
}
