using OmsorgCore.Application.Abstractions;
using OmsorgCore.Application.Services;
using OmsorgCore.Tests.TestDoubles;

namespace OmsorgCore.Tests.Services;

public class ValueListServiceTests
{
    private class FakeUsageChecker : IValueListUsageChecker
    {
        private readonly Dictionary<Guid, List<ValueListUsageEntry>> _usagesByItemId = new();

        public string Key { get; }

        public FakeUsageChecker(string key)
        {
            Key = key;
        }

        public void SetUsages(Guid itemId, params ValueListUsageEntry[] usages)
            => _usagesByItemId[itemId] = usages.ToList();

        public Task<IReadOnlyList<ValueListUsageEntry>> FindUsagesAsync(Guid itemId, string itemValue, CancellationToken cancellationToken = default)
            => Task.FromResult<IReadOnlyList<ValueListUsageEntry>>(_usagesByItemId.GetValueOrDefault(itemId, new List<ValueListUsageEntry>()));
    }

    private static (FakeValueListRepository Repository, FakeUsageChecker Checker, ValueListService Sut) CreateSut()
    {
        var repository = new FakeValueListRepository();
        var checker = new FakeUsageChecker("EmployeeStatus");
        var sut = new ValueListService(repository, new[] { checker });
        return (repository, checker, sut);
    }

    [Fact]
    public async Task DeleteItemAsync_NotInUse_DeletesItem()
    {
        var (repository, _, sut) = CreateSut();
        var item = repository.AddStatus("Aktiv", key: "EmployeeStatus");

        var result = await sut.DeleteItemAsync(item.Id);

        Assert.True(result.Success);
        Assert.Null(await repository.GetItemByIdAsync(item.Id));
    }

    [Fact]
    public async Task DeleteItemAsync_InUse_RefusesDeletionAndReturnsUsages()
    {
        var (repository, checker, sut) = CreateSut();
        var item = repository.AddStatus("Aktiv", key: "EmployeeStatus");
        var employeeId = Guid.NewGuid();
        checker.SetUsages(item.Id, new ValueListUsageEntry("Employee", employeeId, "Max Mustermann"));

        var result = await sut.DeleteItemAsync(item.Id);

        Assert.False(result.Success);
        Assert.Equal(DeleteValueListItemFailureReason.InUse, result.FailureReason);
        Assert.Single(result.Usages);
        Assert.Equal(employeeId, result.Usages[0].EntityId);
        Assert.NotNull(await repository.GetItemByIdAsync(item.Id));
    }

    [Fact]
    public async Task DeleteItemAsync_UnknownId_ReturnsItemNotFoundFailure()
    {
        var (_, _, sut) = CreateSut();

        var result = await sut.DeleteItemAsync(Guid.NewGuid());

        Assert.False(result.Success);
        Assert.Equal(DeleteValueListItemFailureReason.ItemNotFound, result.FailureReason);
    }

    [Fact]
    public async Task GetUsagesAsync_ReturnsUsagesWithoutDeleting()
    {
        var (repository, checker, sut) = CreateSut();
        var item = repository.AddStatus("Aktiv", key: "EmployeeStatus");
        checker.SetUsages(item.Id, new ValueListUsageEntry("Employee", Guid.NewGuid(), "Erika Musterfrau"));

        var usages = await sut.GetUsagesAsync(item.Id);

        Assert.Single(usages);
        Assert.NotNull(await repository.GetItemByIdAsync(item.Id));
    }
}
