using CashLineERP.Application.Finance;
using CashLineERP.Application.Procurement;
using CashLineERP.Application.Tax;
using CashLineERP.Contracts.Finance;
using CashLineERP.Contracts.Procurement;
using CashLineERP.Contracts.Tax;
using CashLineERP.Infrastructure.Persistence.Repositories;
using System.Text.Json;

namespace CashLineERP.Api.Tests;

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

        var billService = new AccountsPayableBillService(new AccountsPayableBillRepository(dbContext));
        var invoiceService = new AccountsReceivableInvoiceService(new AccountsReceivableInvoiceRepository(dbContext));
        var taxService = new StatutoryTaxService(new StatutoryTaxRepository(dbContext));

        var createdBill = billService.CreateBill(new CreateAccountsPayableBillRequest(
            SupplierId: 1,
            PurchaseOrderId: 1,
            GoodsReceiptId: 1,
            BillDate: new DateOnly(2026, 4, 12),
            DueDate: new DateOnly(2026, 4, 25),
            CurrencyCode: "EGP",
            Lines:
            [
                new CreateAccountsPayableBillLineRequest(1, 1001, 50m, "EA", 24m, "VAT14"),
                new CreateAccountsPayableBillLineRequest(2, 1002, 30m, "BOX", 50m, "VAT14")
            ]));

        var submittedBill = billService.SubmitBill(createdBill.AccountsPayableBillId, 3);
        var billSummary = taxService.GetAccountsPayableBillTaxSummary(submittedBill.AccountsPayableBillId);

        Assert.NotNull(billSummary);
        Assert.Equal("AP_BILL", billSummary!.DocumentType);
        Assert.True(billSummary.IsStatutoryReady);
        Assert.Equal(2700m, billSummary.SubtotalAmount);
        Assert.Equal(378m, billSummary.TaxAmount);
        Assert.Equal(3078m, billSummary.TotalAmount);
        Assert.Single(billSummary.Breakdowns);
        Assert.Equal("VAT14", billSummary.Breakdowns.Single().TaxCode);

        var createdInvoice = invoiceService.CreateInvoice(new CreateAccountsReceivableInvoiceRequest(
            CustomerId: 2,
            SalesOrderId: 1,
            DispatchOrderId: 1,
            InvoiceType: "TAX",
            InvoiceDate: new DateOnly(2026, 4, 12),
            TaxDate: new DateOnly(2026, 4, 12),
            DueDate: new DateOnly(2026, 4, 26),
            CurrencyCode: "EGP",
            Lines:
            [
                new CreateAccountsReceivableInvoiceLineRequest(1, 1001, 10m, "EA", 32m, "VAT14"),
                new CreateAccountsReceivableInvoiceLineRequest(2, 1002, 5m, "BOX", 49m, "VAT14")
            ]));

        var postedInvoice = AccountsReceivableWorkflowTestHelper.SubmitApproveAndPostInvoice(invoiceService, createdInvoice.AccountsReceivableInvoiceId);
        var invoiceSummary = taxService.GetAccountsReceivableInvoiceTaxSummary(postedInvoice.AccountsReceivableInvoiceId);

        Assert.NotNull(invoiceSummary);
        Assert.Equal("AR_INVOICE", invoiceSummary!.DocumentType);
        Assert.True(invoiceSummary.IsStatutoryReady);
        Assert.Equal(565m, invoiceSummary.SubtotalAmount);
        Assert.Equal(79.1m, invoiceSummary.TaxAmount);
        Assert.Equal(644.1m, invoiceSummary.TotalAmount);
        Assert.Equal(new DateOnly(2026, 4, 12), invoiceSummary.TaxDate);
        Assert.Single(invoiceSummary.Breakdowns);
        Assert.Equal("VAT14", invoiceSummary.Breakdowns.Single().TaxCode);
    }

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

        var invoiceService = new AccountsReceivableInvoiceService(new AccountsReceivableInvoiceRepository(dbContext));
        var etaService = new EtaComplianceService(new EtaComplianceRepository(dbContext));

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

        var postedInvoice = AccountsReceivableWorkflowTestHelper.SubmitApproveAndPostInvoice(invoiceService, invoice.AccountsReceivableInvoiceId);

        var blockedSubmission = Record.Exception(() => etaService.SubmitInvoice(
            postedInvoice.AccountsReceivableInvoiceId,
            new SubmitInvoiceToEtaRequest(
                ActorUserId: 4,
                PayloadText: "{\"documentType\":\"TAX_INVOICE\"}")));

        Assert.NotNull(blockedSubmission);
        Assert.IsType<InvalidOperationException>(blockedSubmission);
        Assert.Contains("tax date", blockedSubmission.Message, StringComparison.OrdinalIgnoreCase);
    }

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

        var billService = new AccountsPayableBillService(new AccountsPayableBillRepository(dbContext));
        var invoiceService = new AccountsReceivableInvoiceService(new AccountsReceivableInvoiceRepository(dbContext));
        var taxService = new StatutoryTaxService(new StatutoryTaxRepository(dbContext));

        var createdBill = billService.CreateBill(new CreateAccountsPayableBillRequest(
            SupplierId: 1,
            PurchaseOrderId: 1,
            GoodsReceiptId: 1,
            BillDate: new DateOnly(2026, 4, 14),
            DueDate: new DateOnly(2026, 4, 28),
            CurrencyCode: "EGP",
            Lines:
            [
                new CreateAccountsPayableBillLineRequest(1, 1001, 10m, "EA", 20m, "VAT14"),
                new CreateAccountsPayableBillLineRequest(2, 1002, 4m, "BOX", 40m, "VAT0")
            ]));

        var submittedBill = billService.SubmitBill(createdBill.AccountsPayableBillId, 3);
        var billExport = taxService.GetAccountsPayableBillVatBreakdownExport(submittedBill.AccountsPayableBillId);

        Assert.NotNull(billExport);
        Assert.Equal("text/csv", billExport!.ContentType);
        Assert.Contains("DocumentType,DocumentNumber", billExport.ContentText, StringComparison.Ordinal);
        Assert.Contains("VAT14", billExport.ContentText, StringComparison.Ordinal);
        Assert.Contains("VAT0", billExport.ContentText, StringComparison.Ordinal);
        Assert.Equal(2, billExport.Breakdowns.Count);

        var createdInvoice = invoiceService.CreateInvoice(new CreateAccountsReceivableInvoiceRequest(
            CustomerId: 2,
            SalesOrderId: 1,
            DispatchOrderId: 1,
            InvoiceType: "TAX",
            InvoiceDate: new DateOnly(2026, 4, 14),
            TaxDate: new DateOnly(2026, 4, 14),
            DueDate: new DateOnly(2026, 4, 29),
            CurrencyCode: "EGP",
            Lines:
            [
                new CreateAccountsReceivableInvoiceLineRequest(1, 1001, 2m, "EA", 32m, "VAT14"),
                new CreateAccountsReceivableInvoiceLineRequest(2, 1002, 1m, "BOX", 50m, "VAT0")
            ]));

        var postedInvoice = AccountsReceivableWorkflowTestHelper.SubmitApproveAndPostInvoice(invoiceService, createdInvoice.AccountsReceivableInvoiceId);
        var invoiceExport = taxService.GetAccountsReceivableInvoiceVatBreakdownExport(postedInvoice.AccountsReceivableInvoiceId);

        Assert.NotNull(invoiceExport);
        Assert.Equal("text/csv", invoiceExport!.ContentType);
        Assert.Contains("VAT14", invoiceExport.ContentText, StringComparison.Ordinal);
        Assert.Contains("VAT0", invoiceExport.ContentText, StringComparison.Ordinal);
        Assert.Equal(2, invoiceExport.Breakdowns.Count);
    }

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

        var billService = new AccountsPayableBillService(new AccountsPayableBillRepository(dbContext));
        var invoiceService = new AccountsReceivableInvoiceService(new AccountsReceivableInvoiceRepository(dbContext));
        var taxService = new StatutoryTaxService(new StatutoryTaxRepository(dbContext));

        var createdBill = billService.CreateBill(new CreateAccountsPayableBillRequest(
            SupplierId: 1,
            PurchaseOrderId: 1,
            GoodsReceiptId: 1,
            BillDate: new DateOnly(2026, 4, 15),
            DueDate: new DateOnly(2026, 4, 30),
            CurrencyCode: "EGP",
            Lines:
            [
                new CreateAccountsPayableBillLineRequest(1, 1001, 8m, "EA", 25m, "VAT14")
            ]));

        var submittedBill = billService.SubmitBill(createdBill.AccountsPayableBillId, 3);
        var billEvidence = taxService.GetAccountsPayableBillStatutoryEvidenceExport(submittedBill.AccountsPayableBillId);

        Assert.NotNull(billEvidence);
        Assert.Equal("application/json", billEvidence!.ContentType);
        Assert.Equal("SUP-0001", billEvidence.CounterpartyCode);
        Assert.Equal("HO", billEvidence.BranchCode);

        using var billJson = JsonDocument.Parse(billEvidence.ContentText);
        Assert.Equal("PURCHASE", billJson.RootElement.GetProperty("financeReview").GetProperty("reviewPerspective").GetString());
        Assert.Equal("PENDING", billJson.RootElement.GetProperty("payablesControl").GetProperty("ThreeWayMatchStatus").GetString());

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

        var postedInvoice = AccountsReceivableWorkflowTestHelper.SubmitApproveAndPostInvoice(invoiceService, createdInvoice.AccountsReceivableInvoiceId);
        var invoiceEvidence = taxService.GetAccountsReceivableInvoiceStatutoryEvidenceExport(postedInvoice.AccountsReceivableInvoiceId);

        Assert.NotNull(invoiceEvidence);
        Assert.Equal("application/json", invoiceEvidence!.ContentType);
        Assert.Equal("CUS-0001", invoiceEvidence.CounterpartyCode);
        Assert.Equal("HO", invoiceEvidence.BranchCode);

        using var invoiceJson = JsonDocument.Parse(invoiceEvidence.ContentText);
        Assert.Equal("SALES", invoiceJson.RootElement.GetProperty("financeReview").GetProperty("reviewPerspective").GetString());
        Assert.Equal("TAX_INVOICE", invoiceJson.RootElement.GetProperty("etaReadiness").GetProperty("normalizedDocumentType").GetString());
        Assert.True(invoiceJson.RootElement.TryGetProperty("etaPayloadPreview", out _));
    }

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

        var billService = new AccountsPayableBillService(new AccountsPayableBillRepository(dbContext));
        var invoiceService = new AccountsReceivableInvoiceService(new AccountsReceivableInvoiceRepository(dbContext));
        var taxService = new StatutoryTaxService(new StatutoryTaxRepository(dbContext));

        var createdBill = billService.CreateBill(new CreateAccountsPayableBillRequest(
            SupplierId: 1,
            PurchaseOrderId: 1,
            GoodsReceiptId: 1,
            BillDate: new DateOnly(2026, 5, 27),
            DueDate: new DateOnly(2026, 5, 31),
            CurrencyCode: "EGP",
            Lines:
            [
                new CreateAccountsPayableBillLineRequest(1, 1001, 10m, "EA", 20m, "WHT3"),
                new CreateAccountsPayableBillLineRequest(2, 1002, 4m, "BOX", 50m, "WHT5")
            ]));

        var submittedBill = billService.SubmitBill(createdBill.AccountsPayableBillId, 3);
        var billSummary = taxService.GetAccountsPayableBillWithholdingSummary(submittedBill.AccountsPayableBillId);

        Assert.NotNull(billSummary);
        Assert.Equal("AP_BILL", billSummary!.DocumentType);
        Assert.True(billSummary.IsWithholdingReady);
        Assert.Equal(400m, billSummary.DocumentTotalAmount);
        Assert.Equal(16m, billSummary.TotalWithholdingAmount);
        Assert.Equal(384m, billSummary.NetSettlementAmount);
        Assert.Equal(2, billSummary.Breakdowns.Count);
        Assert.Contains(billSummary.Breakdowns, entry => entry.WithholdingCode == "WHT3" && entry.WithholdingAmount == 6m);
        Assert.Contains(billSummary.Breakdowns, entry => entry.WithholdingCode == "WHT5" && entry.WithholdingAmount == 10m);

        var billExport = taxService.GetAccountsPayableBillWithholdingExport(submittedBill.AccountsPayableBillId);
        Assert.NotNull(billExport);
        Assert.Equal("text/csv", billExport!.ContentType);
        Assert.Contains("WithholdingCode,WithholdingCategory", billExport.ContentText, StringComparison.Ordinal);
        Assert.Contains("WHT3", billExport.ContentText, StringComparison.Ordinal);
        Assert.Contains("WHT5", billExport.ContentText, StringComparison.Ordinal);

        var createdInvoice = invoiceService.CreateInvoice(new CreateAccountsReceivableInvoiceRequest(
            CustomerId: 2,
            SalesOrderId: 1,
            DispatchOrderId: 1,
            InvoiceType: "TAX",
            InvoiceDate: new DateOnly(2026, 5, 27),
            TaxDate: new DateOnly(2026, 5, 27),
            DueDate: new DateOnly(2026, 5, 31),
            CurrencyCode: "EGP",
            Lines:
            [
                new CreateAccountsReceivableInvoiceLineRequest(1, 1001, 5m, "EA", 40m, "WHT1"),
                new CreateAccountsReceivableInvoiceLineRequest(2, 1002, 2m, "BOX", 100m, "WHT3")
            ]));

        var postedInvoice = AccountsReceivableWorkflowTestHelper.SubmitApproveAndPostInvoice(invoiceService, createdInvoice.AccountsReceivableInvoiceId);
        var invoiceSummary = taxService.GetAccountsReceivableInvoiceWithholdingSummary(postedInvoice.AccountsReceivableInvoiceId);

        Assert.NotNull(invoiceSummary);
        Assert.Equal("AR_INVOICE", invoiceSummary!.DocumentType);
        Assert.True(invoiceSummary.IsWithholdingReady);
        Assert.Equal(400m, invoiceSummary.DocumentTotalAmount);
        Assert.Equal(8m, invoiceSummary.TotalWithholdingAmount);
        Assert.Equal(392m, invoiceSummary.NetSettlementAmount);
        Assert.Equal(2, invoiceSummary.Breakdowns.Count);
        Assert.Contains(invoiceSummary.Breakdowns, entry => entry.WithholdingCode == "WHT1" && entry.WithholdingAmount == 2m);
        Assert.Contains(invoiceSummary.Breakdowns, entry => entry.WithholdingCode == "WHT3" && entry.WithholdingAmount == 6m);

        var invoiceExport = taxService.GetAccountsReceivableInvoiceWithholdingExport(postedInvoice.AccountsReceivableInvoiceId);
        Assert.NotNull(invoiceExport);
        Assert.Equal("text/csv", invoiceExport!.ContentType);
        Assert.Contains("WHT1", invoiceExport.ContentText, StringComparison.Ordinal);
        Assert.Contains("WHT3", invoiceExport.ContentText, StringComparison.Ordinal);
    }

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

        var billService = new AccountsPayableBillService(new AccountsPayableBillRepository(dbContext));
        var invoiceService = new AccountsReceivableInvoiceService(new AccountsReceivableInvoiceRepository(dbContext));
        var taxService = new StatutoryTaxService(new StatutoryTaxRepository(dbContext));

        var createdBill = billService.CreateBill(new CreateAccountsPayableBillRequest(
            SupplierId: 1,
            PurchaseOrderId: 1,
            GoodsReceiptId: 1,
            BillDate: new DateOnly(2026, 5, 30),
            DueDate: new DateOnly(2026, 5, 31),
            CurrencyCode: "EGP",
            Lines:
            [
                new CreateAccountsPayableBillLineRequest(1, 1001, 5m, "EA", 20m, "VAT14"),
                new CreateAccountsPayableBillLineRequest(2, 1002, 2m, "BOX", 50m, "WHT3")
            ]));

        var submittedBill = billService.SubmitBill(createdBill.AccountsPayableBillId, 3);
        var billPackage = taxService.GetAccountsPayableBillFinanceReviewPackage(submittedBill.AccountsPayableBillId);

        Assert.NotNull(billPackage);
        Assert.Equal("AP_BILL", billPackage!.DocumentType);
        Assert.EndsWith(".zip", billPackage.PackageFileName, StringComparison.OrdinalIgnoreCase);
        Assert.Equal(3, billPackage.Entries.Count);
        Assert.Contains(billPackage.Entries, entry => entry.EntryCode == "VAT_BREAKDOWN" && entry.ContentType == "text/csv");
        Assert.Contains(billPackage.Entries, entry => entry.EntryCode == "WITHHOLDING" && entry.ContentType == "text/csv");
        Assert.Contains(billPackage.Entries, entry => entry.EntryCode == "STATUTORY_EVIDENCE" && entry.ContentType == "application/json");

        var createdInvoice = invoiceService.CreateInvoice(new CreateAccountsReceivableInvoiceRequest(
            CustomerId: 2,
            SalesOrderId: 1,
            DispatchOrderId: 1,
            InvoiceType: "TAX",
            InvoiceDate: new DateOnly(2026, 5, 30),
            TaxDate: new DateOnly(2026, 5, 30),
            DueDate: new DateOnly(2026, 5, 31),
            CurrencyCode: "EGP",
            Lines:
            [
                new CreateAccountsReceivableInvoiceLineRequest(1, 1001, 2m, "EA", 32m, "VAT14"),
                new CreateAccountsReceivableInvoiceLineRequest(2, 1002, 1m, "BOX", 60m, "WHT1")
            ]));

        var postedInvoice = AccountsReceivableWorkflowTestHelper.SubmitApproveAndPostInvoice(invoiceService, createdInvoice.AccountsReceivableInvoiceId);
        var invoicePackage = taxService.GetAccountsReceivableInvoiceFinanceReviewPackage(postedInvoice.AccountsReceivableInvoiceId);

        Assert.NotNull(invoicePackage);
        Assert.Equal("AR_INVOICE", invoicePackage!.DocumentType);
        Assert.Equal(3, invoicePackage.Entries.Count);
        Assert.Contains(invoicePackage.Entries, entry => entry.EntryCode == "STATUTORY_EVIDENCE" && entry.ContentText.Contains("\"financeReview\"", StringComparison.Ordinal));
    }
}
