<?php

namespace App\Domain\Manga\Infrastructure\Service;

use App\Domain\Manga\Domain\Contract\Client\MangadexClientInterface;
use App\Domain\Manga\Domain\Contract\Repository\MangaRepositoryInterface;
use App\Domain\Manga\Domain\Contract\Service\ChapterSynchronizationServiceInterface;
use App\Domain\Manga\Domain\Model\Chapter;
use App\Domain\Manga\Domain\Model\Manga;
use App\Domain\Manga\Domain\Model\ValueObject\ChapterId;
use Ramsey\Uuid\Uuid;

readonly class MangadxChapterSynchronizationService implements ChapterSynchronizationServiceInterface
{
    public function __construct(
        private MangadexClientInterface $mangadxClient,
        private MangaRepositoryInterface $mangaRepository
    ) {
    }

    public function synchronizeChapters(Manga $manga): array
    {
        if ($manga->getExternalId() === null) {
            throw new \RuntimeException('Manga has no external ID');
        }

        $externalId = $manga->getExternalId()->getValue();

        $offset = 0;
        $limit = 500;
        $hasMore = true;
        $chaptersByNumber = [];
        $chapterLanguages = []; // Pour stocker la langue de chaque chapitre
        $chapterNumbers = [];

        while ($hasMore) {
            $feed = $this->mangadxClient->getMangaFeed(
                $externalId,
                $offset,
                $limit
            );

            foreach ($feed['data'] as $chapterData) {
                $chapterNumber = (float) $chapterData['attributes']['chapter'];
                $language = $chapterData['attributes']['translatedLanguage'];
                $title = $chapterData['attributes']['title'];

                // Pour les langues autres que français et anglais, on utilise un titre générique
                if (!in_array($language, ['fr', 'en'])) {
                    $title = "Chapter {$chapterNumber}";
                }

                // Définir les règles de priorité des langues (fr > en > autres)
                $shouldReplaceChapter = false;

                if (!isset($chaptersByNumber[(string) $chapterNumber])) {
                    // Si c'est le premier chapitre avec ce numéro qu'on rencontre
                    $shouldReplaceChapter = true;
                    $chapterNumbers[] = $chapterNumber;
                } elseif ($language === 'fr') {
                    // Le français est toujours prioritaire
                    $shouldReplaceChapter = true;
                } elseif ($language === 'en' && $chapterLanguages[(string) $chapterNumber] !== 'fr') {
                    // L'anglais est prioritaire sur les autres langues, sauf le français
                    $shouldReplaceChapter = true;
                }

                if ($shouldReplaceChapter) {
                    $chaptersByNumber[(string) $chapterNumber] = new Chapter(
                        new ChapterId((string) Uuid::uuid4()),
                        $manga->getId(),
                        $chapterNumber,
                        $title,
                        isset($chapterData['attributes']['volume']) ? (int) $chapterData['attributes']['volume'] : null,
                        true,
                        null,
                        0,
                        new \DateTimeImmutable()
                    );
                    $chapterLanguages[(string) $chapterNumber] = $language;
                }
            }

            $offset += $limit;
            $hasMore = count($feed['data']) === $limit;
        }

        // Harmonisation des volumes: si le chapitre précédent et suivant ont un volume null, alors le chapitre actuel aussi
        $this->harmonizeVolumes($chaptersByNumber);

        // Récupère les chapitres existants
        $existingChapters = $this->mangaRepository->findExistingChaptersByNumbers(
            $manga->getId()->getValue(),
            $chapterNumbers
        );

        $newChapterIds = [];

        // Sauvegarde uniquement les nouveaux chapitres et collecte leurs IDs
        foreach ($chaptersByNumber as $chapterNumber => $chapter) {
            if (!isset($existingChapters[(float) $chapterNumber])) {
                $manga->addChapter($chapter);
                $newChapterIds[] = $chapter->getId();
            }
        }

        return $newChapterIds;
    }

    /**
     * Harmonise les volumes des chapitres:
     * - Si le chapitre précédent et suivant ont un volume null, alors le chapitre actuel aussi
     * - Si le chapitre précédent et suivant ont le même volume, alors le chapitre actuel aura ce volume
     * - Remplit les "trous" de volumes manquants dans une séquence
     */
    private function harmonizeVolumes(array &$chaptersByNumber): void
    {
        // Trie les chapitres par numéro pour faciliter la recherche des adjacents
        uksort($chaptersByNumber, fn ($a, $b) => (float)$a <=> (float)$b);

        $chapterNumbers = array_keys($chaptersByNumber);
        $count = count($chapterNumbers);

        // Première passe : harmonisation locale (chapitres adjacents)
        for ($i = 1; $i < $count - 1; $i++) {
            $prevChapterNum = $chapterNumbers[$i - 1];
            $currentChapterNum = $chapterNumbers[$i];
            $nextChapterNum = $chapterNumbers[$i + 1];

            $prevChapter = $chaptersByNumber[$prevChapterNum];
            $currentChapter = $chaptersByNumber[$currentChapterNum];
            $nextChapter = $chaptersByNumber[$nextChapterNum];

            $prevVolume = $prevChapter->getVolume();
            $currentVolume = $currentChapter->getVolume();
            $nextVolume = $nextChapter->getVolume();

            // Règle 1: Si précédent et suivant sont null, alors actuel aussi
            if ($prevVolume === null && $nextVolume === null && $currentVolume !== null) {
                $chaptersByNumber[$currentChapterNum] = new Chapter(
                    new ChapterId($currentChapter->getId()),
                    $currentChapter->getMangaId(),
                    $currentChapter->getNumber(),
                    $currentChapter->getTitle(),
                    null, // volume = null
                    $currentChapter->isVisible(),
                    $currentChapter->getPagesDirectory(),
                    $currentChapter->getPageCount(),
                    $currentChapter->getCreatedAt()
                );
            }
            // Règle 2: Si précédent et suivant ont le même volume, alors actuel aussi
            elseif ($prevVolume !== null && $prevVolume === $nextVolume && $currentVolume !== $prevVolume) {
                $chaptersByNumber[$currentChapterNum] = new Chapter(
                    new ChapterId($currentChapter->getId()),
                    $currentChapter->getMangaId(),
                    $currentChapter->getNumber(),
                    $currentChapter->getTitle(),
                    $prevVolume, // prend le volume des adjacents
                    $currentChapter->isVisible(),
                    $currentChapter->getPagesDirectory(),
                    $currentChapter->getPageCount(),
                    $currentChapter->getCreatedAt()
                );
            }
        }

        // Deuxième passe : comblement des trous de volumes
        $this->fillVolumeGaps($chaptersByNumber, $chapterNumbers);
    }

    /**
     * Remplit les "trous" de volumes manquants dans une séquence
     */
    private function fillVolumeGaps(array &$chaptersByNumber, array $chapterNumbers): void
    {
        $count = count($chapterNumbers);

        for ($i = 0; $i < $count; $i++) {
            $currentChapterNum = $chapterNumbers[$i];
            $currentChapter = $chaptersByNumber[$currentChapterNum];

            if ($currentChapter->getVolume() !== null) {
                continue; // Ce chapitre a déjà un volume
            }

            // Cherche le volume précédent non-null
            $prevVolume = null;
            for ($j = $i - 1; $j >= 0; $j--) {
                $prevChapter = $chaptersByNumber[$chapterNumbers[$j]];
                if ($prevChapter->getVolume() !== null) {
                    $prevVolume = $prevChapter->getVolume();
                    break;
                }
            }

            // Cherche le volume suivant non-null
            $nextVolume = null;
            for ($k = $i + 1; $k < $count; $k++) {
                $nextChapter = $chaptersByNumber[$chapterNumbers[$k]];
                if ($nextChapter->getVolume() !== null) {
                    $nextVolume = $nextChapter->getVolume();
                    break;
                }
            }

            // Priorité au volume précédent : le chapitre appartient à la fin du volume en cours
            // Couvre les cas : milieu de volume (prev=next), transition entre deux volumes (prev≠next)
            if ($prevVolume !== null) {
                $chaptersByNumber[$currentChapterNum] = new Chapter(
                    new ChapterId($currentChapter->getId()),
                    $currentChapter->getMangaId(),
                    $currentChapter->getNumber(),
                    $currentChapter->getTitle(),
                    $prevVolume,
                    $currentChapter->isVisible(),
                    $currentChapter->getPagesDirectory(),
                    $currentChapter->getPageCount(),
                    $currentChapter->getCreatedAt()
                );
            }
            // Sinon utilise le volume suivant (chapitres en début de série)
            elseif ($nextVolume !== null) {
                $chaptersByNumber[$currentChapterNum] = new Chapter(
                    new ChapterId($currentChapter->getId()),
                    $currentChapter->getMangaId(),
                    $currentChapter->getNumber(),
                    $currentChapter->getTitle(),
                    $nextVolume,
                    $currentChapter->isVisible(),
                    $currentChapter->getPagesDirectory(),
                    $currentChapter->getPageCount(),
                    $currentChapter->getCreatedAt()
                );
            }
        }
    }
}
