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

namespace CashLineERP.Api.Tests;

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

        var closeJournalService = new FinanceCloseJournalService(new FinanceCloseJournalRepository(dbContext));
        var generalLedgerService = new GeneralLedgerService(new GeneralLedgerReadRepository(dbContext));
        var accountingPeriodService = new AccountingPeriodService(new AccountingPeriodRepository(dbContext));

        var created = closeJournalService.CreateCloseJournal(new CreateFinanceCloseJournalRequest(
            AccountingPeriodId: 1,
            BranchId: 1,
            JournalClassCode: "ACCRUAL",
            PostingDate: new DateOnly(2026, 4, 28),
            DocumentDate: new DateOnly(2026, 4, 28),
            Notes: "Month-end accrual close journal.",
            Lines:
            [
                new CreateFinanceCloseJournalLineRequest(6101, 1, null, null, null, 150m, 0m, "EGP", 1m, "Accrual expense"),
                new CreateFinanceCloseJournalLineRequest(2101, 1, null, null, null, 0m, 150m, "EGP", 1m, "Accrual liability")
            ]));

        Assert.Equal("DRAFT", created.StatusCode);
        Assert.Equal(150m, created.TotalDebitAmount);
        Assert.Equal(150m, created.TotalCreditAmount);

        var readinessBeforePosting = accountingPeriodService.GetCloseReadiness(1);
        Assert.True(readinessBeforePosting.OpenCloseJournalCount >= 1);
        Assert.Contains(readinessBeforePosting.BlockingReasons, reason => reason.Contains("close journals", StringComparison.OrdinalIgnoreCase));

        var submitted = closeJournalService.SubmitCloseJournal(created.FinanceCloseJournalId, 71);
        Assert.Equal("SUBMITTED", submitted.StatusCode);

        var selfApprovalError = Assert.Throws<InvalidOperationException>(() =>
            closeJournalService.ApproveCloseJournal(created.FinanceCloseJournalId, 71));
        Assert.Contains("self-approval", selfApprovalError.Message, StringComparison.OrdinalIgnoreCase);

        var approved = closeJournalService.ApproveCloseJournal(created.FinanceCloseJournalId, 72);
        Assert.Equal("APPROVED", approved.StatusCode);

        var posted = closeJournalService.PostCloseJournal(created.FinanceCloseJournalId, 73);
        Assert.Equal("POSTED", posted.StatusCode);
        Assert.NotNull(posted.GeneralLedgerBatchId);

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

        Assert.Equal(posted.GeneralLedgerBatchId, ledgerBatch.GeneralLedgerBatchId);
        Assert.NotEmpty(ledgerBatch.Journals);
        Assert.All(ledgerBatch.Journals, journal => Assert.Equal("FIN_ACCRUAL", journal.JournalType));

        var readinessAfterPosting = accountingPeriodService.GetCloseReadiness(1);
        Assert.Equal(0, readinessAfterPosting.OpenCloseJournalCount);
    }

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

        var closeJournalService = new FinanceCloseJournalService(new FinanceCloseJournalRepository(dbContext));
        var inventoryAdjustmentService = new InventoryAdjustmentService(new InventoryAdjustmentRepository(dbContext));
        var receiptService = new AccountsReceivableReceiptService(new AccountsReceivableReceiptRepository(dbContext));
        var accountingPeriodService = new AccountingPeriodService(new AccountingPeriodRepository(dbContext));

        var closeJournal = closeJournalService.CreateCloseJournal(new CreateFinanceCloseJournalRequest(
            AccountingPeriodId: 1,
            BranchId: 1,
            JournalClassCode: "ACCRUAL",
            PostingDate: new DateOnly(2026, 4, 29),
            DocumentDate: new DateOnly(2026, 4, 29),
            Notes: "Month-end accrual journal for review.",
            Lines:
            [
                new CreateFinanceCloseJournalLineRequest(6101, 1, null, null, null, 300m, 0m, "EGP", 1m, "Accrual expense"),
                new CreateFinanceCloseJournalLineRequest(2101, 1, null, null, null, 0m, 300m, "EGP", 1m, "Accrual liability")
            ]));

        closeJournalService.SubmitCloseJournal(closeJournal.FinanceCloseJournalId, 71);
        closeJournalService.ApproveCloseJournal(closeJournal.FinanceCloseJournalId, 72);
        closeJournalService.PostCloseJournal(closeJournal.FinanceCloseJournalId, 73);

        inventoryAdjustmentService.CreateInventoryAdjustment(new CreateInventoryAdjustmentRequest(
            BranchId: 1,
            WarehouseId: 1,
            AdjustmentDate: new DateOnly(2026, 4, 29),
            ReasonCode: "COUNT_VARIANCE",
            Notes: "Draft month-end adjustment pending posting.",
            Lines:
            [
                new CreateInventoryAdjustmentLineRequest(1001, null, -2m, "EA", 18m, "AVAILABLE", "LOT-APR-01", null, "Variance write-down")
            ]));

        receiptService.CreateReceipt(new CreateAccountsReceivableReceiptRequest(
            CustomerId: 2,
            ReceiptDate: new DateOnly(2026, 4, 29),
            ReceiptMethod: "BANK_TRANSFER",
            CurrencyCode: "EGP",
            AmountAmount: 300m,
            ReferenceNumber: "MONTH-END-OPEN-RCPT",
            Notes: "Draft receipt still open at month end.",
            Allocations: []));

        var readiness = accountingPeriodService.GetCloseReadiness(1);
        Assert.False(readiness.IsReadyToClose);
        Assert.True(readiness.OpenInventoryAdjustmentCount >= 1);
        Assert.True(readiness.OpenArReceiptCount >= 1);
        Assert.True(readiness.PostedAccrualCloseJournalCount >= 1);
        Assert.Contains(readiness.BlockingReasons, reason => reason.Contains("inventory adjustments", StringComparison.OrdinalIgnoreCase));
        Assert.Contains(readiness.BlockingReasons, reason => reason.Contains("receipts", StringComparison.OrdinalIgnoreCase));

        var review = accountingPeriodService.GetMonthEndReview(1);
        Assert.True(review.CloseReadiness.OpenInventoryAdjustmentCount >= 1);
        Assert.Contains(review.CloseJournalClassSummaries, summary =>
            summary.JournalClassCode == "ACCRUAL" &&
            summary.PostedCount >= 1 &&
            summary.PostedAmount >= 300m);
        Assert.Contains(review.OpenItems, item =>
            item.DocumentType == "INV_ADJUSTMENT" &&
            item.DocumentNumber.StartsWith("INVADJ-", StringComparison.OrdinalIgnoreCase));
        Assert.Contains(review.OpenItems, item =>
            item.DocumentType == "AR_RECEIPT" &&
            item.DocumentNumber.StartsWith("ARRCPT-", StringComparison.OrdinalIgnoreCase));
    }
}
