using System.Net;
using System.Net.Http.Json;
using CashLineERP.Contracts.Internal;
using CashLineERP.Contracts.Inventory;
using CashLineERP.Contracts.Sales;

namespace CashLineERP.Api.Tests;

public sealed class DeliveryExceptionRecoveryQaTests : IClassFixture<ApiTestFactory>
{
    private readonly HttpClient _client;

    public DeliveryExceptionRecoveryQaTests(ApiTestFactory factory)
    {
        _client = factory.CreateClient();
    }

    [Fact]
    public async Task Delivery_Exception_And_Recovery_Endpoints_Work()
    {
        await _client.AuthenticateAsInternalAsync();

        var postResponse = await _client.PostAsJsonAsync("/api/v1/sales/dispatches/1/post", new PostDispatchOrderRequest(
            ActorUserId: 3,
            ShippedAtUtc: new DateTime(2026, 5, 12, 8, 30, 0, DateTimeKind.Utc),
            DeliveredAtUtc: null,
            ProofOfDeliveryReceiver: null));
        Assert.Equal(HttpStatusCode.OK, postResponse.StatusCode);

        var exceptionResponse = await _client.PostAsJsonAsync("/api/v1/sales/dispatches/1/delivery-exception", new RecordDeliveryExceptionRequest(
            DeliveredAtUtc: new DateTime(2026, 5, 13, 9, 0, 0, DateTimeKind.Utc),
            DeliveryConditionCode: "FAILED",
            DeliveryExceptionCode: "CUSTOMER_UNAVAILABLE",
            ProofOfDeliveryReceiver: "Driver Attempt",
            ProofOfDeliveryReference: "FD-HTTP-001",
            Notes: "Customer contact was unavailable.",
            Lines:
            [
                new RecordDeliveryExceptionLineRequest(1, 4m),
                new RecordDeliveryExceptionLineRequest(2, 10m)
            ]));
        Assert.Equal(HttpStatusCode.OK, exceptionResponse.StatusCode);

        var exceptionDispatch = await exceptionResponse.Content.ReadFromJsonAsync<DispatchOrderResponse>();
        Assert.NotNull(exceptionDispatch);
        Assert.Equal("CUSTOMER_UNAVAILABLE", exceptionDispatch!.DeliveryExceptionCode);
        Assert.Equal("PENDING", exceptionDispatch.DeliveryRecoveryStatusCode);

        var queueResponse = await _client.GetAsync("/api/v1/internal/work-queues/exceptions?sourceType=DELIVERY");
        Assert.Equal(HttpStatusCode.OK, queueResponse.StatusCode);
        var queue = await queueResponse.Content.ReadFromJsonAsync<IReadOnlyCollection<InternalExceptionQueueItemResponse>>();
        Assert.NotNull(queue);
        Assert.Contains(queue!, item => item.DocumentId == 1 && item.SourceType == "DELIVERY");

        var recoveryResponse = await _client.PostAsJsonAsync("/api/v1/sales/dispatches/1/recover-failed-delivery", new RecoverFailedDeliveryRequest(
            RecoveredAtUtc: new DateTime(2026, 5, 13, 14, 0, 0, DateTimeKind.Utc),
            Notes: "Returned to available and damaged segregation.",
            Lines:
            [
                new RecoverFailedDeliveryLineRequest(1, 4m, null, "AVAILABLE"),
                new RecoverFailedDeliveryLineRequest(2, 10m, null, "DAMAGED")
            ]));
        Assert.Equal(HttpStatusCode.OK, recoveryResponse.StatusCode);

        var recoveredDispatch = await recoveryResponse.Content.ReadFromJsonAsync<DispatchOrderResponse>();
        Assert.NotNull(recoveredDispatch);
        Assert.Equal("RECOVERED", recoveredDispatch!.DeliveryRecoveryStatusCode);
        Assert.Contains(recoveredDispatch.Lines, line => line.DispatchOrderLineId == 2 && line.QuantityRecovered == 10m);

        var movementResponse = await _client.GetAsync("/api/v1/inventory/movements");
        Assert.Equal(HttpStatusCode.OK, movementResponse.StatusCode);
        var movements = await movementResponse.Content.ReadFromJsonAsync<IReadOnlyCollection<InventoryMovementResponse>>();
        Assert.NotNull(movements);
        Assert.Contains(movements!, movement => movement.MovementType == "FAILED_DELIVERY_RECOVERY" && movement.StockStatus == "DAMAGED" && movement.QuantityMoved == 10m);

        var queueAfterResponse = await _client.GetAsync("/api/v1/internal/work-queues/exceptions?sourceType=DELIVERY");
        Assert.Equal(HttpStatusCode.OK, queueAfterResponse.StatusCode);
        var queueAfter = await queueAfterResponse.Content.ReadFromJsonAsync<IReadOnlyCollection<InternalExceptionQueueItemResponse>>();
        Assert.NotNull(queueAfter);
        Assert.DoesNotContain(queueAfter!, item => item.DocumentId == 1 && item.SourceType == "DELIVERY");
    }
}
