using System.Threading.Channels;
using ErsatzTV.Application;
using ErsatzTV.Application.MediaCollections;
using ErsatzTV.Core;
using ErsatzTV.Core.Domain;
using ErsatzTV.Core.Errors;
using ErsatzTV.Core.Interfaces.Repositories;
using ErsatzTV.Infrastructure.Data;
using ErsatzTV.Tests.Support;
using LanguageExt;
using Microsoft.EntityFrameworkCore;
using NSubstitute;
using NUnit.Framework;
using Shouldly;
using Unit = LanguageExt.Unit;
using static LanguageExt.Prelude;

namespace ErsatzTV.Tests.Application.MediaCollections;

/// <summary>
///     #253 optimistic-concurrency contract on the Collection custom-order handler (#6): the pre-check
///     412 (standalone Either after validation, not flattened to 422) and the unconditional Version bump.
/// </summary>
[TestFixture]
public class CollectionCustomOrderConcurrencyTests : MediaCollectionHandlerTestBase
{
    private async Task SeedCollectionWithVersion(int id, int version, string name = "Collection")
    {
        await using TvContext context = Db.CreateContext();
        context.Collections.Add(new Collection
        {
            Id = id,
            Name = name,
            Version = version,
            MediaItems = [],
            CollectionItems = []
        });
        await context.SaveChangesAsync();
    }

    private async Task<int> ReadVersion(int id)
    {
        await using TvContext context = Db.CreateContext();
        return await context.Collections.Where(c => c.Id == id).Select(c => c.Version).SingleAsync();
    }

    private UpdateCollectionCustomOrderHandler MakeHandler()
    {
        IMediaCollectionRepository repo = Substitute.For<IMediaCollectionRepository>();
        repo.PlayoutIdsUsingCollection(Arg.Any<int>()).Returns([]);
        return new UpdateCollectionCustomOrderHandler(Db.Factory, repo, Worker);
    }

    private static BaseError LeftOf(Either<BaseError, Unit> either) =>
        either.Match(Left: e => e, Right: _ => throw new AssertionException("Expected a Left result"));

    [Test]
    public async Task Stale_If_Match_Should_Fail_Precondition_And_Not_Mutate()
    {
        await SeedCollectionWithVersion(1, version: 2);

        Either<BaseError, Unit> result = await MakeHandler().Handle(
            new UpdateCollectionCustomOrder(1, [], Some(new[] { 1 }.ToSeq())),
            CancellationToken.None);

        LeftOf(result).ShouldBeOfType<PreconditionFailedError>();
        (await ReadVersion(1)).ShouldBe(2);
    }

    [Test]
    public async Task Matching_If_Match_Should_Succeed_And_Bump()
    {
        await SeedCollectionWithVersion(1, version: 2);

        Either<BaseError, Unit> result = await MakeHandler().Handle(
            new UpdateCollectionCustomOrder(1, [], Some(new[] { 2 }.ToSeq())),
            CancellationToken.None);

        result.IsRight.ShouldBeTrue();
        (await ReadVersion(1)).ShouldBe(3);
    }
}
