using ErsatzTV.Application.Emby;
using ErsatzTV.Core;
using ErsatzTV.Core.Domain;
using ErsatzTV.Core.Interfaces.Emby;
using ErsatzTV.Core.Interfaces.Locking;
using ErsatzTV.Core.Interfaces.Repositories;
using ErsatzTV.Core.Interfaces.Search;
using LanguageExt;
using NSubstitute;
using NSubstitute.ExceptionExtensions;
using NUnit.Framework;
using Shouldly;
using Unit = LanguageExt.Unit;

namespace ErsatzTV.Tests.Application.Emby;

[TestFixture]
public class DisconnectEmbyHandlerTests
{
    private IMediaSourceRepository _mediaSourceRepository = null!;
    private IEmbySecretStore _embySecretStore = null!;
    private IEntityLocker _entityLocker = null!;
    private ISearchIndex _searchIndex = null!;
    private DisconnectEmbyHandler _handler = null!;

    [SetUp]
    public void SetUp()
    {
        _mediaSourceRepository = Substitute.For<IMediaSourceRepository>();
        _embySecretStore = Substitute.For<IEmbySecretStore>();
        _entityLocker = Substitute.For<IEntityLocker>();
        _searchIndex = Substitute.For<ISearchIndex>();
        _handler = new DisconnectEmbyHandler(
            _mediaSourceRepository,
            _embySecretStore,
            _entityLocker,
            _searchIndex);
    }

    [Test]
    public async Task Handle_Should_Release_Lock_On_Success()
    {
        _mediaSourceRepository.DeleteAllEmby().Returns(new List<int>());

        Either<BaseError, Unit> result = await _handler.Handle(new DisconnectEmby(), CancellationToken.None);

        result.IsRight.ShouldBeTrue();
        _entityLocker.Received(1).UnlockRemoteMediaSource<EmbyMediaSource>();
    }

    [Test]
    public async Task Handle_Should_Release_Lock_When_Repository_Delete_Throws()
    {
        _mediaSourceRepository.DeleteAllEmby().ThrowsAsync(new InvalidOperationException("db exploded"));

        await Should.ThrowAsync<InvalidOperationException>(
            async () => await _handler.Handle(new DisconnectEmby(), CancellationToken.None));

        _entityLocker.Received(1).UnlockRemoteMediaSource<EmbyMediaSource>();
    }

    [Test]
    public async Task Handle_Should_Release_Lock_When_SearchIndex_RemoveItems_Throws()
    {
        _mediaSourceRepository.DeleteAllEmby().Returns([1, 2]);
        _searchIndex.RemoveItems(Arg.Any<IEnumerable<int>>()).ThrowsAsync(new InvalidOperationException("index down"));

        await Should.ThrowAsync<InvalidOperationException>(
            async () => await _handler.Handle(new DisconnectEmby(), CancellationToken.None));

        _entityLocker.Received(1).UnlockRemoteMediaSource<EmbyMediaSource>();
    }

    [Test]
    public async Task Handle_Should_Release_Lock_When_SecretStore_DeleteAll_Throws()
    {
        _mediaSourceRepository.DeleteAllEmby().Returns(new List<int>());
        _embySecretStore.DeleteAll().ThrowsAsync(new InvalidOperationException("secret store down"));

        await Should.ThrowAsync<InvalidOperationException>(
            async () => await _handler.Handle(new DisconnectEmby(), CancellationToken.None));

        _entityLocker.Received(1).UnlockRemoteMediaSource<EmbyMediaSource>();
    }
}
