using System.Net;
using System.Net.Http.Json;
using CashLineERP.Application.Finance;
using CashLineERP.Application.Inventory;
using CashLineERP.Contracts.Inventory;
using CashLineERP.Infrastructure.Persistence.Repositories;

namespace CashLineERP.Api.Tests;

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

        var adjustmentService = new InventoryAdjustmentService(new InventoryAdjustmentRepository(dbContext));
        var inventoryMovementService = new InventoryMovementService(new InventoryMovementReadRepository(dbContext));
        var generalLedgerService = new GeneralLedgerService(new GeneralLedgerReadRepository(dbContext));

        var created = adjustmentService.CreateInventoryAdjustment(new CreateInventoryAdjustmentRequest(
            BranchId: 1,
            WarehouseId: 1,
            AdjustmentDate: new DateOnly(2026, 5, 4),
            ReasonCode: "COUNT_VARIANCE",
            Notes: "Cycle count variance adjustment.",
            Lines:
            [
                new CreateInventoryAdjustmentLineRequest(1001, null, 4m, "EA", 22m, "AVAILABLE", "COUNT-ADJ-GAIN-1", null, "Physical gain"),
                new CreateInventoryAdjustmentLineRequest(1002, null, -2m, "BOX", 35m, "AVAILABLE", "COUNT-ADJ-LOSS-1", null, "Physical loss")
            ]));

        Assert.Equal("DRAFT", created.StatusCode);
        Assert.Equal(158m, created.TotalAdjustmentAmount);

        var submitted = adjustmentService.SubmitInventoryAdjustment(created.InventoryAdjustmentId, 7);
        Assert.Equal("SUBMITTED", submitted.StatusCode);
        Assert.Equal(7, submitted.SubmittedByUserId);

        var approved = adjustmentService.ApproveInventoryAdjustment(created.InventoryAdjustmentId, 8);
        Assert.Equal("APPROVED", approved.StatusCode);
        Assert.Equal(8, approved.ApprovedByUserId);

        var posted = adjustmentService.PostInventoryAdjustment(created.InventoryAdjustmentId, 9);
        Assert.Equal("POSTED", posted.StatusCode);
        Assert.Equal(9, posted.PostedByUserId);
        Assert.NotNull(posted.GeneralLedgerBatchId);

        var movements = inventoryMovementService.GetInventoryMovements()
            .Where(movement => movement.MovementType is "ADJUSTMENT_GAIN" or "ADJUSTMENT_LOSS")
            .ToArray();

        Assert.Contains(movements, movement => movement.MovementType == "ADJUSTMENT_GAIN" && movement.QuantityMoved == 4m && movement.UnitCost == 22m);
        Assert.Contains(movements, movement => movement.MovementType == "ADJUSTMENT_LOSS" && movement.QuantityMoved == 2m && movement.UnitCost == 35m);

        var ledgerBatch = generalLedgerService.GetGeneralLedgerBatches()
            .Single(batch => batch.BatchType == "INV_ADJUSTMENT" && batch.SourceDocumentId == created.InventoryAdjustmentId);

        Assert.Equal(posted.GeneralLedgerBatchId, ledgerBatch.GeneralLedgerBatchId);
        Assert.NotEmpty(ledgerBatch.Journals);
        Assert.All(ledgerBatch.Journals, journal => Assert.Equal("INV_ADJUSTMENT", journal.JournalType));
        Assert.All(ledgerBatch.Journals, journal => Assert.True(journal.Lines.Count >= 2));
    }

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

        var adjustmentService = new InventoryAdjustmentService(new InventoryAdjustmentRepository(dbContext));

        var created = adjustmentService.CreateInventoryAdjustment(new CreateInventoryAdjustmentRequest(
            BranchId: 1,
            WarehouseId: 1,
            AdjustmentDate: new DateOnly(2026, 5, 5),
            ReasonCode: "DAMAGE_WRITE_OFF",
            Notes: "Damaged stock adjustment.",
            Lines:
            [
                new CreateInventoryAdjustmentLineRequest(1001, null, -1m, "EA", 18m, "DAMAGED", "COUNT-ADJ-LOSS-2", null, "Write-off")
            ]));

        var submitted = adjustmentService.SubmitInventoryAdjustment(created.InventoryAdjustmentId, 12);
        Assert.Equal("SUBMITTED", submitted.StatusCode);

        var selfApprovalError = Assert.Throws<InvalidOperationException>(() =>
            adjustmentService.ApproveInventoryAdjustment(created.InventoryAdjustmentId, 12));
        Assert.Contains("self-approval", selfApprovalError.Message, StringComparison.OrdinalIgnoreCase);

        var prematurePostingError = Assert.Throws<InvalidOperationException>(() =>
            adjustmentService.PostInventoryAdjustment(created.InventoryAdjustmentId, 14));
        Assert.Contains("approved", prematurePostingError.Message, StringComparison.OrdinalIgnoreCase);
    }
}
