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

namespace CashLineERP.Api.Tests;

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

        var replenishmentService = new ReplenishmentRuleService(new ReplenishmentRuleRepository(dbContext));

        dbContext.InventoryMovements.Add(new InventoryMovement(
            movementNumber: "IM-QA-RPL-000001",
            movementType: "GRN_RECEIPT",
            branchId: 1,
            itemId: 1001,
            fromWarehouseId: null,
            fromWarehouseLocationId: null,
            toWarehouseId: 2,
            toWarehouseLocationId: null,
            batchNumber: "RPL-QA-1001-TGT",
            serialNumber: null,
            stockStatus: "AVAILABLE",
            quantityMoved: 3m,
            unitCost: 24m,
            movedAtUtc: new DateTime(2026, 5, 20, 9, 0, 0, DateTimeKind.Utc)));
        dbContext.SaveChanges();

        var createdRule = replenishmentService.CreateReplenishmentRule(new CreateReplenishmentRuleRequest(
            RuleName: "Warehouse 2 item 1001 replenishment",
            BranchId: 1,
            TargetWarehouseId: 2,
            PreferredSourceWarehouseId: 1,
            ItemId: 1001,
            UnitOfMeasureCode: "EA",
            ReorderPointQuantity: 10m,
            TargetSafetyStockQuantity: 15m,
            TargetStockQuantity: 25m,
            SourceSafetyStockQuantity: 20m,
            Priority: 100,
            EffectiveFromDate: new DateOnly(2026, 5, 1),
            EffectiveToDate: new DateOnly(2026, 5, 31),
            Notes: "Primary warehouse replenishment rule"));

        Assert.Equal("DRAFT", createdRule.StatusCode);
        Assert.False(createdRule.IsActive);

        var submittedRule = replenishmentService.SubmitReplenishmentRule(createdRule.ReplenishmentRuleId, 2);
        Assert.Equal("SUBMITTED", submittedRule.StatusCode);

        var selfApprovalException = Assert.Throws<InvalidOperationException>(() =>
            replenishmentService.ApproveReplenishmentRule(createdRule.ReplenishmentRuleId, 2));
        Assert.Contains("self-approval", selfApprovalException.Message, StringComparison.OrdinalIgnoreCase);

        var approvedRule = replenishmentService.ApproveReplenishmentRule(createdRule.ReplenishmentRuleId, 4);
        Assert.Equal("APPROVED", approvedRule.StatusCode);

        var activeRule = replenishmentService.ActivateReplenishmentRule(createdRule.ReplenishmentRuleId, 4);
        Assert.True(activeRule.IsActive);

        var evaluation = replenishmentService.EvaluateReplenishment(new ReplenishmentEvaluationRequest(
            BranchId: 1,
            AsOfDate: new DateOnly(2026, 5, 21),
            TargetWarehouseId: 2,
            ItemId: 1001));

        var line = Assert.Single(evaluation.Lines);
        Assert.Equal(activeRule.ReplenishmentRuleId, line.ReplenishmentRuleId);
        Assert.Equal(2, line.TargetWarehouseId);
        Assert.Equal(1, line.SourceWarehouseId);
        Assert.Equal(15m, line.TargetSafetyStockQuantity);
        Assert.Equal(3m, line.TargetOnHandQuantity);
        Assert.Equal(22m, line.TargetShortfallQuantity);
        Assert.Equal(50m, line.SourceOnHandQuantity);
        Assert.Equal(30m, line.SourceAvailableToTransferQuantity);
        Assert.Equal(22m, line.RecommendedTransferQuantity);
        Assert.Equal("REPLENISHMENT_REQUIRED", line.RecommendationStatusCode);
    }

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

        var replenishmentService = new ReplenishmentRuleService(new ReplenishmentRuleRepository(dbContext));

        dbContext.InventoryMovements.Add(new InventoryMovement(
            movementNumber: "IM-QA-RPL-000002",
            movementType: "DISPATCH",
            branchId: 1,
            itemId: 1001,
            fromWarehouseId: 1,
            fromWarehouseLocationId: null,
            toWarehouseId: null,
            toWarehouseLocationId: null,
            batchNumber: "RPL-QA-1001-MOVE",
            serialNumber: null,
            stockStatus: "AVAILABLE",
            quantityMoved: 45m,
            unitCost: 24m,
            movedAtUtc: new DateTime(2026, 5, 21, 9, 0, 0, DateTimeKind.Utc)));
        dbContext.InventoryMovements.Add(new InventoryMovement(
            movementNumber: "IM-QA-RPL-000003",
            movementType: "GRN_RECEIPT",
            branchId: 1,
            itemId: 1001,
            fromWarehouseId: null,
            fromWarehouseLocationId: null,
            toWarehouseId: 3,
            toWarehouseLocationId: null,
            batchNumber: "RPL-QA-1001-ALT",
            serialNumber: null,
            stockStatus: "AVAILABLE",
            quantityMoved: 35m,
            unitCost: 24m,
            movedAtUtc: new DateTime(2026, 5, 21, 10, 0, 0, DateTimeKind.Utc)));
        dbContext.SaveChanges();

        var rule = replenishmentService.ActivateReplenishmentRule(
            replenishmentService.ApproveReplenishmentRule(
                replenishmentService.SubmitReplenishmentRule(
                    replenishmentService.CreateReplenishmentRule(new CreateReplenishmentRuleRequest(
                        RuleName: "Fallback source replenishment",
                        BranchId: 1,
                        TargetWarehouseId: 2,
                        PreferredSourceWarehouseId: 1,
                        ItemId: 1001,
                        UnitOfMeasureCode: "EA",
                        ReorderPointQuantity: 12m,
                        TargetSafetyStockQuantity: 12.5m,
                        TargetStockQuantity: 13m,
                        SourceSafetyStockQuantity: 10m,
                        Priority: 90,
                        EffectiveFromDate: new DateOnly(2026, 5, 1),
                        EffectiveToDate: new DateOnly(2026, 5, 31),
                        Notes: "Fallback source evaluation")).ReplenishmentRuleId,
                    2).ReplenishmentRuleId,
                4).ReplenishmentRuleId,
            4);

        var evaluation = replenishmentService.EvaluateReplenishment(new ReplenishmentEvaluationRequest(
            BranchId: 1,
            AsOfDate: new DateOnly(2026, 5, 21),
            TargetWarehouseId: 2,
            ItemId: 1001));

        var line = evaluation.Lines.Single(entry => entry.ReplenishmentRuleId == rule.ReplenishmentRuleId);
        Assert.Equal(3, line.SourceWarehouseId);
        Assert.Equal("REPLENISHMENT_REQUIRED", line.RecommendationStatusCode);
        Assert.Equal(35m, line.SourceOnHandQuantity);
        Assert.Equal(25m, line.SourceAvailableToTransferQuantity);
        Assert.Equal(13m, line.RecommendedTransferQuantity);
    }

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

        var replenishmentService = new ReplenishmentRuleService(new ReplenishmentRuleRepository(dbContext));

        dbContext.InventoryMovements.Add(new InventoryMovement(
            movementNumber: "IM-QA-RPL-000004",
            movementType: "GRN_RECEIPT",
            branchId: 1,
            itemId: 1002,
            fromWarehouseId: null,
            fromWarehouseLocationId: null,
            toWarehouseId: 2,
            toWarehouseLocationId: null,
            batchNumber: "RPL-QA-1002-TGT",
            serialNumber: null,
            stockStatus: "AVAILABLE",
            quantityMoved: 9m,
            unitCost: 40m,
            movedAtUtc: new DateTime(2026, 5, 22, 8, 0, 0, DateTimeKind.Utc)));
        dbContext.SaveChanges();

        var rule = replenishmentService.ActivateReplenishmentRule(
            replenishmentService.ApproveReplenishmentRule(
                replenishmentService.SubmitReplenishmentRule(
                    replenishmentService.CreateReplenishmentRule(new CreateReplenishmentRuleRequest(
                        RuleName: "Safety stock monitor and procurement",
                        BranchId: 1,
                        TargetWarehouseId: 2,
                        PreferredSourceWarehouseId: 1,
                        ItemId: 1002,
                        UnitOfMeasureCode: "BOX",
                        ReorderPointQuantity: 8m,
                        TargetSafetyStockQuantity: 12m,
                        TargetStockQuantity: 20m,
                        SourceSafetyStockQuantity: 15m,
                        Priority: 95,
                        EffectiveFromDate: new DateOnly(2026, 5, 1),
                        EffectiveToDate: new DateOnly(2026, 5, 31),
                        Notes: "Safety stock planning baseline")).ReplenishmentRuleId,
                    2).ReplenishmentRuleId,
                4).ReplenishmentRuleId,
            4);

        var monitorPlan = replenishmentService.BuildReorderPlan(new ReorderPlanningRequest(
            BranchId: 1,
            AsOfDate: new DateOnly(2026, 5, 22),
            TargetWarehouseId: 2,
            ItemId: 1002));

        var monitorLine = monitorPlan.Lines.Single(entry => entry.ReplenishmentRuleId == rule.ReplenishmentRuleId);
        Assert.False(monitorLine.IsBelowReorderPoint);
        Assert.True(monitorLine.IsBelowSafetyStock);
        Assert.Equal("MONITOR", monitorLine.PlanningActionCode);
        Assert.Equal(11m, monitorLine.RecommendedReorderQuantity);
        Assert.Equal(0m, monitorLine.RecommendedTransferQuantity);
        Assert.Equal(0m, monitorLine.RecommendedProcurementQuantity);

        dbContext.InventoryMovements.Add(new InventoryMovement(
            movementNumber: "IM-QA-RPL-000005",
            movementType: "DISPATCH",
            branchId: 1,
            itemId: 1002,
            fromWarehouseId: 2,
            fromWarehouseLocationId: null,
            toWarehouseId: null,
            toWarehouseLocationId: null,
            batchNumber: "RPL-QA-1002-OUT",
            serialNumber: null,
            stockStatus: "AVAILABLE",
            quantityMoved: 3m,
            unitCost: 40m,
            movedAtUtc: new DateTime(2026, 5, 22, 9, 0, 0, DateTimeKind.Utc)));
        dbContext.SaveChanges();

        var procurementPlan = replenishmentService.BuildReorderPlan(new ReorderPlanningRequest(
            BranchId: 1,
            AsOfDate: new DateOnly(2026, 5, 22),
            TargetWarehouseId: 2,
            ItemId: 1002));

        var procurementLine = procurementPlan.Lines.Single(entry => entry.ReplenishmentRuleId == rule.ReplenishmentRuleId);
        Assert.True(procurementLine.IsBelowReorderPoint);
        Assert.True(procurementLine.IsBelowSafetyStock);
        Assert.Equal("REPLENISH_PROCUREMENT", procurementLine.PlanningActionCode);
        Assert.Equal(14m, procurementLine.RecommendedReorderQuantity);
        Assert.Equal(0m, procurementLine.RecommendedTransferQuantity);
        Assert.Equal(14m, procurementLine.RecommendedProcurementQuantity);
    }
}
