using JSMR.Application.Enums;
using JSMR.Application.Integrations.DLSite.Models;
using JSMR.Application.Integrations.DLSite.Models.ReleasedWorks;
using JSMR.Domain.Enums;
using JSMR.Domain.ValueObjects;
using JSMR.Infrastructure.Integrations.DLSite;
using JSMR.Infrastructure.Integrations.DLSite.Mapping;
using JSMR.Infrastructure.Integrations.DLSite.Models;
using JSMR.Infrastructure.Integrations.DLSite.Models.NewWorks;
using JSMR.Tests.Http;
using JSMR.Tests.Utilities;
using Microsoft.Extensions.Logging;
using NSubstitute;
using Shouldly;
using System.Net;
using System.Text;

namespace JSMR.Tests.Integrations.DLSite;

public class DLSiteClientTests
{
    private static async Task<string> ReadJsonResourceAsync(string resourceName)
    {
        return await ResourceHelper.ReadAsync($"JSMR.Tests.Integrations.DLSite.{resourceName}");
    }

    private static async Task<(DLSiteClient Client, FakeHttpMessageHandler Handler)> GetDLSiteClientThatReturns(string resourceName)
    {
        string content = await ReadJsonResourceAsync(resourceName);

        FakeHttpMessageHandler handler = new(_ =>
        {
            return new HttpResponseMessage(HttpStatusCode.OK)
            {
                Content = new StringContent(content, Encoding.UTF8, "application/json")
            };
        });

        HttpClient httpClient = new(handler)
        {
            BaseAddress = new Uri("https://www.fake-dlsite.com/")
        };

        var logger = Substitute.For<ILogger<DLSiteClient>>();
        var client = new DLSiteClient(httpClient, logger);

        return (client, handler);
    }

    [Fact]
    public async Task GetVoiceWorkDetailsAsync_Should_Call_Expected_Request()
    {
        var (client, handler) = await GetDLSiteClientThatReturns("Product-Info.json");
        await client.GetVoiceWorkDetailsAsync(["RJ01230163", "RJ01536422"], CancellationToken.None);

        handler.Requests.Count.ShouldBe(1);

        HttpRequestMessage httpRequest = handler.Requests[0];
        httpRequest.Method.ShouldBe(HttpMethod.Get);
        httpRequest.ShouldHavePath("/maniax/product/info/ajax");
        httpRequest.ShouldHaveQueryParam("product_id", "RJ01230163,RJ01536422");
    }

    [Fact]
    public async Task Deserialize_Product_Info_Collection()
    {
        var (client, handler) = await GetDLSiteClientThatReturns("Product-Info.json");
        var result = await client.GetVoiceWorkDetailsAsync(["RJ01230163", "RJ01536422"], CancellationToken.None);

        result.Count.ShouldBe(2);

        result.ShouldContainKey("RJ01230163");
        result["RJ01230163"].Title.ShouldBe("[Azur Lane ASMR] Commander Pampering Team! Golden Hind's Tentacular Treatment");
        result["RJ01230163"].HasTrial.ShouldBeTrue();
        result["RJ01230163"].HasDLPlay.ShouldBeTrue();
        result["RJ01230163"].HasReviews.ShouldBeTrue();
        result["RJ01230163"].SupportedLanguages.Count.ShouldBe(1);
        result["RJ01230163"].SupportedLanguages.Select(x => x.Language).ShouldContain(Language.English);
        result["RJ01230163"].DownloadCount.ShouldBe(659);
        result["RJ01230163"].WishlistCount.ShouldBe(380);

        // Testing this one for empty array of prices
        result.ShouldContainKey("RJ01536422");
    }

    [Fact]
    public void Map_Basic_ProductInfoCollection()
    {
        ProductInfoCollection productInfoCollection = new()
        {
            {
                "RG0001",
                new ProductInfo()
                {
                    WorkName = "Product 1",
                    WorkNameMasked = "Product 1 (Masked)",
                    WishlistCount = 250,
                    DownloadCount = 100,
                    Options = ["TRI", "DLP", "JPN"],
                    TitleId = "SE0001",
                    TitleName = "Series 1"
                }
            },
            {
                "RG0002",
                new ProductInfo()
                {
                    WorkName = "Product 2",
                    WorkNameMasked = "Product 2 (Masked)",
                    WishlistCount = 100,
                    DownloadCount = 50,
                    Options = ["TRI", "ENG", "DOT", "VET"],
                    TitleId = "SE0001",
                    TitleName = "Series 1",
                    TranslationInfo = new()
                    {
                        OriginalWorkNumber = "RG0001",
                        Language = "ENG"
                    }
                }
            }
        };

        // General
        VoiceWorkDetailCollection mappedCollection = DLSiteToDomainMapper.Map(productInfoCollection);
        mappedCollection.Count.ShouldBe(2);
        mappedCollection.ShouldContainKey("RG0001");
        mappedCollection.ShouldContainKey("RG0002");

        // RG0001
        VoiceWorkDetails voiceWorkDetails = mappedCollection["RG0001"];
        voiceWorkDetails.Title.ShouldBe("Product 1");
        voiceWorkDetails.WishlistCount.ShouldBe(250);
        voiceWorkDetails.DownloadCount.ShouldBe(100);
        voiceWorkDetails.HasTrial.ShouldBe(true);
        voiceWorkDetails.HasDLPlay.ShouldBe(true);
        voiceWorkDetails.AI.ShouldBe(AIGeneration.None);
        voiceWorkDetails.SupportedLanguages.ShouldBe([SupportedLanguage.Japanese]);

        voiceWorkDetails.Series.ShouldNotBeNull();
        voiceWorkDetails.Series.Identifier.ShouldBe("SE0001");
        voiceWorkDetails.Series.Name.ShouldBe("Series 1");


        // RG0002
        VoiceWorkDetails secondVoiceWorkDetails = mappedCollection["RG0002"];
        secondVoiceWorkDetails.Title.ShouldBe("Product 2");
        secondVoiceWorkDetails.WishlistCount.ShouldBe(100);
        secondVoiceWorkDetails.DownloadCount.ShouldBe(50);
        secondVoiceWorkDetails.HasTrial.ShouldBe(true);
        secondVoiceWorkDetails.HasDLPlay.ShouldBe(false);
        secondVoiceWorkDetails.AI.ShouldBe(AIGeneration.None);
        secondVoiceWorkDetails.SupportedLanguages.ShouldBe([SupportedLanguage.English]);

        secondVoiceWorkDetails.Series.ShouldNotBeNull();
        secondVoiceWorkDetails.Series.Identifier.ShouldBe("SE0001");
        secondVoiceWorkDetails.Series.Name.ShouldBe("Series 1");

        secondVoiceWorkDetails.Translation.ShouldNotBeNull();
        secondVoiceWorkDetails.Translation.OriginalProductId.ShouldBe("RG0001");
        secondVoiceWorkDetails.Translation.Language.ShouldBe(Language.English);
        secondVoiceWorkDetails.Translation.Kind.ShouldBe(TranslationKind.Official | TranslationKind.Recommended);
    }

    [Fact]
    public async Task GetReleasedWorksAsync_Should_Call_Expected_Request()
    {
        var (client, handler) = await GetDLSiteClientThatReturns("Released-Works.json");

        ReleasedWorksRequest request = new(Locale.English, new(2025, 1, 1), 1);

        await client.GetReleasedWorksAsync(request, CancellationToken.None);

        handler.Requests.Count.ShouldBe(1);

        HttpRequestMessage httpRequest = handler.Requests[0];
        httpRequest.Method.ShouldBe(HttpMethod.Get);
        httpRequest.ShouldHavePath("/maniax/new/work/api");
        httpRequest.ShouldHaveQueryParam("locale", "en-us");
        httpRequest.ShouldHaveQueryParam("date", "2025-01-01");
        httpRequest.ShouldHaveQueryParam("period", "1");
    }

    [Fact]
    public async Task Map_Basic_Released_Work_Collection()
    {
        NewWorksApiResponse response = new()
        {
            Meta = new()
            {
                Code = 200
            },
            Data = new()
            {
                Products =
                [
                    new()
                    {
                        Id = "RG1",
                        Name = "The Title",
                        NameMasked = "Masked Title",
                        Description = "The description",
                        DescriptionMasked = "The masked description"
                    }
                ]
            }
        };

        ReleasedWorksCollection collection = DLSiteReleasedWorksMapper.Map(response);
        collection.Count.ShouldBe(1);
        collection.ShouldContainKey("RG1");

        ReleasedWork releasedWork = collection["RG1"];
        releasedWork.ProductId.ShouldBe("RG1");
        releasedWork.Title.ShouldBe("The Title");
        releasedWork.MaskedTitle.ShouldBe("Masked Title");
        releasedWork.Description.ShouldBe("The description");
        releasedWork.MaskedDescription.ShouldBe("The masked description");
    }

    [Fact]
    public async Task Deserialize_Released_Work_Collection()
    {
        var (client, handler) = await GetDLSiteClientThatReturns("Released-Works.json");
        var request = new ReleasedWorksRequest(Locale.English, new(2025, 1, 1), 1);
        var result = await client.GetReleasedWorksAsync(request, CancellationToken.None);

        result.Count.ShouldBe(13);

        result.ShouldContainKey("RJ01588345");

        ReleasedWork releasedWork = result["RJ01588345"];
        releasedWork.Title.ShouldBe("魔都一贅肉のスゴイデブ");
        releasedWork.MaskedTitle.ShouldBe("魔都一贅肉のスゴイデブ");
        releasedWork.Description.ShouldBe("圧巻の超連続・激太りご褒美パラダイス、ここに！全24ページ描き下ろし！太→激太への肥満化シークエンスが11作収録！餌付け、自己肥育、お風呂、縛り…などなど多種多様の太り方でご褒美を堪能せよ！");
        releasedWork.MaskedDescription.ShouldBe("圧巻の超連続・激太りご褒美パラダイス、ここに！全24ページ描き下ろし！太→激太への肥満化シークエンスが11作収録！餌付け、自己肥育、お風呂、縛り…などなど多種多様の太り方でご褒美を堪能せよ！");
    }
}