using CashLineERP.Application.Inventory;
using CashLineERP.Contracts.Inventory;
using CashLineERP.Infrastructure.Persistence.Repositories;

namespace CashLineERP.Api.Tests;

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

        var adjustmentService = new InventoryAdjustmentService(new InventoryAdjustmentRepository(dbContext));
        var transferService = new WarehouseTransferService(new WarehouseTransferRepository(dbContext));
        var movementService = new InventoryMovementService(new InventoryMovementReadRepository(dbContext));

        var postedAdjustment = PostAdjustment(
            adjustmentService,
            new CreateInventoryAdjustmentLineRequest(1001, 101, 4m, "EA", 20m, "AVAILABLE", "LOT-TRACE-1001-A", null, "Lot receipt baseline"),
            adjustmentDate: new DateOnly(2026, 5, 30),
            submitUserId: 71,
            approveUserId: 72,
            postUserId: 73);

        Assert.Equal("POSTED", postedAdjustment.StatusCode);

        var postedTransfer = PostTransfer(
            transferService,
            new CreateWarehouseTransferLineRequest(1001, 101, 201, 2m, "EA", 20m, "AVAILABLE", "LOT-TRACE-1001-A", null, "Lot move"),
            transferDate: new DateOnly(2026, 5, 31),
            submitUserId: 74,
            approveUserId: 75,
            postUserId: 76);

        Assert.Equal("POSTED", postedTransfer.StatusCode);

        var traceability = movementService.GetLotTraceability("LOT-TRACE-1001-A");

        Assert.NotNull(traceability);
        Assert.Equal("LOT", traceability!.TrackingType);
        Assert.Equal(4m, traceability.NetTrackedQuantity);
        Assert.False(traceability.HasLocationConflict);
        Assert.Equal(new long[] { 1001 }, traceability.ItemIds);
        Assert.Contains(traceability.Positions, position => position.WarehouseId == 1 && position.NetQuantity == 2m);
        Assert.Contains(traceability.Positions, position => position.WarehouseId == 2 && position.NetQuantity == 2m);
        Assert.Contains(traceability.Movements, movement => movement.MovementType == "ADJUSTMENT_GAIN" && movement.BatchNumber == "LOT-TRACE-1001-A");
        Assert.Contains(traceability.Movements, movement => movement.MovementType == "WAREHOUSE_TRANSFER" && movement.BatchNumber == "LOT-TRACE-1001-A");
    }

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

        var adjustmentService = new InventoryAdjustmentService(new InventoryAdjustmentRepository(dbContext));
        var transferService = new WarehouseTransferService(new WarehouseTransferRepository(dbContext));
        var movementService = new InventoryMovementService(new InventoryMovementReadRepository(dbContext));

        var serialError = Assert.Throws<InvalidOperationException>(() =>
            adjustmentService.CreateInventoryAdjustment(new CreateInventoryAdjustmentRequest(
                BranchId: 1,
                WarehouseId: 1,
                AdjustmentDate: new DateOnly(2026, 5, 27),
                ReasonCode: "SERIAL_GAIN",
                Notes: "Invalid serial quantity.",
                Lines:
                [
                    new CreateInventoryAdjustmentLineRequest(1001, 101, 2m, "EA", 22m, "AVAILABLE", null, "SER-TRACE-1001-ERR", "Invalid serial")
                ])));

        Assert.Contains("serial", serialError.Message, StringComparison.OrdinalIgnoreCase);

        var postedAdjustment = PostAdjustment(
            adjustmentService,
            new CreateInventoryAdjustmentLineRequest(1001, 101, 1m, "EA", 22m, "AVAILABLE", null, "SER-TRACE-1001-001", "Serial receipt baseline"),
            adjustmentDate: new DateOnly(2026, 5, 28),
            submitUserId: 81,
            approveUserId: 82,
            postUserId: 83);

        Assert.Equal("POSTED", postedAdjustment.StatusCode);

        var postedTransfer = PostTransfer(
            transferService,
            new CreateWarehouseTransferLineRequest(1001, 101, 201, 1m, "EA", 22m, "AVAILABLE", null, "SER-TRACE-1001-001", "Serial move"),
            transferDate: new DateOnly(2026, 5, 29),
            submitUserId: 84,
            approveUserId: 85,
            postUserId: 86);

        Assert.Equal("POSTED", postedTransfer.StatusCode);

        var traceability = movementService.GetSerialTraceability("SER-TRACE-1001-001");

        Assert.NotNull(traceability);
        Assert.Equal("SERIAL", traceability!.TrackingType);
        Assert.Equal(1m, traceability.NetTrackedQuantity);
        Assert.False(traceability.HasLocationConflict);
        Assert.Single(traceability.Positions);
        Assert.Equal(2, traceability.Positions.Single().WarehouseId);
        Assert.Equal(1m, traceability.Positions.Single().NetQuantity);
        Assert.All(traceability.Movements, movement => Assert.Equal("SER-TRACE-1001-001", movement.SerialNumber));
    }

    private static InventoryAdjustmentResponse PostAdjustment(
        InventoryAdjustmentService adjustmentService,
        CreateInventoryAdjustmentLineRequest line,
        DateOnly adjustmentDate,
        long submitUserId,
        long approveUserId,
        long postUserId)
    {
        var created = adjustmentService.CreateInventoryAdjustment(new CreateInventoryAdjustmentRequest(
            BranchId: 1,
            WarehouseId: 1,
            AdjustmentDate: adjustmentDate,
            ReasonCode: "TRACEABILITY",
            Notes: "Traceability baseline adjustment.",
            Lines: [line]));

        adjustmentService.SubmitInventoryAdjustment(created.InventoryAdjustmentId, submitUserId);
        adjustmentService.ApproveInventoryAdjustment(created.InventoryAdjustmentId, approveUserId);
        return adjustmentService.PostInventoryAdjustment(created.InventoryAdjustmentId, postUserId);
    }

    private static WarehouseTransferResponse PostTransfer(
        WarehouseTransferService transferService,
        CreateWarehouseTransferLineRequest line,
        DateOnly transferDate,
        long submitUserId,
        long approveUserId,
        long postUserId)
    {
        var created = transferService.CreateWarehouseTransfer(new CreateWarehouseTransferRequest(
            BranchId: 1,
            FromWarehouseId: 1,
            ToWarehouseId: 2,
            TransferDate: transferDate,
            ReasonCode: "TRACEABILITY",
            Notes: "Traceability baseline transfer.",
            Lines: [line]));

        transferService.SubmitWarehouseTransfer(created.WarehouseTransferId, submitUserId);
        transferService.ApproveWarehouseTransfer(created.WarehouseTransferId, approveUserId);
        return transferService.PostWarehouseTransfer(created.WarehouseTransferId, postUserId);
    }
}
