using CashLineERP.Application.Finance;
using CashLineERP.Application.Integration;
using CashLineERP.Application.IslamicFinance.PartnerLenders;
using CashLineERP.Application.IslamicFinance.ProductProfiles;
using CashLineERP.Application.IslamicFinance.Requests;
using CashLineERP.Application.Internal;
using CashLineERP.Application.Inventory;
using CashLineERP.Application.Sales;
using CashLineERP.Domain.Integration;
using CashLineERP.Domain.Notifications;
using CashLineERP.Contracts.Finance;
using CashLineERP.Contracts.IslamicFinance;
using CashLineERP.Contracts.Inventory;
using CashLineERP.Contracts.Sales;
using CashLineERP.Infrastructure.Persistence.Repositories.IslamicFinance;
using CashLineERP.Infrastructure.Persistence.Repositories;

namespace CashLineERP.Api.Tests;

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

        var salesOrderService = new SalesOrderService(new SalesOrderRepository(dbContext));
        var dispatchService = new DispatchOrderService(new DispatchOrderRepository(dbContext));
        var invoiceService = new AccountsReceivableInvoiceService(new AccountsReceivableInvoiceRepository(dbContext));
        var receiptService = new AccountsReceivableReceiptService(new AccountsReceivableReceiptRepository(dbContext));
        var reconciliationService = new ReconciliationService(new ReconciliationRepository(dbContext));
        var inventoryAdjustmentService = new InventoryAdjustmentService(new InventoryAdjustmentRepository(dbContext));
        var internalWorkbenchService = new InternalWorkbenchService(new InternalWorkbenchRepository(dbContext));

        var heldSalesOrder = salesOrderService.CreateSalesOrder(new CreateSalesOrderRequest(
            BranchId: 1,
            CustomerId: 2,
            OrderDate: new DateOnly(2026, 4, 29),
            CurrencyCode: "EGP",
            ExchangeRate: 1m,
            Lines:
            [
                new CreateSalesOrderLineRequest(1001, 1, 150m, "EA", 32m, "VAT14")
            ]));
        var submittedHeldOrder = salesOrderService.SubmitSalesOrder(heldSalesOrder.SalesOrderId, 2);
        Assert.Equal("ON_HOLD", submittedHeldOrder.CreditStatusCode);

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

        var postedReceipt = AccountsReceivableWorkflowTestHelper.SubmitApproveAndPostReceipt(
            receiptService,
            receiptService.CreateReceipt(new CreateAccountsReceivableReceiptRequest(
                CustomerId: 2,
                ReceiptDate: new DateOnly(2026, 4, 29),
                ReceiptMethod: "BANK_TRANSFER",
                CurrencyCode: "EGP",
                AmountAmount: postedInvoice.TotalAmount,
                ReferenceNumber: "WB-RCPT-0001",
                Notes: "Workbench receipt",
                Allocations:
                [
                    new CreateAccountsReceivableReceiptAllocationRequest(postedInvoice.AccountsReceivableInvoiceId, postedInvoice.TotalAmount, new DateOnly(2026, 4, 29))
                ])).AccountsReceivableReceiptId);

        var bankStatement = reconciliationService.CreateBankStatement(new CreateBankStatementRequest(
            BankAccountCode: "CIB-EGP-WB-001",
            StatementDate: new DateOnly(2026, 4, 29),
            CurrencyCode: "EGP",
            OpeningBalanceAmount: 10000m,
            ClosingBalanceAmount: 9823m,
            Lines:
            [
                new CreateBankStatementLineRequest(
                    TransactionDate: new DateOnly(2026, 4, 29),
                    ExternalReference: "WB-RCPT-0001",
                    DescriptionText: "Workbench mismatch receipt",
                    DebitAmount: 0m,
                    CreditAmount: postedReceipt.AmountAmount - 20m,
                    RunningBalanceAmount: 9823m)
            ]));

        var reconciliationRun = reconciliationService.RunReceiptReconciliation(new RunReceiptReconciliationRequest(1, bankStatement.BankStatementId, 4));
        Assert.Equal(1, reconciliationRun.ExceptionItemCount);

        var adjustment = inventoryAdjustmentService.CreateInventoryAdjustment(new CreateInventoryAdjustmentRequest(
            BranchId: 1,
            WarehouseId: 1,
            AdjustmentDate: new DateOnly(2026, 4, 29),
            ReasonCode: "COUNT_VARIANCE",
            Notes: "Workbench approval queue",
            Lines:
            [
                new CreateInventoryAdjustmentLineRequest(1001, null, 1m, "EA", 18m, "AVAILABLE", "WB-ADJ-1", null, null)
            ]));
        inventoryAdjustmentService.SubmitInventoryAdjustment(adjustment.InventoryAdjustmentId, 7);

        var dashboard = internalWorkbenchService.GetDashboardSummary();
        Assert.True(dashboard.PendingApprovalCount >= 3);
        Assert.True(dashboard.InventoryApprovalCount >= 1);
        Assert.True(dashboard.SalesApprovalCount >= 1);
        Assert.Equal(1, dashboard.ReconciliationExceptionCount);
        Assert.Equal(1, dashboard.EtaAttentionCount);
        Assert.Equal(1, dashboard.CreditHoldCount);
        Assert.True(dashboard.TotalExceptionCount >= 3);

        var approvalQueue = internalWorkbenchService.GetApprovalQueue();
        Assert.Contains(approvalQueue, item => item.DocumentType == "INV_ADJUSTMENT" && item.DocumentId == adjustment.InventoryAdjustmentId);
        Assert.Contains(approvalQueue, item => item.DocumentType == "SALES_ORDER" && item.DocumentId == submittedHeldOrder.SalesOrderId);

        var financeApprovals = internalWorkbenchService.GetApprovalQueue("FINANCE");
        Assert.Empty(financeApprovals);

        var exceptions = internalWorkbenchService.GetExceptionQueue();
        Assert.Contains(exceptions, item => item.SourceType == "RECONCILIATION" && item.ExceptionCategory == "EXCEPTION");
        Assert.Contains(exceptions, item => item.SourceType == "ETA" && item.DocumentId == postedInvoice.AccountsReceivableInvoiceId);
        Assert.Contains(exceptions, item => item.SourceType == "CREDIT_CONTROL" && item.DocumentId == submittedHeldOrder.SalesOrderId);

        var etaOnly = internalWorkbenchService.GetExceptionQueue("ETA");
        Assert.Single(etaOnly);
        Assert.All(etaOnly, item => Assert.Equal("ETA", item.SourceType));
    }

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

        var salesOrderService = new SalesOrderService(new SalesOrderRepository(dbContext));
        var invoiceService = new AccountsReceivableInvoiceService(new AccountsReceivableInvoiceRepository(dbContext));
        var reconciliationService = new ReconciliationService(new ReconciliationRepository(dbContext));
        var internalWorkbenchService = new InternalWorkbenchService(new InternalWorkbenchRepository(dbContext));

        var heldSalesOrder = salesOrderService.CreateSalesOrder(new CreateSalesOrderRequest(
            BranchId: 1,
            CustomerId: 2,
            OrderDate: new DateOnly(2026, 4, 2),
            CurrencyCode: "EGP",
            ExchangeRate: 1m,
            Lines:
            [
                new CreateSalesOrderLineRequest(1001, 1, 400m, "EA", 32m, "VAT14")
            ]));
        salesOrderService.SubmitSalesOrder(heldSalesOrder.SalesOrderId, 2);

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

        var bankStatement = reconciliationService.CreateBankStatement(new CreateBankStatementRequest(
            BankAccountCode: "CIB-EGP-MON-001",
            StatementDate: new DateOnly(2026, 4, 3),
            CurrencyCode: "EGP",
            OpeningBalanceAmount: 5000m,
            ClosingBalanceAmount: 4800m,
            Lines:
            [
                new CreateBankStatementLineRequest(
                    TransactionDate: new DateOnly(2026, 4, 3),
                    ExternalReference: "MON-NO-MATCH-001",
                    DescriptionText: "Monitoring mismatch line",
                    DebitAmount: 0m,
                    CreditAmount: 200m,
                    RunningBalanceAmount: 4800m)
            ]));
        reconciliationService.RunReceiptReconciliation(new RunReceiptReconciliationRequest(1, bankStatement.BankStatementId, 4));

        var endpoint = new IntegrationEndpointRegistryEntry(
            "MON-IN-001",
            "Monitoring inbound endpoint",
            "PAYMOB",
            "TEST",
            "INBOUND",
            "HTTPS",
            "POST",
            "https://gateway.cashline.local",
            "/callbacks",
            "SIGNATURE",
            "mon-secret",
            30,
            "LINEAR",
            3,
            true,
            "Monitoring endpoint");
        endpoint.Submit(7);
        endpoint.Approve(8);
        endpoint.Activate(8);
        dbContext.IntegrationEndpointRegistryEntries.Add(endpoint);

        var notificationTemplate = new NotificationTemplate(
            "MON-NTF-001",
            "Monitoring template",
            "EMAIL",
            "Ops subject",
            "Ops body",
            "Monitoring template");
        dbContext.NotificationTemplates.Add(notificationTemplate);
        dbContext.SaveChanges();

        var outboxEvent = new OutboxEvent(
            "OUTBOX-MON-001",
            "INTEGRATION",
            "TEST",
            1,
            "TEST_EVENT",
            "MON-CORR-001",
            "{\"status\":\"pending\"}",
            DateTime.UtcNow);
        outboxEvent.MoveToDeadLetter("Monitoring dead-letter");

        var inboxMessage = new InboxMessage(
            "INBOX-MON-001",
            endpoint.Id,
            "PAYMENT_CALLBACK",
            "EXT-MON-001",
            "MON-CORR-002",
            "{\"status\":\"retry\"}",
            DateTime.UtcNow);
        inboxMessage.ScheduleRetry("Temporary handler issue", DateTime.UtcNow.AddMinutes(5));

        var notificationQueueItem = new NotificationQueueItem(
            notificationTemplate.Id,
            notificationTemplate.TemplateCode,
            "EMAIL",
            "ops@cashline.local",
            "MONITORING",
            "AR_INVOICE",
            overdueInvoice.AccountsReceivableInvoiceId,
            "Ops subject",
            "Ops body");
        notificationQueueItem.MoveToDeadLetter("Monitoring notification dead-letter");

        dbContext.OutboxEvents.Add(outboxEvent);
        dbContext.InboxMessages.Add(inboxMessage);
        dbContext.NotificationQueueItems.Add(notificationQueueItem);
        dbContext.SaveChanges();

        var dashboard = internalWorkbenchService.GetOperationalDashboard("Testing", "1.0.0.0");
        Assert.Equal("OK", dashboard.ServiceStatus);
        Assert.Equal("Testing", dashboard.EnvironmentName);
        Assert.Equal("1.0.0.0", dashboard.BuildVersion);
        Assert.True(dashboard.DeadLetterOutboxCount >= 1);
        Assert.True(dashboard.RetryInboxCount >= 1);
        Assert.True(dashboard.DeadLetterNotificationCount >= 1);
        Assert.True(dashboard.OpenReconciliationExceptionCount >= 1);
        Assert.True(dashboard.CreditHoldCount >= 1);
        Assert.True(dashboard.EtaAttentionCount >= 1);
        Assert.True(dashboard.OverdueCollectionsCount >= 1);
        Assert.True(dashboard.CriticalAlertCount >= 1);
        Assert.True(dashboard.HighAlertCount >= 1);
        Assert.True(dashboard.WarningAlertCount >= 1);

        var criticalAlerts = internalWorkbenchService.GetOperationalAlerts("CRITICAL");
        Assert.Contains(criticalAlerts, item => item.AlertCode == "OUTBOX_DEAD_LETTER" && item.SourceArea == "INTEGRATION");

        var salesAlerts = internalWorkbenchService.GetOperationalAlerts("WARNING", "SALES");
        Assert.Contains(salesAlerts, item => item.AlertCode == "CREDIT_HOLD_OPEN");

        var collectionsAlerts = internalWorkbenchService.GetOperationalAlerts(sourceArea: "COLLECTIONS");
        Assert.Contains(collectionsAlerts, item => item.AlertCode == "OVERDUE_COLLECTIONS_ACTION");
    }

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

        var salesOrderService = new SalesOrderService(new SalesOrderRepository(dbContext));
        var internalWorkbenchService = new InternalWorkbenchService(
            new InternalWorkbenchRepository(dbContext),
            new OutboxEventService(new OutboxEventRepository(dbContext)),
            new InboxMessageService(new InboxMessageRepository(dbContext)));

        var heldSalesOrder = salesOrderService.CreateSalesOrder(new CreateSalesOrderRequest(
            BranchId: 1,
            CustomerId: 2,
            OrderDate: new DateOnly(2026, 4, 6),
            CurrencyCode: "EGP",
            ExchangeRate: 1m,
            Lines:
            [
                new CreateSalesOrderLineRequest(1001, 1, 375m, "EA", 32m, "VAT14")
            ]));
        salesOrderService.SubmitSalesOrder(heldSalesOrder.SalesOrderId, 2);

        var outboxEvent = new OutboxEvent(
            "MON-WF-OUT-001",
            "FINANCE",
            "AR_INVOICE",
            1,
            "INVOICE_POSTED",
            "MON-WF-001",
            "{\"status\":\"pending\"}",
            DateTime.UtcNow);
        outboxEvent.MoveToDeadLetter("Workflow monitoring dead-letter");

        var integrationEndpoint = new IntegrationEndpointRegistryEntry(
            "MON-WF-IN",
            "Workflow Monitoring Inbox Endpoint",
            "PAYMENT_GATEWAY",
            "TEST",
            "INBOUND",
            "HTTPS",
            "POST",
            "https://monitoring.cashline.local",
            "/callbacks/monitoring",
            "SIGNATURE",
            "MON-WF-SECRET",
            30,
            "STANDARD",
            3,
            true,
            "Workflow monitoring inbox seed");
        dbContext.IntegrationEndpointRegistryEntries.Add(integrationEndpoint);
        dbContext.SaveChanges();

        var inboxMessage = new InboxMessage(
            "MON-WF-IN-001",
            integrationEndpoint.Id,
            "PAYMENT_CALLBACK",
            "EXT-MON-WF-001",
            "MON-WF-001",
            "{\"status\":\"received\"}",
            DateTime.UtcNow);
        inboxMessage.ScheduleRetry("Workflow monitoring retry", DateTime.UtcNow.AddMinutes(2));

        dbContext.OutboxEvents.Add(outboxEvent);
        dbContext.InboxMessages.Add(inboxMessage);
        dbContext.SaveChanges();

        var monitoring = internalWorkbenchService.GetWorkflowMonitoring("Testing", "1.0.0.0", 5);
        Assert.Equal("OK", monitoring.ServiceStatus);
        Assert.Equal("Testing", monitoring.EnvironmentName);
        Assert.Equal("1.0.0.0", monitoring.BuildVersion);
        Assert.True(monitoring.RefreshIntervalSeconds >= 10);
        Assert.True(monitoring.Dashboard.PendingApprovalCount >= 1);
        Assert.Contains(monitoring.OutboxBacklog, item => item.StatusCode == "DEAD_LETTER");
        Assert.Contains(monitoring.InboxBacklog, item => item.StatusCode == "RETRY");
        Assert.NotEmpty(monitoring.Alerts);
    }

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

        var salesOrderService = new SalesOrderService(new SalesOrderRepository(dbContext));
        var internalWorkbenchService = new InternalWorkbenchService(new InternalWorkbenchRepository(dbContext));

        var heldSalesOrder = salesOrderService.CreateSalesOrder(new CreateSalesOrderRequest(
            BranchId: 1,
            CustomerId: 2,
            OrderDate: new DateOnly(2026, 4, 5),
            CurrencyCode: "EGP",
            ExchangeRate: 1m,
            Lines:
            [
                new CreateSalesOrderLineRequest(1001, 1, 500m, "EA", 32m, "VAT14")
            ]));
        salesOrderService.SubmitSalesOrder(heldSalesOrder.SalesOrderId, 2);

        var console = internalWorkbenchService.GetHypercareConsole("Testing", "1.0.0.0");
        Assert.Equal("Testing", console.EnvironmentName);
        Assert.Equal("1.0.0.0", console.BuildVersion);
        Assert.Equal("1.0.0-v1-controlled-baseline.0", console.ReleaseVersion);
        Assert.Equal("READY_FOR_CONTROLLED_CUTOVER", console.ReadinessStatus);
        Assert.True(console.RunbookCount >= 6);

        var salesIncidents = internalWorkbenchService.GetHypercareIncidents(sourceArea: "SALES");
        Assert.Contains(salesIncidents, item => item.IncidentCode == "CREDIT_HOLD_OPEN" && item.RecommendedRunbookCode == "HYPERCARE_TRIAGE");

        var operationsRunbooks = internalWorkbenchService.GetHypercareRunbooks("OPERATIONS");
        Assert.Contains(operationsRunbooks, item => item.RunbookCode == "HYPERCARE_TRIAGE");

        var cutoverRunbook = internalWorkbenchService.GetHypercareRunbook("CUTOVER");
        Assert.NotNull(cutoverRunbook);
        Assert.Equal("AVAILABLE", cutoverRunbook!.StatusCode);
        Assert.Contains("Cutover Runbook", cutoverRunbook.MarkdownContent);
    }

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

        var partnerService = new IslamicFinancePartnerService(new IslamicFinancePartnerRepository(dbContext));
        var productService = new IslamicFinanceProductService(new IslamicFinanceProductRepository(dbContext));
        var requestService = new IslamicFinanceRequestService(new IslamicFinanceRequestRepository(dbContext));
        var internalWorkbenchService = new InternalWorkbenchService(new InternalWorkbenchRepository(dbContext));

        var partner = partnerService.CreatePartnerLender(new CreateIslamicFinancePartnerLenderRequest
        {
            PartnerLenderPartyId = 1,
            DefaultCurrencyCode = "EGP",
            SupportsMurabaha = true,
            SupportsReceivablesFinance = false,
            SupportsTawarruq = false
        });
        partnerService.SubmitPartnerLender(partner.Id, 3);

        var product = productService.CreateProductProfile(new CreateIslamicFinanceProductProfileRequest
        {
            ProductType = "MURABAHA",
            ProductCode = "MUR-WBQ-001",
            ProductName = "Murabaha Workbench Product",
            RequiresShariahReview = true,
            AllowsPartnerFunding = true,
            AllowsCashLineFunding = false
        });

        var request = requestService.CreateRequest(new CreateIslamicFinanceFinancingRequestRequest
        {
            RequestNumber = "IF-REQ-WBQ-001",
            CustomerPartyId = 2,
            ProductProfileId = product.Id,
            FundingSourceMode = "PARTNER",
            SourceDocumentType = "SALES_ORDER",
            SourceDocumentId = 1,
            TransactionCurrencyCode = "EGP",
            RequestedAmount = 125000m
        });
        requestService.SubmitRequest(request.Id, 3);
        requestService.ReturnRequest(request.Id, 4);

        var dashboard = internalWorkbenchService.GetDashboardSummary();
        Assert.True(dashboard.FinanceApprovalCount >= 1);
        Assert.True(dashboard.TotalExceptionCount >= 1);

        var approvals = internalWorkbenchService.GetApprovalQueue("FINANCE");
        Assert.Contains(approvals, item => item.DocumentType == "IF_PARTNER_LENDER" && item.DocumentId == partner.Id);

        var exceptions = internalWorkbenchService.GetExceptionQueue("ISLAMIC_FINANCE");
        Assert.Contains(exceptions, item =>
            item.DocumentType == "IF_FINANCING_REQUEST"
            && item.DocumentId == request.Id
            && item.CurrentStatus == "RETURNED");
    }
}
