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

namespace CashLineERP.Api.Tests;

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

        var integrationRegistryService = new IntegrationEndpointRegistryService(new IntegrationEndpointRegistryRepository(dbContext));
        var foreignExchangeQuoteService = new ForeignExchangeQuoteService(new ForeignExchangeQuoteRepository(dbContext));
        var receivableInvoiceService = new AccountsReceivableInvoiceService(new AccountsReceivableInvoiceRepository(dbContext));
        var payableBillService = new AccountsPayableBillService(new AccountsPayableBillRepository(dbContext));
        var cashAccountService = new CashAccountService(new CashAccountRepository(dbContext));
        var foreignExchangeRevaluationService = new ForeignExchangeRevaluationService(new ForeignExchangeRevaluationRepository(dbContext));
        var accountingPeriodService = new AccountingPeriodService(new AccountingPeriodRepository(dbContext));
        var generalLedgerService = new GeneralLedgerService(new GeneralLedgerReadRepository(dbContext));

        var endpoint = integrationRegistryService.CreateIntegrationEndpoint(new CreateIntegrationEndpointRequest(
            EndpointName: "Treasury FX feed",
            IntegrationProviderCode: "FX_SOURCE",
            EnvironmentCode: "UAT",
            DirectionCode: "INBOUND",
            TransportCode: "REST",
            HttpMethodCode: "POST",
            BaseUrl: "https://fx.uat.example.com",
            RelativePath: "/quotes",
            AuthenticationModeCode: "API_KEY",
            CredentialReference: "secret://fx/uat/key",
            TimeoutSeconds: 20,
            RetryPolicyCode: "STANDARD",
            MaxRetryCount: 2,
            IsIdempotencyKeyRequired: true,
            Notes: "Treasury FX source"));

        integrationRegistryService.SubmitIntegrationEndpoint(endpoint.IntegrationEndpointId, 2);
        integrationRegistryService.ApproveIntegrationEndpoint(endpoint.IntegrationEndpointId, 4);
        var activeEndpoint = integrationRegistryService.ActivateIntegrationEndpoint(endpoint.IntegrationEndpointId, 4);

        foreignExchangeQuoteService.IngestQuotes(3, new IngestForeignExchangeQuotesRequest(
            activeEndpoint.IntegrationEndpointId,
            new DateOnly(2026, 4, 20),
            "EGP",
            "FX-BATCH-001",
            "Initial quote batch",
            [
                new IngestForeignExchangeQuoteLineRequest("USD", 48m, "USD-20260420", null)
            ]));

        foreignExchangeQuoteService.IngestQuotes(3, new IngestForeignExchangeQuotesRequest(
            activeEndpoint.IntegrationEndpointId,
            new DateOnly(2026, 4, 25),
            "EGP",
            "FX-BATCH-002",
            "Revaluation quote batch",
            [
                new IngestForeignExchangeQuoteLineRequest("USD", 50m, "USD-20260425", null)
            ]));

        AccountsReceivableWorkflowTestHelper.SubmitApproveAndPostInvoice(
            receivableInvoiceService,
            receivableInvoiceService.CreateInvoice(new CreateAccountsReceivableInvoiceRequest(
                CustomerId: 2,
                SalesOrderId: 1,
                DispatchOrderId: 1,
                InvoiceType: "TAX",
                InvoiceDate: new DateOnly(2026, 4, 20),
                TaxDate: new DateOnly(2026, 4, 20),
                DueDate: new DateOnly(2026, 4, 30),
                CurrencyCode: "USD",
                Lines:
                [
                    new CreateAccountsReceivableInvoiceLineRequest(1, 1001, 1m, "EA", 10m, "VAT14")
                ])).AccountsReceivableInvoiceId);

        var bill = payableBillService.CreateBill(new CreateAccountsPayableBillRequest(
            SupplierId: 1,
            PurchaseOrderId: 1,
            GoodsReceiptId: 1,
            BillDate: new DateOnly(2026, 4, 20),
            DueDate: new DateOnly(2026, 4, 30),
            CurrencyCode: "USD",
            Lines:
            [
                new CreateAccountsPayableBillLineRequest(1, 1001, 50m, "EA", 24m, "VAT14"),
                new CreateAccountsPayableBillLineRequest(2, 1002, 30m, "BOX", 50m, "VAT14")
            ]));
        payableBillService.SubmitBill(bill.AccountsPayableBillId, 3);
        payableBillService.ApproveMatch(bill.AccountsPayableBillId, 4);
        payableBillService.PostBill(bill.AccountsPayableBillId, 4);

        cashAccountService.CreateCashAccount(new CreateCashAccountRequest(
            AccountCode: "USD-OPER",
            AccountName: "USD Operating Cash",
            AccountType: "BANK",
            CurrencyCode: "USD",
            BranchId: 1,
            OpeningBalanceAmount: 100m));

        var createdRevaluation = foreignExchangeRevaluationService.CreateForeignExchangeRevaluation(new CreateForeignExchangeRevaluationRequest(
            AccountingPeriodId: 1,
            RevaluationDate: new DateOnly(2026, 4, 25),
            IncludeAccountsReceivable: true,
            IncludeAccountsPayable: true,
            IncludeCashAccounts: true,
            Notes: "April month-end FX revaluation"));

        Assert.Equal("DRAFT", createdRevaluation.StatusCode);
        Assert.Equal(3, createdRevaluation.Lines.Count);
        Assert.Contains(createdRevaluation.Lines, line => line.SourceTypeCode == "AR" && line.VarianceBaseAmount == 22.8m);
        Assert.Contains(createdRevaluation.Lines, line => line.SourceTypeCode == "AP" && line.VarianceBaseAmount == 6156m);
        Assert.Contains(createdRevaluation.Lines, line => line.SourceTypeCode == "CASH" && line.VarianceBaseAmount == 200m);

        var readinessBeforePosting = accountingPeriodService.GetCloseReadiness(1);
        Assert.Equal(1, readinessBeforePosting.OpenFxRevaluationCount);
        Assert.False(readinessBeforePosting.IsReadyToClose);

        var submittedRevaluation = foreignExchangeRevaluationService.SubmitForeignExchangeRevaluation(createdRevaluation.ForeignExchangeRevaluationId, 3);
        Assert.Equal("SUBMITTED", submittedRevaluation.StatusCode);

        var approvedRevaluation = foreignExchangeRevaluationService.ApproveForeignExchangeRevaluation(createdRevaluation.ForeignExchangeRevaluationId, 4);
        Assert.Equal("APPROVED", approvedRevaluation.StatusCode);

        var postedRevaluation = foreignExchangeRevaluationService.PostForeignExchangeRevaluation(createdRevaluation.ForeignExchangeRevaluationId, 4);
        Assert.Equal("POSTED", postedRevaluation.StatusCode);
        Assert.NotNull(postedRevaluation.GeneralLedgerBatchId);

        var postedBatch = generalLedgerService.GetGeneralLedgerBatches()
            .SingleOrDefault(batch => batch.GeneralLedgerBatchId == postedRevaluation.GeneralLedgerBatchId!.Value);
        Assert.NotNull(postedBatch);
        Assert.Equal("FX_REVALUATION", postedBatch!.BatchType);

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