using CashLineERP.Application.Internal;
using CashLineERP.Application.Sales;
using CashLineERP.Contracts.Sales;
using CashLineERP.Infrastructure.Persistence.Repositories;

namespace CashLineERP.Api.Tests;

public sealed class DeliveryExceptionRecoveryServiceQaTests
{
    [Fact]
    public void Dispatch_Delivery_Exception_Captures_Failed_Quantities_And_Partial_Delivery()
    {
        using var dbContext = QaDbContextFactory.CreateContext();

        var service = new DispatchOrderService(new DispatchOrderRepository(dbContext));

        service.PostDispatchOrder(1, new PostDispatchOrderRequest(
            ActorUserId: 3,
            ShippedAtUtc: new DateTime(2026, 5, 12, 8, 30, 0, DateTimeKind.Utc),
            DeliveredAtUtc: null,
            ProofOfDeliveryReceiver: null));

        var exceptionDispatch = service.RecordDeliveryException(1, 4, new RecordDeliveryExceptionRequest(
            DeliveredAtUtc: new DateTime(2026, 5, 13, 9, 0, 0, DateTimeKind.Utc),
            DeliveryConditionCode: "FAILED",
            DeliveryExceptionCode: "CUSTOMER_CLOSED",
            ProofOfDeliveryReceiver: "Driver Attempt",
            ProofOfDeliveryReference: "FD-001",
            Notes: "Customer location closed at arrival.",
            Lines:
            [
                new RecordDeliveryExceptionLineRequest(1, 4m),
                new RecordDeliveryExceptionLineRequest(2, 20m)
            ]));

        Assert.Equal("FAILED", exceptionDispatch.DeliveryConditionCode);
        Assert.Equal("CUSTOMER_CLOSED", exceptionDispatch.DeliveryExceptionCode);
        Assert.Equal("PENDING", exceptionDispatch.DeliveryRecoveryStatusCode);
        Assert.Equal(6m, exceptionDispatch.Lines.Single(line => line.DispatchOrderLineId == 1).QuantityDelivered);
        Assert.Equal(4m, exceptionDispatch.Lines.Single(line => line.DispatchOrderLineId == 1).QuantityFailed);
        Assert.Equal(0m, exceptionDispatch.Lines.Single(line => line.DispatchOrderLineId == 2).QuantityDelivered);
        Assert.Equal(20m, exceptionDispatch.Lines.Single(line => line.DispatchOrderLineId == 2).QuantityFailed);

        var salesOrderLines = dbContext.SalesOrderLines.OrderBy(line => line.Id).ToArray();
        Assert.Equal(6m, salesOrderLines[0].DeliveredQuantity);
        Assert.Equal(0m, salesOrderLines[1].DeliveredQuantity);
    }

    [Fact]
    public void Failed_Delivery_Recovery_Creates_Return_Movements_And_Clears_Exception_Queue()
    {
        using var dbContext = QaDbContextFactory.CreateContext();

        var dispatchService = new DispatchOrderService(new DispatchOrderRepository(dbContext));
        var workbenchRepository = new InternalWorkbenchRepository(dbContext);

        dispatchService.PostDispatchOrder(1, new PostDispatchOrderRequest(
            ActorUserId: 3,
            ShippedAtUtc: new DateTime(2026, 5, 12, 8, 30, 0, DateTimeKind.Utc),
            DeliveredAtUtc: null,
            ProofOfDeliveryReceiver: null));

        dispatchService.RecordDeliveryException(1, 4, new RecordDeliveryExceptionRequest(
            DeliveredAtUtc: new DateTime(2026, 5, 13, 9, 0, 0, DateTimeKind.Utc),
            DeliveryConditionCode: "FAILED",
            DeliveryExceptionCode: "ADDRESS_ISSUE",
            ProofOfDeliveryReceiver: "Driver Attempt",
            ProofOfDeliveryReference: "FD-002",
            Notes: "Address mismatch prevented delivery.",
            Lines:
            [
                new RecordDeliveryExceptionLineRequest(1, 4m)
            ]));

        Assert.Contains(workbenchRepository.GetExceptionQueue("DELIVERY"), item => item.DocumentId == 1);

        var recoveredDispatch = dispatchService.RecoverFailedDelivery(1, 5, new RecoverFailedDeliveryRequest(
            RecoveredAtUtc: new DateTime(2026, 5, 13, 13, 15, 0, DateTimeKind.Utc),
            Notes: "Recovered to quarantine for review.",
            Lines:
            [
                new RecoverFailedDeliveryLineRequest(1, 4m, null, "QUARANTINE")
            ]));

        Assert.Equal("RECOVERED", recoveredDispatch.DeliveryRecoveryStatusCode);
        Assert.Equal(4m, recoveredDispatch.Lines.Single(line => line.DispatchOrderLineId == 1).QuantityRecovered);

        var recoveryMovement = dbContext.InventoryMovements.Single(movement => movement.MovementType == "FAILED_DELIVERY_RECOVERY");
        Assert.Equal("QUARANTINE", recoveryMovement.StockStatus);
        Assert.Equal(4m, recoveryMovement.QuantityMoved);

        Assert.DoesNotContain(workbenchRepository.GetExceptionQueue("DELIVERY"), item => item.DocumentId == 1);
    }
}
