<?php

namespace App\Service;

use App\Entity\Chapter;
use App\Entity\Manga;
use App\Interface\ClientInterface;
use App\Interface\MetadataProviderInterface;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Symfony\Component\String\Slugger\SluggerInterface;

readonly class MangadexProvider implements MetadataProviderInterface
{
    public function __construct(private ClientInterface $client, private SluggerInterface $slugger, private NotificationService $notificationService)
    {
    }

    public function search(?string $title): Collection
    {
        if (null === $title) {
            return new ArrayCollection();
        }

        try {
            $results = $this->client->get('/manga', [
                'title' => $title,
                'contentRating' => ['safe', 'suggestive', 'erotica'],
                'includes' => ['cover_art', 'author'],
                'limit' => 50,
            ]);
        } catch (\Exception $e) {
            $this->notificationService->sendUpdate('notification', ['status' => 'error', 'message' => 'An error occurred while fetching data from Mangadex.']);

            return new ArrayCollection();
        }

        $mangas = [];
        foreach ($results['data'] as $result) {
            $mangas[] = (new Manga())
                ->setExternalId($result['id'])
                ->setTitle($result['attributes']['title']['en'])
                ->setSlug($this->slugger->slug($result['attributes']['title']['en'])->lower())
                ->setDescription($result['attributes']['description']['fr'] ?? $result['attributes']['description']['en'] ?? '')
                ->setPublicationYear($result['attributes']['year'])
                ->setStatus($result['attributes']['status']);
            $tags = [];
            foreach ($result['attributes']['tags'] as $tag) {
                $tags[] = $tag['attributes']['name']['en'];
            }

            $mangas[count($mangas) - 1]->setGenres($tags);

            foreach ($result['relationships'] as $relationship) {
                if ('author' === $relationship['type']) {
                    $mangas[count($mangas) - 1]->setAuthor($relationship['attributes']['name']);
                }

                if ('cover_art' === $relationship['type']) {
                    $mangas[count($mangas) - 1]->setImageUrl('https://mangadex.org/covers/'.$result['id'].'/'.$relationship['attributes']['fileName']);
                }
            }
        }

        $test = array_map(fn ($manga) => $manga->getExternalId(), $mangas);

        $ratings = $this->client->get('/statistics/manga', [
            'manga' => $test,
        ]);

        foreach ($mangas as $manga) {
            $manga->setRating($ratings['statistics'][$manga->getExternalId()]['rating']['average']);
        }

        usort($mangas, fn ($a, $b) => $b->getRating() <=> $a->getRating());

        return new ArrayCollection($mangas);
    }

    public function getFeed(Manga $manga): array
    {
        if (null === $manga->getExternalId()) {
            return [];
        }

        $chapters = [];
        $page = 0;

        do {
            $results = $this->getFeedWithPagination($manga->getExternalId(), $page);
            if (isset($results['data'])) {
                $chapters = array_merge($chapters, $results['data']);
            } else {
                break;
            }
            ++$page;
        } while (count($chapters) < $results['total']);

        return $this->getChaptersFromFeed($chapters, $manga);
    }

    public function getLastFeed(Manga $manga, int $limit = 100): array
    {
        if (null === $manga->getExternalId()) {
            return [];
        }

        $chapters = [];

        try {
            $results = $this->getFeedWithPagination($manga->getExternalId(), 0, $limit, 'desc');
            if (isset($results['data'])) {
                $chapters = $results['data'];
            }
        } catch (\Exception $e) {
            $this->notificationService->sendUpdate(['status' => 'error', 'message' => 'An error occurred while fetching recent chapters from Mangadex.']);

            return [];
        }

        return $this->getChaptersFromFeed($chapters, $manga);
    }

    private function getFeedWithPagination(string $externalId, int $page, int $limit = 500, string $order = 'asc'): array
    {
        try {
            $response = $this->client->get('/manga/'.$externalId.'/feed', [
                'limit' => $limit,
                'translatedLanguage' => ['en', 'fr'],
                'order' => ['chapter' => $order],
                'offset' => $page * $limit,
            ]);
        } catch (\Exception $e) {
            $this->notificationService->sendUpdate(['status' => 'error', 'message' => 'An error occurred while fetching data from Mangadex.']);

            return [];
        }

        return $response;
    }

    public function getMangaAggregate(Manga $manga): array
    {
        if (null === $manga->getExternalId()) {
            return [];
        }

        try {
            $response = $this->client->get('/manga/'.$manga->getExternalId().'/aggregate');
        } catch (\Exception $e) {
            //            $this->notificationService->sendUpdate(['status' => 'error', 'message' => 'An error occurred while fetching data from Mangadex.']);
            return [];
        }

        $chapterEntities = [];
        if ('ok' === $response['result']) {
            foreach ($response['volumes'] as $volume) {
                $volumeNumber = 'none' === $volume['volume'] ? 0 : (float) $volume['volume'];
                foreach ($volume['chapters'] as $chapter) {
                    $chapterEntity = new Chapter();
                    $chapterEntity->setNumber((float) $chapter['chapter'])
                        ->setTitle('Chapter '.$chapter['chapter'])
                        ->setVolume($volumeNumber)
                        ->setExternalId('');

                    $chapterEntities[] = $chapterEntity;
                    //                    $manga->addChapter($chapterEntity);
                }
            }
        }

        return $chapterEntities;
    }

    public function getChaptersFromFeed(mixed $chapters, Manga $manga): array
    {
        $chapterEntities = [];
        $uniqueChapterNumbers = [];

        foreach ($chapters as $result) {
            $chapterNumber = (float) $result['attributes']['chapter'];

            // Vérifiez si le chapitre existe déjà dans la base de données
            $chapterExists = $manga->getChapters()->exists(function ($key, $existingChapter) use ($chapterNumber) {
                return $existingChapter->getNumber() === $chapterNumber;
            });

            // Si le chapitre existe déjà dans la base de données ou dans notre nouvelle liste, on skip
            if ($chapterExists || in_array($chapterNumber, $uniqueChapterNumbers)) {
                continue;
            }

            // Créez et ajoutez le nouveau chapitre
            $chapter = new Chapter();
            $chapter->setNumber($chapterNumber)
                ->setTitle($result['attributes']['title'])
                ->setVolume((int) $result['attributes']['volume'] ?? null)
                ->setExternalId($result['id']);

            $chapterEntities[] = $chapter;
            $uniqueChapterNumbers[] = $chapterNumber;
        }

        // Trier les chapitres par numéro
        usort($chapterEntities, function ($a, $b) {
            return $a->getNumber() <=> $b->getNumber();
        });

        return $chapterEntities;
    }

    public function addAllChaptersToManga(Manga $manga): array
    {
        $mangaFeed = $this->getFeed($manga);
        $mangaAggregate = $this->getMangaAggregate($manga);

        $allChapters = array_merge($mangaFeed, $mangaAggregate);

        if (empty($allChapters)) {
            $this->notificationService->sendUpdate([
                'status' => 'error',
                'message' => 'No chapters found for this manga.',
            ]);

            return [];
        }

        $mergedChapters = [];
        foreach ($allChapters as $chapter) {
            $number = $chapter->getNumber();
            $existingChapter = $manga->getChapterByNumber($number);
            if ($existingChapter) {
                if ($existingChapter->getExternalId() !== $chapter->getExternalId() && is_null($existingChapter->getExternalId())) {
                    $this->updateChapter($existingChapter, $chapter);
                    $mergedChapters[$number] = $existingChapter;
                }
            } else {
                // Add new chapter
                $manga->addChapter($chapter);
                $mergedChapters[$number] = $chapter;
            }
        }

        return array_values($mergedChapters);
    }

    private function updateChapter(Chapter $existingChapter, Chapter $newChapter): void
    {
        $existingChapter->setVolume($newChapter->getVolume());
        $existingChapter->setExternalId($newChapter->getExternalId());
    }
}
