using ErsatzTV.Core;
using ErsatzTV.Core.Domain;
using ErsatzTV.Core.Interfaces.Images;
using ErsatzTV.Infrastructure.Data;
using ErsatzTV.Services.RunOnce;
using ErsatzTV.Tests.Support;
using LanguageExt;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging.Abstractions;
using NSubstitute;
using NUnit.Framework;
using Shouldly;

namespace ErsatzTV.Tests.Services;

[TestFixture]
public class ExternalLogoMigratorTests
{
    private InMemoryTvContext _db = null!;
    private IRemoteLogoCacher _cacher = null!;

    [SetUp]
    public async Task SetUp()
    {
        _db = await InMemoryTvContext.CreateAsync();
        _cacher = Substitute.For<IRemoteLogoCacher>();
    }

    [TearDown]
    public async Task TearDown() => await _db.DisposeAsync();

    [Test]
    public async Task Should_Convert_Url_Logo_Row_To_Cache_Name()
    {
        await Seed(new Artwork
        {
            Path = "https://example.com/logo.png",
            ArtworkKind = ArtworkKind.Logo
        });

        _cacher.CacheFromUrl(Arg.Any<Uri>(), Arg.Any<CancellationToken>())
            .Returns(Either<BaseError, string>.Right("hash"));

        await using (TvContext db = _db.CreateContext())
        {
            await ExternalLogoMigratorService.MigrateAsync(
                db,
                _cacher,
                NullLogger.Instance,
                CancellationToken.None);
        }

        await using TvContext verify = _db.CreateContext();
        Artwork row = await verify.Artwork.SingleAsync();
        row.Path.ShouldBe("hash");
        row.IsExternalUrl().ShouldBeFalse();
    }

    [Test]
    public async Task Should_Leave_Row_Unchanged_When_Cacher_Fails()
    {
        const string Url = "https://example.com/logo.png";
        await Seed(new Artwork
        {
            Path = Url,
            ArtworkKind = ArtworkKind.Logo
        });

        _cacher.CacheFromUrl(Arg.Any<Uri>(), Arg.Any<CancellationToken>())
            .Returns(Either<BaseError, string>.Left(BaseError.New("boom")));

        await using (TvContext db = _db.CreateContext())
        {
            await ExternalLogoMigratorService.MigrateAsync(
                db,
                _cacher,
                NullLogger.Instance,
                CancellationToken.None);
        }

        await using TvContext verify = _db.CreateContext();
        Artwork row = await verify.Artwork.SingleAsync();
        row.Path.ShouldBe(Url);
        row.IsExternalUrl().ShouldBeTrue();
    }

    [Test]
    public async Task Should_Not_Call_Cacher_For_Already_Migrated_Rows()
    {
        await Seed(new Artwork
        {
            Path = "some-bare-hash",
            ArtworkKind = ArtworkKind.Logo
        });

        await using (TvContext db = _db.CreateContext())
        {
            await ExternalLogoMigratorService.MigrateAsync(
                db,
                _cacher,
                NullLogger.Instance,
                CancellationToken.None);
        }

        await _cacher.DidNotReceive().CacheFromUrl(Arg.Any<Uri>(), Arg.Any<CancellationToken>());
    }

    private async Task Seed(Artwork artwork)
    {
        await using TvContext db = _db.CreateContext();
        await db.Artwork.AddAsync(artwork);
        await db.SaveChangesAsync();
    }
}
