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

namespace OmsorgCore.Tests.Services;

public class OrderServiceTests
{
    private static (FakeOrderRepository Orders, FakeValueListRepository Statuses, OrderService Sut, ValueListItem Anfrage, ValueListItem Pruefung, ValueListItem Aktiv, ValueListItem Storniert) CreateSut()
    {
        var orders = new FakeOrderRepository();
        var statuses = new FakeValueListRepository();

        var anfrage = statuses.AddStatus("Anfrage", isInitial: true);
        var pruefung = statuses.AddStatus("Prüfung");
        var aktiv = statuses.AddStatus("aktiv");
        var storniert = statuses.AddStatus("storniert", isTerminal: true);

        statuses.AllowTransition(anfrage.Id, pruefung.Id);
        statuses.AllowTransition(anfrage.Id, storniert.Id);
        statuses.AllowTransition(pruefung.Id, storniert.Id);
        statuses.AllowTransition(aktiv.Id, storniert.Id);

        var sut = new OrderService(orders, statuses);
        return (orders, statuses, sut, anfrage, pruefung, aktiv, storniert);
    }

    [Fact]
    public async Task CreateAsync_SetsInitialStatus_AndPersistsAllFields()
    {
        var (_, _, sut, anfrage, _, _, _) = CreateSut();
        var facilityId = Guid.NewGuid();
        var contactId = Guid.NewGuid();

        var order = new Order
        {
            FacilityId = facilityId,
            FacilityContactId = contactId,
            StartDate = new DateOnly(2026, 1, 1),
            EndDate = new DateOnly(2026, 3, 1),
            RequiredQualification = "Pflegefachkraft",
            ShiftType = "Nachtdienst",
            RequiredHeadcount = 3,
            Conditions = "Zuschlag 15%",
            Priority = "Hoch"
        };

        var created = await sut.CreateAsync(order);
        var stored = await sut.GetByIdAsync(created.Id);

        Assert.NotNull(stored);
        Assert.Equal(anfrage.Id, stored!.StatusId);
        Assert.Equal(facilityId, stored.FacilityId);
        Assert.Equal(contactId, stored.FacilityContactId);
        Assert.Equal(3, stored.RequiredHeadcount);
        Assert.Equal("Hoch", stored.Priority);
    }

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

        await sut.CreateAsync(new Order { FacilityId = Guid.NewGuid(), StartDate = new DateOnly(2026, 1, 1), Priority = "Normal" });

        var all = await sut.GetAllAsync();

        Assert.Single(all);
    }

    [Fact]
    public async Task UpdateAsync_ValidTransition_UpdatesFieldsAndReturnsOk()
    {
        var (_, _, sut, _, pruefung, _, _) = CreateSut();
        var created = await sut.CreateAsync(new Order { FacilityId = Guid.NewGuid(), StartDate = new DateOnly(2026, 1, 1), Priority = "Normal" });

        var updates = new Order
        {
            FacilityId = created.FacilityId,
            StartDate = created.StartDate,
            RequiredHeadcount = 2,
            Priority = "Hoch",
            StatusId = pruefung.Id
        };

        var result = await sut.UpdateAsync(created.Id, updates);

        Assert.True(result.Success);
        Assert.Equal(pruefung.Id, result.Order!.StatusId);
        Assert.Equal("Hoch", result.Order.Priority);
        Assert.NotNull(result.Order.UpdatedAt);
    }

    [Fact]
    public async Task UpdateAsync_SameStatus_Succeeds()
    {
        var (_, _, sut, anfrage, _, _, _) = CreateSut();
        var created = await sut.CreateAsync(new Order { FacilityId = Guid.NewGuid(), StartDate = new DateOnly(2026, 1, 1), Priority = "Normal" });

        var updates = new Order
        {
            FacilityId = created.FacilityId,
            StartDate = created.StartDate,
            RequiredHeadcount = 5,
            Priority = "Normal",
            StatusId = anfrage.Id
        };

        var result = await sut.UpdateAsync(created.Id, updates);

        Assert.True(result.Success);
        Assert.Equal(5, result.Order!.RequiredHeadcount);
    }

    [Fact]
    public async Task UpdateAsync_InvalidTransition_ReturnsInvalidStatusTransitionFailure_AndLeavesStatusUnchanged()
    {
        var (_, _, sut, anfrage, _, aktiv, _) = CreateSut();
        var created = await sut.CreateAsync(new Order { FacilityId = Guid.NewGuid(), StartDate = new DateOnly(2026, 1, 1), Priority = "Normal" });

        var updates = new Order
        {
            FacilityId = created.FacilityId,
            StartDate = created.StartDate,
            Priority = "Normal",
            StatusId = aktiv.Id
        };

        var result = await sut.UpdateAsync(created.Id, updates);

        Assert.False(result.Success);
        Assert.Equal(UpdateOrderFailureReason.InvalidStatusTransition, result.FailureReason);

        var stored = await sut.GetByIdAsync(created.Id);
        Assert.Equal(anfrage.Id, stored!.StatusId);
    }

    [Fact]
    public async Task UpdateAsync_TerminalStatus_RejectsFurtherTransitions()
    {
        var (_, _, sut, anfrage, _, _, storniert) = CreateSut();
        var created = await sut.CreateAsync(new Order { FacilityId = Guid.NewGuid(), StartDate = new DateOnly(2026, 1, 1), Priority = "Normal" });
        await sut.UpdateAsync(created.Id, new Order { FacilityId = created.FacilityId, StartDate = created.StartDate, Priority = "Normal", StatusId = storniert.Id });

        var result = await sut.UpdateAsync(created.Id, new Order { FacilityId = created.FacilityId, StartDate = created.StartDate, Priority = "Normal", StatusId = anfrage.Id });

        Assert.False(result.Success);
        Assert.Equal(UpdateOrderFailureReason.InvalidStatusTransition, result.FailureReason);
    }

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

        var result = await sut.UpdateAsync(Guid.NewGuid(), new Order { FacilityId = Guid.NewGuid(), StartDate = new DateOnly(2026, 1, 1), Priority = "Normal", StatusId = anfrage.Id });

        Assert.False(result.Success);
        Assert.Equal(UpdateOrderFailureReason.OrderNotFound, result.FailureReason);
    }

    [Fact]
    public async Task GetPagedAsync_FiltersByStatusAndFacility_AndPaginates()
    {
        var (_, _, sut, anfrage, pruefung, _, _) = CreateSut();
        var facilityA = Guid.NewGuid();
        var facilityB = Guid.NewGuid();

        var first = await sut.CreateAsync(new Order { FacilityId = facilityA, StartDate = new DateOnly(2026, 1, 1), Priority = "Normal" });
        await sut.CreateAsync(new Order { FacilityId = facilityB, StartDate = new DateOnly(2026, 2, 1), Priority = "Normal" });
        await sut.UpdateAsync(first.Id, new Order { FacilityId = facilityA, StartDate = first.StartDate, Priority = "Normal", StatusId = pruefung.Id });

        var (items, totalCount) = await sut.GetPagedAsync(search: null, statusId: pruefung.Id, facilityId: facilityA, priority: null, requiredQualification: null, shiftType: null, page: 1, pageSize: 10);

        Assert.Equal(1, totalCount);
        Assert.Single(items);
        Assert.Equal(facilityA, items[0].FacilityId);
    }
}
