using MangaReader.Core.Metadata;
using MangaReader.Core.Sources.MangaDex.Api;

namespace MangaReader.Core.Sources.MangaDex.Metadata;

public class MangaDexMetadataProvider(IMangaDexClient mangaDexClient) : IMangaMetadataProvider
{
    public string SourceId => "MangaDex";

    public async Task<SourceManga?> GetMangaAsync(string url, CancellationToken cancellationToken)
    {
        Guid mangaGuid = GetSourceMangaGuid(url);
        MangaDexResponse? mangaDexResponse = await mangaDexClient.GetMangaAsync(mangaGuid, cancellationToken);

        if (mangaDexResponse == null || mangaDexResponse is not MangaDexEntityResponse mangaDexEntityResponse)
            return null;

        if (mangaDexEntityResponse.Data == null || mangaDexEntityResponse.Data is not MangaEntity mangaEntity)
            return null;

        if (mangaEntity.Attributes == null)
            return null;

        MangaAttributes mangaAttributes = mangaEntity.Attributes;
        List<MangaDexEntity> mangaRelationships = mangaEntity.Relationships;
        MangaDexResponse? mangaDexFeedResponse = await mangaDexClient.GetFeedAsync(mangaGuid, cancellationToken);
        MangaDexResponse? coverArtResponse = await mangaDexClient.GetCoverArtAsync(mangaGuid, cancellationToken);

        return new SourceManga()
        {
            Title = GetTitle(mangaAttributes),
            AlternateTitles = GetAlternateTitles(mangaAttributes),
            Genres = GetGenres(mangaAttributes),
            Contributors = GetContributors(mangaRelationships),
            Chapters = GetChapters(mangaDexFeedResponse),
            CoverArt = GetCoverArt(mangaGuid, coverArtResponse)
        };
    }

    private static Guid GetSourceMangaGuid(string url)
    {
        string[] parts = url.Split('/');

        if (parts.Length < 5 || Guid.TryParse(parts[4], out Guid mangaGuid) == false)
        {
            throw new Exception("Unable to get guid from MangaDex url: " + url);
        }

        return mangaGuid;
    }

    private static string GetTitle(MangaAttributes attributes)
    {
        if (attributes.Title.TryGetValue("en", out string? title))
            return title;

        return string.Empty;
    }

    private static List<SourceMangaTitle> GetAlternateTitles(MangaAttributes attributes)
    {
        if (attributes.AltTitles == null || attributes.AltTitles.Count == 0)
            return [];

        Dictionary<string, SourceMangaLanguage> languageIdMap = new()
        {
            { "en", SourceMangaLanguage.English },
            { "ja", SourceMangaLanguage.Japanese },
            { "ja-ro", SourceMangaLanguage.Romanji },
        };

        List<SourceMangaTitle> sourceMangaTitles = [];

        foreach (Dictionary<string, string> alternateTitle in attributes.AltTitles)
        {
            foreach (string alternateTitleKey in alternateTitle.Keys)
            {
                if (languageIdMap.TryGetValue(alternateTitleKey, out SourceMangaLanguage language) == false)
                    continue;

                SourceMangaTitle sourceMangaTitle = new()
                {
                    Title = alternateTitle[alternateTitleKey],
                    Language = language
                };

                sourceMangaTitles.Add(sourceMangaTitle);
            }
        }

        return sourceMangaTitles;
    }

    private static List<string> GetGenres(MangaAttributes attributes)
    {
        if (attributes.Tags == null || attributes.Tags.Count == 0)
            return [];

        List<string> tags = [];

        foreach (TagEntity tagEntity in attributes.Tags)
        {
            if (tagEntity.Attributes == null)
                continue;

            if (tagEntity.Attributes.Name == null || tagEntity.Attributes.Name.Count == 0)
                continue;

            tags.Add(tagEntity.Attributes.Name.FirstOrDefault().Value);
        }

        return tags;
    }

    private static SourceMangaContributor[] GetContributors(List<MangaDexEntity> relationships)
    {
        List<SourceMangaContributor> contributors = [];

        AuthorEntity[] authorEntities = [.. relationships.Where(entity => entity is AuthorEntity).Cast<AuthorEntity>()];
        ArtistEntity[] artistEntities = [.. relationships.Where(entity => entity is ArtistEntity).Cast<ArtistEntity>()];

        foreach (AuthorEntity authorEntity in authorEntities)
        {
            if (authorEntity.Attributes == null)
                continue;

            SourceMangaContributor contributor = new()
            {
                Name = authorEntity.Attributes.Name,
                Role = SourceMangaContributorRole.Author
            };

            contributors.Add(contributor);
        }

        foreach (ArtistEntity artistEntity in artistEntities)
        {
            if (artistEntity.Attributes == null)
                continue;

            SourceMangaContributor contributor = new()
            {
                Name = artistEntity.Attributes.Name,
                Role = SourceMangaContributorRole.Artist
            };

            contributors.Add(contributor);
        }

        return [.. contributors];
    }

    private static List<SourceMangaChapter> GetChapters(MangaDexResponse? mangaDexFeedResponse)
    {
        if (mangaDexFeedResponse == null || mangaDexFeedResponse is not MangaDexCollectionResponse collectionResponse)
            return [];

        //https://mangadex.org/chapter/46084762-855c-46dd-a7b6-66e5cd15604d

        List<SourceMangaChapter> chapters = [];
        ChapterEntity[] chapterEntities = [.. collectionResponse.Data.Where(entity => entity is ChapterEntity).Cast<ChapterEntity>()];

        foreach (ChapterEntity chapterEntity in chapterEntities)
        {
            if (chapterEntity.Attributes == null || chapterEntity.Attributes.TranslatedLanguage != "en")
                continue;

            int? volume = int.TryParse(chapterEntity.Attributes.Volume, out var temp) ? temp : null;

            if (float.TryParse(chapterEntity.Attributes.Chapter, out float chapterNumber) == false)
                continue;

            SourceMangaChapter chapter = new()
            {
                Volume = volume,
                Number = chapterNumber,
                Title = chapterEntity.Attributes.Title,
                Url = $"https://mangadex.org/chapter/{chapterEntity.Id}"
            };

            chapters.Add(chapter);
        }

        return chapters;
    }

    private static string[] GetCoverArt(Guid mangaGuid, MangaDexResponse? coverArtResponse)
    {
        if (coverArtResponse == null || coverArtResponse is not MangaDexCollectionResponse collectionResponse)
            return [];

        List<string> coverArtUrls = [];
        CoverArtEntity[] coverArtEntities = [.. collectionResponse.Data.Where(entity => entity is CoverArtEntity).Cast<CoverArtEntity>()];

        foreach (CoverArtEntity coverArtEntity in coverArtEntities)
        {
            if (coverArtEntity.Attributes == null || string.IsNullOrWhiteSpace(coverArtEntity.Attributes.FileName))
                continue;

            string url = $"https://mangadex.org/covers/{mangaGuid}/{coverArtEntity.Attributes.FileName}";

            coverArtUrls.Add(url);
        }

        return [.. coverArtUrls];
    }
}