using CashLineERP.Application.Finance;
using CashLineERP.Application.Inventory;
using CashLineERP.Application.Procurement;
using CashLineERP.Application.Reporting;
using CashLineERP.Application.Sales;
using CashLineERP.Contracts.Finance;
using CashLineERP.Contracts.Inventory;
using CashLineERP.Contracts.Procurement;
using CashLineERP.Contracts.Sales;
using CashLineERP.Infrastructure.Persistence.Repositories;

namespace CashLineERP.Api.Tests;

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

        var purchaseRequisitionService = new PurchaseRequisitionService(new PurchaseRequisitionRepository(dbContext));
        var rfqService = new RfqService(new RfqRepository(dbContext));
        var quotationService = new SupplierQuotationService(new SupplierQuotationRepository(dbContext));
        var evaluationService = new RfqEvaluationService(new RfqEvaluationRepository(dbContext));
        var goodsReceiptService = new GoodsReceiptService(new GoodsReceiptRepository(dbContext));
        var apBillService = new AccountsPayableBillService(new AccountsPayableBillRepository(dbContext));
        var apPaymentService = new AccountsPayablePaymentService(new AccountsPayablePaymentRepository(dbContext));
        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 inventoryAdjustmentService = new InventoryAdjustmentService(new InventoryAdjustmentRepository(dbContext));
        var warehouseTransferService = new WarehouseTransferService(new WarehouseTransferRepository(dbContext));
        var reportingService = new CoreReportingService(new CoreReportingRepository(dbContext));

        var requisition = purchaseRequisitionService.CreatePurchaseRequisition(new CreatePurchaseRequisitionRequest(
            BranchId: 1,
            RequesterUserId: 1,
            RequisitionDate: new DateOnly(2026, 4, 24),
            NeedByDate: new DateOnly(2026, 4, 27),
            PurposeText: "Core reporting replenishment",
            CurrencyCode: "EGP",
            Lines:
            [
                new PurchaseRequisitionLineRequest(1001, 8m, "EA", new DateOnly(2026, 4, 27), 24m, "Replenishment item")
            ]));

        var submittedRequisition = purchaseRequisitionService.SubmitPurchaseRequisition(requisition.PurchaseRequisitionId, 1);

        var rfq = rfqService.CreateRfq(new CreateRfqRequest(
            BranchId: 1,
            DepartmentId: 3,
            PurchaseRequisitionId: submittedRequisition.PurchaseRequisitionId,
            RequesterUserId: 1,
            BuyerUserId: 2,
            RfqType: "STANDARD",
            RfqDate: new DateOnly(2026, 4, 24),
            ValidUntilDate: new DateOnly(2026, 4, 25),
            CurrencyCode: "EGP",
            Notes: "Core reporting RFQ",
            Lines:
            [
                new CreateRfqLineRequest(
                    submittedRequisition.Lines.First().LineId,
                    submittedRequisition.Lines.First().ItemId,
                    "Reporting item",
                    8m,
                    "EA",
                    new DateOnly(2026, 4, 27),
                    "VAT14",
                    24m,
                    null)
            ],
            SupplierIds: [1]));

        var submittedRfq = rfqService.SubmitRfq(rfq.RfqId, 2);
        var quotation = quotationService.CreateQuotation(new CreateSupplierQuotationRequest(
            RequestForQuotationId: submittedRfq.RfqId,
            SupplierId: 1,
            QuoteDate: new DateOnly(2026, 4, 24),
            ValidUntilDate: new DateOnly(2026, 4, 28),
            CurrencyCode: "EGP",
            FxRate: 1m,
            Notes: "Reporting quote",
            Lines:
            [
                new CreateSupplierQuotationLineRequest(
                    submittedRfq.Lines.First().RfqLineId,
                    submittedRfq.Lines.First().ItemId,
                    8m,
                    "EA",
                    24m,
                    0m,
                    0m,
                    "VAT14",
                    1)
            ]));

        var submittedQuotation = quotationService.SubmitQuotation(quotation.SupplierQuotationId, 2);
        var evaluation = evaluationService.CreateEvaluation(new CreateRfqEvaluationRequest(
            RequestForQuotationId: submittedRfq.RfqId,
            EvaluationDate: new DateOnly(2026, 4, 24),
            Notes: "Reporting evaluation",
            Lines:
            [
                new CreateRfqEvaluationLineRequest(
                    submittedQuotation.SupplierQuotationId,
                    submittedQuotation.SupplierId,
                    95m,
                    92m,
                    90m,
                    88m,
                    1,
                    true,
                    "Recommended")
            ]));

        evaluationService.FinalizeRecommendation(evaluation.RfqEvaluationId, 3);
        var purchaseOrder = evaluationService.AwardPurchaseOrder(evaluation.RfqEvaluationId, new AwardPurchaseOrderRequest(
            ActorUserId: 4,
            PurchaseOrderDate: new DateOnly(2026, 4, 24),
            DefaultWarehouseId: 1));

        var goodsReceipt = goodsReceiptService.PostGoodsReceipt(
            goodsReceiptService.CreateGoodsReceipt(new CreateGoodsReceiptRequest(
                BranchId: 1,
                PurchaseOrderId: purchaseOrder.PurchaseOrderId,
                SupplierId: purchaseOrder.SupplierId,
                ReceivedAtUtc: new DateTime(2026, 4, 25, 9, 0, 0, DateTimeKind.Utc),
                ReceivedByUserId: 4,
                InspectionStatus: "ACCEPTED",
                Lines:
                [
                    new CreateGoodsReceiptLineRequest(
                        purchaseOrder.Lines.First().PurchaseOrderLineId,
                        purchaseOrder.Lines.First().ItemId,
                        purchaseOrder.Lines.First().WarehouseId,
                        null,
                        8m,
                        8m,
                        0m,
                        "REPORT-GRN-1",
                        null)
                ])).GoodsReceiptId,
            4);

        var apBill = apBillService.CreateBill(new CreateAccountsPayableBillRequest(
            SupplierId: 1,
            PurchaseOrderId: purchaseOrder.PurchaseOrderId,
            GoodsReceiptId: goodsReceipt.GoodsReceiptId,
            BillDate: new DateOnly(2026, 4, 25),
            DueDate: new DateOnly(2026, 4, 30),
            CurrencyCode: "EGP",
            Lines:
            [
                new CreateAccountsPayableBillLineRequest(purchaseOrder.Lines.First().PurchaseOrderLineId, purchaseOrder.Lines.First().ItemId, 8m, "EA", 24m, "VAT14")
            ]));

        apBillService.SubmitBill(apBill.AccountsPayableBillId, 3);
        apBillService.ApproveMatch(apBill.AccountsPayableBillId, 4);
        var postedBill = apBillService.PostBill(apBill.AccountsPayableBillId, 4);

        var postedPayment = apPaymentService.PostPayment(
            apPaymentService.ApprovePayment(
                apPaymentService.SubmitPayment(
                    apPaymentService.CreatePayment(new CreateAccountsPayablePaymentRequest(
                        SupplierId: 1,
                        PaymentDate: new DateOnly(2026, 4, 26),
                        PaymentMethod: "BANK_TRANSFER",
                        CurrencyCode: "EGP",
                        AmountAmount: postedBill.TotalAmount,
                        ReferenceNumber: "REPORT-AP-PAY-1",
                        Notes: "Reporting payment",
                        Allocations:
                        [
                            new CreateAccountsPayablePaymentAllocationRequest(postedBill.AccountsPayableBillId, postedBill.TotalAmount, new DateOnly(2026, 4, 26))
                        ])).AccountsPayablePaymentId,
                    5).AccountsPayablePaymentId,
                6).AccountsPayablePaymentId,
            6);

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

        var submittedSalesOrder = salesOrderService.SubmitSalesOrder(salesOrder.SalesOrderId, 2);
        var dispatch = dispatchService.PostDispatchOrder(
            dispatchService.CreateDispatchOrder(new CreateDispatchOrderRequest(
                BranchId: 1,
                SalesOrderId: submittedSalesOrder.SalesOrderId,
                DispatchWarehouseId: 1,
                PlannedShipAtUtc: new DateTime(2026, 4, 25, 11, 0, 0, DateTimeKind.Utc),
                Lines:
                [
                    new CreateDispatchOrderLineRequest(submittedSalesOrder.Lines.First().SalesOrderLineId, submittedSalesOrder.Lines.First().ItemId, 3m, null, "REPORT-DSP-1", null)
                ])).DispatchOrderId,
            new PostDispatchOrderRequest(
                ActorUserId: 6,
                ShippedAtUtc: new DateTime(2026, 4, 25, 12, 0, 0, DateTimeKind.Utc),
                DeliveredAtUtc: new DateTime(2026, 4, 25, 15, 0, 0, DateTimeKind.Utc),
                ProofOfDeliveryReceiver: "Reporting receiver"));

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

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

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

        var postedAdjustment = inventoryAdjustmentService.PostInventoryAdjustment(
            inventoryAdjustmentService.ApproveInventoryAdjustment(
                inventoryAdjustmentService.SubmitInventoryAdjustment(
                    inventoryAdjustmentService.CreateInventoryAdjustment(new CreateInventoryAdjustmentRequest(
                        BranchId: 1,
                        WarehouseId: 1,
                        AdjustmentDate: new DateOnly(2026, 4, 26),
                        ReasonCode: "COUNT_VARIANCE",
                        Notes: "Reporting adjustment",
                        Lines:
                        [
                            new CreateInventoryAdjustmentLineRequest(1001, null, 2m, "EA", 18m, "AVAILABLE", "REPORT-ADJ-1", null, null)
                        ])).InventoryAdjustmentId,
                    7).InventoryAdjustmentId,
                8).InventoryAdjustmentId,
            9);

        var postedTransfer = warehouseTransferService.PostWarehouseTransfer(
            warehouseTransferService.ApproveWarehouseTransfer(
                warehouseTransferService.SubmitWarehouseTransfer(
                    warehouseTransferService.CreateWarehouseTransfer(new CreateWarehouseTransferRequest(
                        BranchId: 1,
                        FromWarehouseId: 1,
                        ToWarehouseId: 2,
                        TransferDate: new DateOnly(2026, 4, 26),
                        ReasonCode: "REBALANCE",
                        Notes: "Reporting transfer",
                        Lines:
                        [
                            new CreateWarehouseTransferLineRequest(1001, null, null, 4m, "EA", 20m, "AVAILABLE", "REPORT-TRF-1", null, null)
                        ])).WarehouseTransferId,
                    10).WarehouseTransferId,
                11).WarehouseTransferId,
            12);

        var financeReport = reportingService.GetCoreFinanceReport(new DateOnly(2026, 4, 24), new DateOnly(2026, 4, 30));
        var usdInvoiceRow = financeReport.Rows.Single(row => row.DocumentNumber == postedUsdInvoice.InvoiceNumber && row.ReportSection == "AR");
        Assert.Equal("EGP", financeReport.BaseCurrencyCode);
        Assert.Equal(1, financeReport.PostedAccountsPayableBillCount);
        Assert.Equal(1, financeReport.PostedAccountsPayablePaymentCount);
        Assert.Equal(2, financeReport.PostedAccountsReceivableInvoiceCount);
        Assert.Equal(1, financeReport.PostedAccountsReceivableReceiptCount);
        Assert.True(financeReport.GeneralLedgerBatchCount >= 5);
        Assert.Equal(2, financeReport.PendingEtaInvoiceCount);
        Assert.Contains(financeReport.Rows, row => row.DocumentNumber == postedBill.BillNumber && row.ReportSection == "AP");
        Assert.Contains(financeReport.Rows, row => row.DocumentNumber == postedPayment.PaymentNumber && row.DocumentType == "AP_PAYMENT");
        Assert.Contains(financeReport.Rows, row => row.DocumentNumber == postedInvoice.InvoiceNumber && row.ReportSection == "AR");
        Assert.Contains(financeReport.Rows, row => row.DocumentNumber == postedReceipt.ReceiptNumber && row.DocumentType == "AR_RECEIPT");
        Assert.Equal("USD", usdInvoiceRow.CurrencyCode);
        Assert.Equal("EGP", usdInvoiceRow.BaseCurrencyCode);
        Assert.Equal(49.5m, usdInvoiceRow.FxRateToBase);
        Assert.Equal(Math.Round(postedUsdInvoice.TotalAmount * 49.5m, 2, MidpointRounding.AwayFromZero), usdInvoiceRow.BaseAmount);

        var financeCsv = reportingService.ExportCoreFinanceReportCsv(new DateOnly(2026, 4, 24), new DateOnly(2026, 4, 30));
        Assert.Contains("BusinessDate,ReportSection,DocumentType,DocumentNumber,PartyCode,CurrencyCode,BaseCurrencyCode,FxRateToBase,Amount,BaseAmount,StatusCode,ReferenceText", financeCsv);
        Assert.Contains(postedInvoice.InvoiceNumber, financeCsv);
        Assert.Contains(postedPayment.PaymentNumber, financeCsv);
        Assert.Contains(postedUsdInvoice.InvoiceNumber, financeCsv);

        var operationsReport = reportingService.GetCoreOperationsReport(new DateOnly(2026, 4, 24), new DateOnly(2026, 4, 30));
        Assert.Equal(1, operationsReport.PurchaseRequisitionCount);
        Assert.Equal(1, operationsReport.RequestForQuotationCount);
        Assert.Equal(1, operationsReport.PurchaseOrderCount);
        Assert.Equal(1, operationsReport.GoodsReceiptCount);
        Assert.Equal(1, operationsReport.SalesOrderCount);
        Assert.Equal(1, operationsReport.DispatchCount);
        Assert.Equal(1, operationsReport.InventoryAdjustmentCount);
        Assert.Equal(1, operationsReport.WarehouseTransferCount);
        Assert.Equal(8m, operationsReport.TotalReceivedQuantity);
        Assert.Equal(3m, operationsReport.TotalDispatchedQuantity);
        Assert.Equal(2m, operationsReport.TotalAdjustedAbsoluteQuantity);
        Assert.Equal(4m, operationsReport.TotalTransferredQuantity);
        Assert.Contains(operationsReport.Rows, row => row.DocumentNumber == requisition.RequisitionNumber && row.ActivityType == "PURCHASE_REQUISITION");
        Assert.Contains(operationsReport.Rows, row => row.DocumentNumber == postedTransfer.TransferNumber && row.ActivityType == "WAREHOUSE_TRANSFER");
        Assert.Contains(operationsReport.Rows, row => row.DocumentNumber == postedAdjustment.AdjustmentNumber && row.ActivityType == "INVENTORY_ADJUSTMENT");

        var operationsCsv = reportingService.ExportCoreOperationsReportCsv(new DateOnly(2026, 4, 24), new DateOnly(2026, 4, 30));
        Assert.Contains("ActivityDate,ActivityType,DocumentNumber", operationsCsv);
        Assert.Contains(purchaseOrder.PurchaseOrderNumber, operationsCsv);
        Assert.Contains(postedTransfer.TransferNumber, operationsCsv);
    }
}
