using JSMR.Application.Integrations.DLSite.Models;
using JSMR.Domain.Enums;
using JSMR.Domain.ValueObjects;
using JSMR.Infrastructure.Integrations.DLSite.Models;

namespace JSMR.Infrastructure.Integrations.DLSite.Mapping;

public static class DLSiteToDomainMapper
{
    private const string OptTrial = "TRI";
    private const string OptDLPlay = "DLP";
    private const string OptReviews = "REV";
    private const string OptOfficialTranslation = "DOT";
    private const string OptRecommendedTranslation = "VET";

    private const string OptAIFull = "AIG";
    private const string OptAIPartial = "AIP";

    private static readonly Dictionary<int, AgeRating> AgeRatingMap = new()
    {
        {  1, AgeRating.AllAges },
        {  2, AgeRating.R15 },
        {  3, AgeRating.R18 }
    };

    public static VoiceWorkDetailCollection Map(ProductInfoCollection? productInfoCollection)
    {
        VoiceWorkDetailCollection result = [];

        if (productInfoCollection is null)
            return result;

        foreach (var keyValue in productInfoCollection)
        {
            string productId = keyValue.Key;
            ProductInfo productInfo = keyValue.Value;

            VoiceWorkDetails voiceWorkDetails = Map(productInfo);
            result[productId] = voiceWorkDetails;
        }

        return result;
    }

    public static VoiceWorkDetails Map(ProductInfo productInfo)
    {
        ArgumentNullException.ThrowIfNull(productInfo);

        string[] options = [.. productInfo.Options.Where(s => !string.IsNullOrWhiteSpace(s))];
        HashSet<string> optionsSet = new(options, StringComparer.OrdinalIgnoreCase);

        return new VoiceWorkDetails
        {
            Title = productInfo.WorkName,
            Series = MapSeries(productInfo),
            Translation = MapTranslation(productInfo, optionsSet),
            WishlistCount = productInfo.WishlistCount,
            DownloadCount = productInfo.DownloadCount,
            RegistrationDate = productInfo.RegistrationDate,
            SupportedLanguages = MapSupportedLanguages(optionsSet),
            AI = MapAIGeneration(optionsSet),
            HasTrial = optionsSet.Contains(OptTrial),
            HasDLPlay = optionsSet.Contains(OptDLPlay),
            HasReviews = optionsSet.Contains(OptReviews),
            AgeRating = MapAgeRating(productInfo)
        };
    }

    private static VoiceWorkSeries? MapSeries(ProductInfo productInfo)
    {
        if (string.IsNullOrWhiteSpace(productInfo.TitleId) || string.IsNullOrWhiteSpace(productInfo.TitleName))
            return null;

        return new VoiceWorkSeries(
            Identifier: productInfo.TitleId,
            Name: productInfo.TitleName
        );
    }

    private static VoiceWorkTranslation? MapTranslation(ProductInfo info, HashSet<string> options)
    {
        ProductTranslationInfo? translationInfo = info.TranslationInfo;

        if (translationInfo == null || string.IsNullOrWhiteSpace(translationInfo.OriginalWorkNumber) || string.IsNullOrWhiteSpace(translationInfo.Language))
            return null;

        string languageCode = translationInfo.Language.Trim();

        if (!options.Contains(languageCode))
            return null;

        if (!SupportedLanguage.TryFromCode(languageCode, out SupportedLanguage? supportedLanguage))
            return null;

        string originalId = translationInfo.OriginalWorkNumber;

        TranslationKind translationKind = TranslationKind.None;

        if (options.Contains(OptOfficialTranslation))
            translationKind |= TranslationKind.Official;

        if (options.Contains(OptRecommendedTranslation))
            translationKind |= TranslationKind.Recommended;

        //bool isOfficial = options.Contains(OptOfficialTranslation);
        //bool isRecommended = options.Contains(OptRecommendedTranslation);

        return new VoiceWorkTranslation(
            OriginalProductId: originalId,
            Language: supportedLanguage.Language,
            Kind: translationKind
        );
    }

    private static SupportedLanguage[] MapSupportedLanguages(HashSet<string> options)
    {
        List<SupportedLanguage> languages = [];

        foreach (SupportedLanguage supportedLanguage in SupportedLanguage.All)
        {
            if (options.Contains(supportedLanguage.Code) && !languages.Contains(supportedLanguage))
                languages.Add(supportedLanguage);
        }

        return [.. languages];
    }

    private static AIGeneration MapAIGeneration(HashSet<string> options)
    {
        if (options.Contains(OptAIFull))
            return AIGeneration.Full;

        if (options.Contains(OptAIPartial))
            return AIGeneration.Partial;

        return AIGeneration.None;
    }

    private static AgeRating MapAgeRating(ProductInfo productInfo)
    {
        if (AgeRatingMap.TryGetValue(productInfo.AgeCategory, out AgeRating ageRating))
            return ageRating;

        return AgeRating.R18;
    }
}