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

namespace CashLineERP.Api.Tests;

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

        var refundService = new CustomerRefundService(new CustomerRefundRepository(dbContext), new PaymentGatewayExecutionService(new PaymentGatewayExecutionRepository(dbContext)));
        var receiptService = new AccountsReceivableReceiptService(new AccountsReceivableReceiptRepository(dbContext), refundService);
        var customerStatementService = new CustomerStatementService(new CustomerStatementRepository(dbContext));

        var postedReceipt = CreatePostedReceipt(receiptService, 300m, "RFND-QA-0001", new DateOnly(2026, 5, 10));

        var createdRefund = refundService.CreateRefund(new CreateCustomerRefundRequest(
            CustomerId: 2,
            SourceAccountsReceivableReceiptId: postedReceipt.AccountsReceivableReceiptId,
            RefundDate: new DateOnly(2026, 5, 11),
            RefundMethod: "BANK_TRANSFER",
            CurrencyCode: "EGP",
            AmountAmount: 120m,
            PaymentChannelConfigurationId: null,
            ReferenceNumber: "RFND-QA-0001",
            Notes: "Customer overpayment refund"));

        var submittedRefund = refundService.SubmitRefund(createdRefund.CustomerRefundId, 3);
        var approvedRefund = refundService.ApproveRefund(createdRefund.CustomerRefundId, 4);
        var postedRefund = refundService.PostRefund(createdRefund.CustomerRefundId, 4);

        Assert.Equal("SUBMITTED", submittedRefund.StatusCode);
        Assert.Equal("APPROVED", approvedRefund.StatusCode);
        Assert.Equal("POSTED", postedRefund.StatusCode);
        Assert.NotNull(postedRefund.GeneralLedgerBatchId);

        var refreshedReceipt = receiptService.GetReceipt(postedReceipt.AccountsReceivableReceiptId);
        Assert.NotNull(refreshedReceipt);
        Assert.Equal(120m, refreshedReceipt!.RefundedAmount);
        Assert.Equal(180m, refreshedReceipt.AvailableRefundAmount);

        var statement = customerStatementService.GenerateStatement(3, new CreateCustomerStatementRequest(
            CustomerId: 2,
            DateFrom: new DateOnly(2026, 5, 10),
            DateTo: new DateOnly(2026, 5, 11),
            CurrencyCode: "EGP"));

        Assert.Contains(statement.Lines, line => line.DocumentType == "CUSTOMER_REFUND" && line.ChargeAmount == 120m);
        Assert.Equal(-180m, statement.ClosingBalanceAmount);
    }

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

        var refundService = new CustomerRefundService(new CustomerRefundRepository(dbContext), new PaymentGatewayExecutionService(new PaymentGatewayExecutionRepository(dbContext)));
        var receiptService = new AccountsReceivableReceiptService(new AccountsReceivableReceiptRepository(dbContext), refundService);
        var reconciliationService = new ReconciliationService(new ReconciliationRepository(dbContext));

        var postedReceipt = CreatePostedReceipt(receiptService, 250m, "RFND-RECON-0001", new DateOnly(2026, 5, 12));
        var refund = refundService.CreateRefund(new CreateCustomerRefundRequest(
            CustomerId: 2,
            SourceAccountsReceivableReceiptId: postedReceipt.AccountsReceivableReceiptId,
            RefundDate: new DateOnly(2026, 5, 13),
            RefundMethod: "BANK_TRANSFER",
            CurrencyCode: "EGP",
            AmountAmount: 100m,
            PaymentChannelConfigurationId: null,
            ReferenceNumber: "RFND-RECON-0001",
            Notes: "Refund for reconciliation"));

        refundService.SubmitRefund(refund.CustomerRefundId, 3);
        refundService.ApproveRefund(refund.CustomerRefundId, 4);
        var postedRefund = refundService.PostRefund(refund.CustomerRefundId, 4);

        var statement = reconciliationService.CreateBankStatement(new CreateBankStatementRequest(
            BankAccountCode: "CIB-EGP-RFND-001",
            StatementDate: new DateOnly(2026, 5, 13),
            CurrencyCode: "EGP",
            OpeningBalanceAmount: 1000m,
            ClosingBalanceAmount: 900m,
            Lines:
            [
                new CreateBankStatementLineRequest(
                    TransactionDate: new DateOnly(2026, 5, 13),
                    ExternalReference: "RFND-RECON-0001",
                    DescriptionText: "Customer refund transfer",
                    DebitAmount: 100m,
                    CreditAmount: 0m,
                    RunningBalanceAmount: 900m)
            ]));

        var run = reconciliationService.RunSettlementReconciliation(new RunReceiptReconciliationRequest(2, statement.BankStatementId, 4));

        Assert.Equal(1, run.MatchedItemCount);
        Assert.Contains(run.Items, item => item.MatchedDocumentType == "CUSTOMER_REFUND" && item.CustomerRefundId == postedRefund.CustomerRefundId);

        var refreshedStatement = reconciliationService.GetBankStatement(statement.BankStatementId);
        Assert.NotNull(refreshedStatement);
        Assert.Equal(postedRefund.CustomerRefundId, refreshedStatement!.Lines.Single().CustomerRefundId);
    }

    private static AccountsReceivableReceiptResponse CreatePostedReceipt(
        AccountsReceivableReceiptService receiptService,
        decimal amount,
        string referenceNumber,
        DateOnly receiptDate)
    {
        var created = receiptService.CreateReceipt(new CreateAccountsReceivableReceiptRequest(
            CustomerId: 2,
            ReceiptDate: receiptDate,
            ReceiptMethod: "BANK_TRANSFER",
            CurrencyCode: "EGP",
            AmountAmount: amount,
            ReferenceNumber: referenceNumber,
            Notes: "Refund QA receipt",
            Allocations: []));

        receiptService.SubmitReceipt(created.AccountsReceivableReceiptId, 3);
        receiptService.ApproveReceipt(created.AccountsReceivableReceiptId, 4);
        return receiptService.PostReceipt(created.AccountsReceivableReceiptId, 4);
    }
}
