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

namespace CashLineERP.Api.Tests;

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

        var integrationRegistryService = new IntegrationEndpointRegistryService(new IntegrationEndpointRegistryRepository(dbContext));
        var foreignExchangeQuoteService = new ForeignExchangeQuoteService(new ForeignExchangeQuoteRepository(dbContext));
        var invoiceService = new AccountsReceivableInvoiceService(new AccountsReceivableInvoiceRepository(dbContext));
        var reportingService = new CoreReportingService(new CoreReportingRepository(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(
            IntegrationEndpointId: activeEndpoint.IntegrationEndpointId,
            QuoteDate: new DateOnly(2026, 4, 20),
            BaseCurrencyCode: "EGP",
            SourceBatchReference: "FX-BATCH-001",
            Notes: "Opening quote batch",
            Quotes:
            [
                new IngestForeignExchangeQuoteLineRequest("USD", 48m, "USD-20260420", null)
            ]));

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

        var quoteHistory = foreignExchangeQuoteService.GetForeignExchangeQuotes("USD", "EGP", activeEndpoint.IntegrationEndpointId);
        Assert.Equal(2, quoteHistory.Count);

        var quoteAsOf22 = foreignExchangeQuoteService.GetLatestForeignExchangeQuote("USD", "EGP", new DateOnly(2026, 4, 22), activeEndpoint.IntegrationEndpointId);
        Assert.NotNull(quoteAsOf22);
        Assert.Equal(48m, quoteAsOf22!.RateToBase);

        var quoteAsOf26 = foreignExchangeQuoteService.GetLatestForeignExchangeQuote("USD", "EGP", new DateOnly(2026, 4, 26), activeEndpoint.IntegrationEndpointId);
        Assert.NotNull(quoteAsOf26);
        Assert.Equal(50m, quoteAsOf26!.RateToBase);

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

        var financeReport = reportingService.GetCoreFinanceReport(new DateOnly(2026, 4, 21), new DateOnly(2026, 4, 21));
        var arRow = financeReport.Rows.Single(row => row.DocumentNumber == postedInvoice.InvoiceNumber && row.ReportSection == "AR");

        Assert.Equal(48m, arRow.FxRateToBase);
        Assert.Equal(Math.Round(postedInvoice.TotalAmount * 48m, 2, MidpointRounding.AwayFromZero), arRow.BaseAmount);
    }

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

        var integrationRegistryService = new IntegrationEndpointRegistryService(new IntegrationEndpointRegistryRepository(dbContext));
        var foreignExchangeQuoteService = new ForeignExchangeQuoteService(new ForeignExchangeQuoteRepository(dbContext));

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

        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, 23),
            "EGP",
            "FX-BATCH-DUP",
            null,
            [
                new IngestForeignExchangeQuoteLineRequest("USD", 49m, "USD-1", null)
            ]));

        var duplicateException = Assert.Throws<InvalidOperationException>(() =>
            foreignExchangeQuoteService.IngestQuotes(3, new IngestForeignExchangeQuotesRequest(
                activeEndpoint.IntegrationEndpointId,
                new DateOnly(2026, 4, 23),
                "EGP",
                "FX-BATCH-DUP-2",
                null,
                [
                    new IngestForeignExchangeQuoteLineRequest("USD", 49.25m, "USD-2", null)
                ])));

        Assert.Contains("already contains", duplicateException.Message, StringComparison.OrdinalIgnoreCase);
    }
}
