using JSMR.Application.Scanning.Contracts;
using JSMR.Domain.Entities;
using JSMR.Domain.Enums;
using JSMR.Domain.ValueObjects;
using JSMR.Infrastructure.Data;
using JSMR.Tests.Fixtures;
using Microsoft.EntityFrameworkCore;
using Shouldly;

namespace JSMR.Tests.Ingestion.Japanese;

public class Basic_Insert_And_Update_Tests(MariaDbContainerFixture fixture) : JapaneseIngestionTestsBase(fixture)
{
    [Fact]
    public async Task Basic_Insert_And_Update_Test()
    {
        await using AppDbContext dbContext = await GetAppDbContextAsync();

        VoiceWorkIngest ingest = new()
        {
            MakerId = "RG1",
            MakerName = "My Maker",
            ProductId = "RJ1",
            Title = "My Product",
            Description = "My description",
            Tags = ["Tag 1", "Tag 2"],
            Creators = ["Creator 1"],
            WishlistCount = 100,
            Downloads = 0,
            HasTrial = true,
            HasChobit = false,
            AgeRating = AgeRating.AllAges,
            HasImage = false,
            SupportedLanguages = [SupportedLanguage.Japanese],
            SalesDate = null,
            ExpectedDate = new DateOnly(2025, 2, 1)
        };

        DateTime dateTime = TokyoLocalToUtc(2025, 01, 15, 00, 00, 00);
        await UpsertAsync(dbContext, dateTime, [ingest]);

        Circle? circle = await dbContext.Circles.FirstOrDefaultAsync(v => v.MakerId == ingest.MakerId, TestContext.Current.CancellationToken);
        circle.ShouldNotBeNull();
        circle.Name.ShouldBe(ingest.MakerName);

        VoiceWork? voiceWork = await dbContext.VoiceWorks.FirstOrDefaultAsync(v => v.ProductId == ingest.ProductId, TestContext.Current.CancellationToken);
        voiceWork.ShouldNotBeNull();
        voiceWork.ProductName.ShouldBe(ingest.Title);
        voiceWork.Description.ShouldBe(ingest.Description);
        voiceWork.WishlistCount.ShouldBe(ingest.WishlistCount);
        voiceWork.Downloads.ShouldBe(ingest.Downloads);
        voiceWork.HasTrial.ShouldBe(ingest.HasTrial);
        voiceWork.HasChobit.ShouldBe(ingest.HasChobit);
        voiceWork.Rating.ShouldBe((int)ingest.AgeRating);
        voiceWork.HasImage.ShouldBe(ingest.HasImage);
        voiceWork.SubtitleLanguage.ShouldBe((byte)Language.Japanese);
        voiceWork.SalesDate.ShouldBeNull();
        voiceWork.ExpectedDate.ShouldBe(ingest.ExpectedDate!.Value.ToDateTime(TimeOnly.MinValue));

        foreach (string tagName in ingest.Tags)
        {
            Tag? tag = await dbContext.Tags.FirstOrDefaultAsync(t => t.Name == tagName, TestContext.Current.CancellationToken);
            tag.ShouldNotBeNull();

            VoiceWorkTag? voiceWorkTag = await dbContext.VoiceWorkTags.FirstOrDefaultAsync(vwt =>
                vwt.VoiceWorkId == voiceWork.VoiceWorkId && vwt.TagId == tag.TagId, TestContext.Current.CancellationToken);

            voiceWorkTag.ShouldNotBeNull();
        }

        foreach (string creatorName in ingest.Creators)
        {
            Creator? creator = await dbContext.Creators.FirstOrDefaultAsync(c => c.Name == creatorName, TestContext.Current.CancellationToken);
            creator.ShouldNotBeNull();

            VoiceWorkCreator? voiceWorkCreator = await dbContext.VoiceWorkCreators.FirstOrDefaultAsync(vwc =>
                vwc.VoiceWorkId == voiceWork.VoiceWorkId && vwc.CreatorId == creator.CreatorId, TestContext.Current.CancellationToken);

            voiceWorkCreator.ShouldNotBeNull();
        }

        foreach (SupportedLanguage supportedLanguage in ingest.SupportedLanguages)
        {
            VoiceWorkSupportedLanguage? voiceWorkSupportedLanauge = await dbContext.VoiceWorkSupportedLanguages.FirstOrDefaultAsync(x =>
                x.VoiceWorkId == voiceWork.VoiceWorkId && x.Language == supportedLanguage.Code, TestContext.Current.CancellationToken);

            voiceWorkSupportedLanauge.ShouldNotBeNull();
        }

        VoiceWorkIngest updatedIngest = ingest with
        {
            MakerName = "My Maker (Updated)",
            Tags = ["Tag 1", "Not Tag 2"],
            Creators = ["Not Creator 1"],
            Downloads = 50,
            HasChobit = true,
            SupportedLanguages = [SupportedLanguage.Japanese, SupportedLanguage.English],
            HasImage = true,
            SalesDate = new DateOnly(2025, 2, 5),
            ExpectedDate = null
        };

        DateTime updateDateTime = TokyoLocalToUtc(2025, 02, 03, 00, 00, 00);
        await UpsertAsync(dbContext, dateTime, [updatedIngest]);

        circle = await dbContext.Circles.FirstOrDefaultAsync(v => v.MakerId == updatedIngest.MakerId, TestContext.Current.CancellationToken);
        circle.ShouldNotBeNull();
        circle.Name.ShouldBe(updatedIngest.MakerName);

        voiceWork = await dbContext.VoiceWorks.FirstOrDefaultAsync(v => v.ProductId == updatedIngest.ProductId, TestContext.Current.CancellationToken);
        voiceWork.ShouldNotBeNull();
        voiceWork.ProductName.ShouldBe(updatedIngest.Title);
        voiceWork.Description.ShouldBe(updatedIngest.Description);
        voiceWork.WishlistCount.ShouldBe(updatedIngest.WishlistCount);
        voiceWork.Downloads.ShouldBe(updatedIngest.Downloads);
        voiceWork.HasTrial.ShouldBe(updatedIngest.HasTrial);
        voiceWork.HasChobit.ShouldBe(updatedIngest.HasChobit);
        voiceWork.Rating.ShouldBe((int)updatedIngest.AgeRating);
        voiceWork.HasImage.ShouldBe(updatedIngest.HasImage);
        voiceWork.SubtitleLanguage.ShouldBe((byte)Language.English);
        voiceWork.SalesDate.ShouldBe(updatedIngest.SalesDate!.Value.ToDateTime(TimeOnly.MinValue));
        voiceWork.ExpectedDate.ShouldBeNull();

        foreach (string tagName in updatedIngest.Tags.Union(ingest.Tags))
        {
            Tag? tag = await dbContext.Tags.FirstOrDefaultAsync(t => t.Name == tagName, TestContext.Current.CancellationToken);
            tag.ShouldNotBeNull();

            VoiceWorkTag? voiceWorkTag = await dbContext.VoiceWorkTags.FirstOrDefaultAsync(vwt =>
                vwt.VoiceWorkId == voiceWork.VoiceWorkId && vwt.TagId == tag.TagId, TestContext.Current.CancellationToken);

            voiceWorkTag.ShouldNotBeNull();
        }

        foreach (string creatorName in updatedIngest.Creators.Union(ingest.Creators))
        {
            Creator? creator = await dbContext.Creators.FirstOrDefaultAsync(c => c.Name == creatorName, TestContext.Current.CancellationToken);
            creator.ShouldNotBeNull();

            VoiceWorkCreator? voiceWorkCreator = await dbContext.VoiceWorkCreators.FirstOrDefaultAsync(vwc =>
                vwc.VoiceWorkId == voiceWork.VoiceWorkId && vwc.CreatorId == creator.CreatorId, TestContext.Current.CancellationToken);

            voiceWorkCreator.ShouldNotBeNull();
        }

        foreach (SupportedLanguage supportedLanguage in updatedIngest.SupportedLanguages.Union(ingest.SupportedLanguages))
        {
            VoiceWorkSupportedLanguage? voiceWorkSupportedLanauge = await dbContext.VoiceWorkSupportedLanguages.FirstOrDefaultAsync(x =>
                x.VoiceWorkId == voiceWork.VoiceWorkId && x.Language == supportedLanguage.Code, TestContext.Current.CancellationToken);

            voiceWorkSupportedLanauge.ShouldNotBeNull();
        }
    }
}