<?php

namespace App\Domain\Manga\Application\QueryHandler;

use App\Domain\Manga\Application\Query\GetMangaChapters;
use App\Domain\Manga\Application\Response\ChapterListResponse;
use App\Domain\Manga\Application\Response\ChapterResponse;
use App\Domain\Manga\Domain\Contract\Repository\MangaRepositoryInterface;
use App\Domain\Manga\Domain\Exception\MangaNotFoundException;
use App\Domain\Manga\Domain\Model\Chapter;

readonly class GetMangaChaptersHandler
{
    public function __construct(
        private MangaRepositoryInterface $mangaRepository
    ) {
    }

    public function handle(GetMangaChapters $query): ChapterListResponse
    {
        $manga = $this->mangaRepository->findById($query->mangaId);
        if (!$manga) {
            throw new MangaNotFoundException();
        }

        $allChapters = $this->mangaRepository->findAllChapters(
            mangaId: $query->mangaId,
            sortOrder: 'asc'
        );

        $grouped = $this->groupChapters($allChapters);

        if ($query->sortOrder === 'desc') {
            usort($grouped, fn (ChapterResponse $a, ChapterResponse $b) => $b->number <=> $a->number);
        }

        $total = count($grouped);
        $offset = ($query->page - 1) * $query->limit;
        $paginatedChapters = array_slice($grouped, $offset, $query->limit);

        return new ChapterListResponse(
            chapters: $paginatedChapters,
            total: $total,
            page: $query->page,
            limit: $query->limit
        );
    }

    /** @param Chapter[] $chapters */
    private function groupChapters(array $chapters): array
    {
        $result = [];
        $currentGroup = [];
        $currentPagesDir = null;
        $currentVolume = null;

        foreach ($chapters as $chapter) {
            $pagesDir = $chapter->getPagesDirectory();
            $volume = $chapter->getVolume();

            if ($pagesDir !== null && $volume !== null) {
                if ($pagesDir === $currentPagesDir && $volume === $currentVolume) {
                    $currentGroup[] = $chapter;
                } else {
                    if (!empty($currentGroup)) {
                        $result[] = $this->buildVolumeGroupResponse($currentGroup);
                    }
                    $currentGroup = [$chapter];
                    $currentPagesDir = $pagesDir;
                    $currentVolume = $volume;
                }
            } else {
                if (!empty($currentGroup)) {
                    $result[] = $this->buildVolumeGroupResponse($currentGroup);
                    $currentGroup = [];
                    $currentPagesDir = null;
                    $currentVolume = null;
                }
                $result[] = new ChapterResponse(
                    id: $chapter->getId(),
                    number: $chapter->getNumber(),
                    title: $chapter->getTitle(),
                    volume: $chapter->getVolume(),
                    isVisible: $chapter->isVisible(),
                    pagesDirectory: $chapter->getPagesDirectory(),
                    createdAt: $chapter->getCreatedAt()->format(\DateTimeInterface::RFC3339)
                );
            }
        }

        if (!empty($currentGroup)) {
            $result[] = $this->buildVolumeGroupResponse($currentGroup);
        }

        return $result;
    }

    /** @param Chapter[] $group */
    private function buildVolumeGroupResponse(array $group): ChapterResponse
    {
        $first = $group[0];
        $numbers = array_map(fn (Chapter $c) => $c->getNumber(), $group);
        $min = min($numbers);
        $max = max($numbers);

        $fmt = fn (float $n) => $n == (int) $n ? (string) (int) $n : (string) $n;
        $range = count($group) > 1 ? $fmt($min) . '-' . $fmt($max) : $fmt($min);

        return new ChapterResponse(
            id: $first->getId(),
            number: $first->getNumber(),
            title: $first->getTitle(),
            volume: $first->getVolume(),
            isVisible: $first->isVisible(),
            pagesDirectory: $first->getPagesDirectory(),
            createdAt: $first->getCreatedAt()->format(\DateTimeInterface::RFC3339),
            isVolumeGroup: true,
            volumeChaptersRange: $range,
            volumeChapterCount: count($group)
        );
    }
}
