using OmsorgCore.Application.Services;
using OmsorgCore.Domain.Entities;
using OmsorgCore.Tests.TestDoubles;

namespace OmsorgCore.Tests.Services;

public class EmployeeFacilityDistanceServiceTests
{
    [Fact]
    public async Task CreateAsync_PersistsAllFields()
    {
        var repository = new FakeEmployeeFacilityDistanceRepository();
        var sut = new EmployeeFacilityDistanceService(repository);
        var facilityId = Guid.NewGuid();
        var employeeId = Guid.NewGuid();

        var distance = new EmployeeFacilityDistance
        {
            FacilityId = facilityId,
            EmployeeId = employeeId,
            DistanceKm = 12.5m
        };

        var created = await sut.CreateAsync(distance);

        Assert.Equal(facilityId, created.FacilityId);
        Assert.Equal(employeeId, created.EmployeeId);
        Assert.Equal(12.5m, created.DistanceKm);
    }

    [Fact]
    public async Task GetByFacilityIdAsync_ReturnsOnlyDistancesOfThatFacility()
    {
        var repository = new FakeEmployeeFacilityDistanceRepository();
        var sut = new EmployeeFacilityDistanceService(repository);
        var facilityIdA = Guid.NewGuid();
        var facilityIdB = Guid.NewGuid();

        await sut.CreateAsync(new EmployeeFacilityDistance { FacilityId = facilityIdA, EmployeeId = Guid.NewGuid(), DistanceKm = 5 });
        await sut.CreateAsync(new EmployeeFacilityDistance { FacilityId = facilityIdA, EmployeeId = Guid.NewGuid(), DistanceKm = 8 });
        await sut.CreateAsync(new EmployeeFacilityDistance { FacilityId = facilityIdB, EmployeeId = Guid.NewGuid(), DistanceKm = 3 });

        var distancesForA = await sut.GetByFacilityIdAsync(facilityIdA);

        Assert.Equal(2, distancesForA.Count);
        Assert.All(distancesForA, d => Assert.Equal(facilityIdA, d.FacilityId));
    }

    [Fact]
    public async Task GetByFacilityAndEmployeeAsync_FindsExistingPair()
    {
        var repository = new FakeEmployeeFacilityDistanceRepository();
        var sut = new EmployeeFacilityDistanceService(repository);
        var facilityId = Guid.NewGuid();
        var employeeId = Guid.NewGuid();

        await sut.CreateAsync(new EmployeeFacilityDistance { FacilityId = facilityId, EmployeeId = employeeId, DistanceKm = 7 });

        var found = await sut.GetByFacilityAndEmployeeAsync(facilityId, employeeId);
        var notFound = await sut.GetByFacilityAndEmployeeAsync(facilityId, Guid.NewGuid());

        Assert.NotNull(found);
        Assert.Equal(7, found!.DistanceKm);
        Assert.Null(notFound);
    }

    [Fact]
    public async Task UpdateAsync_UpdatesDistanceKm_AndReturnsUpdatedEntity()
    {
        var repository = new FakeEmployeeFacilityDistanceRepository();
        var sut = new EmployeeFacilityDistanceService(repository);
        var created = await sut.CreateAsync(new EmployeeFacilityDistance { FacilityId = Guid.NewGuid(), EmployeeId = Guid.NewGuid(), DistanceKm = 5 });

        var updated = await sut.UpdateAsync(created.Id, new EmployeeFacilityDistance { DistanceKm = 9.5m });

        Assert.NotNull(updated);
        Assert.Equal(9.5m, updated!.DistanceKm);
        Assert.NotNull(updated.UpdatedAt);
    }

    [Fact]
    public async Task UpdateAsync_UnknownId_ReturnsNull()
    {
        var repository = new FakeEmployeeFacilityDistanceRepository();
        var sut = new EmployeeFacilityDistanceService(repository);

        var updated = await sut.UpdateAsync(Guid.NewGuid(), new EmployeeFacilityDistance { DistanceKm = 1 });

        Assert.Null(updated);
    }

    [Fact]
    public async Task DeleteAsync_ThenGetDeletedAsync_ReturnsSoftDeletedDistance()
    {
        var repository = new FakeEmployeeFacilityDistanceRepository();
        var sut = new EmployeeFacilityDistanceService(repository);
        var created = await sut.CreateAsync(new EmployeeFacilityDistance { FacilityId = Guid.NewGuid(), EmployeeId = Guid.NewGuid(), DistanceKm = 5 });

        var deleted = await sut.DeleteAsync(created.Id);
        var deletedList = await sut.GetDeletedAsync(null);

        Assert.True(deleted);
        Assert.Contains(deletedList, d => d.Id == created.Id);
    }

    [Fact]
    public async Task RestoreAsync_UndoesSoftDelete()
    {
        var repository = new FakeEmployeeFacilityDistanceRepository();
        var sut = new EmployeeFacilityDistanceService(repository);
        var created = await sut.CreateAsync(new EmployeeFacilityDistance { FacilityId = Guid.NewGuid(), EmployeeId = Guid.NewGuid(), DistanceKm = 5 });
        await sut.DeleteAsync(created.Id);

        var restored = await sut.RestoreAsync(created.Id);
        var deletedList = await sut.GetDeletedAsync(null);

        Assert.True(restored);
        Assert.DoesNotContain(deletedList, d => d.Id == created.Id);
    }
}
