using CashLineERP.Application.Finance;
using CashLineERP.Application.Tax;
using CashLineERP.Contracts.Finance;
using CashLineERP.Contracts.Tax;
using CashLineERP.Infrastructure.Persistence.Repositories;

namespace CashLineERP.Api.Tests;

public sealed class EtaComplianceServiceQaTests
{
    [Fact]
    public void Eta_Payload_Preview_Is_Generated_From_Invoice_And_Statutory_Evidence()
    {
        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, 10),
            TaxDate: new DateOnly(2026, 4, 10),
            DueDate: new DateOnly(2026, 4, 20),
            CurrencyCode: "EGP",
            Lines:
            [
                new CreateAccountsReceivableInvoiceLineRequest(1, 1001, 10m, "EA", 32m, "VAT14"),
                new CreateAccountsReceivableInvoiceLineRequest(2, 1002, 5m, "BOX", 49m, "VAT14")
            ]));

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

        var preview = etaService.GetPayloadPreview(postedInvoice.AccountsReceivableInvoiceId);

        Assert.NotNull(preview);
        Assert.True(preview!.IsStatutoryReady);
        Assert.Empty(preview.ValidationMessages);
        Assert.Contains("\"documentType\": \"TAX_INVOICE\"", preview.PayloadText);
        Assert.Contains("\"invoiceNumber\":", preview.PayloadText);
        Assert.Contains("\"taxBreakdown\":", preview.PayloadText);
        Assert.Contains("\"statutoryEvidence\":", preview.PayloadText);
        Assert.Contains("CUS-0001", preview.PayloadText);
    }

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

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

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

        var postedDebitNote = AccountsReceivableWorkflowTestHelper.SubmitApproveAndPostInvoice(invoiceService, debitNote.AccountsReceivableInvoiceId);

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

        var postedCreditNote = AccountsReceivableWorkflowTestHelper.SubmitApproveAndPostInvoice(invoiceService, creditNote.AccountsReceivableInvoiceId);

        var debitPreview = etaService.GetPayloadPreview(postedDebitNote.AccountsReceivableInvoiceId);
        var creditPreview = etaService.GetPayloadPreview(postedCreditNote.AccountsReceivableInvoiceId);

        Assert.NotNull(debitPreview);
        Assert.True(debitPreview!.IsStatutoryReady);
        Assert.Contains("\"documentType\": \"DEBIT_NOTE\"", debitPreview.PayloadText);
        Assert.Contains("\"referenceDocument\":", debitPreview.PayloadText);
        Assert.Contains("\"sourceSalesOrderId\": 1", debitPreview.PayloadText);

        Assert.NotNull(creditPreview);
        Assert.True(creditPreview!.IsStatutoryReady);
        Assert.Contains("\"documentType\": \"CREDIT_NOTE\"", creditPreview.PayloadText);
        Assert.Contains("\"referenceDocument\":", creditPreview.PayloadText);
        Assert.Contains("\"sourceDispatchOrderId\": 1", creditPreview.PayloadText);
    }

    [Fact]
    public void Eta_Validation_Pack_Reports_Branch_Buyer_Tax_And_Document_Type_Rules()
    {
        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: new DateOnly(2026, 4, 13),
            DueDate: new DateOnly(2026, 4, 23),
            CurrencyCode: "EGP",
            Lines:
            [
                new CreateAccountsReceivableInvoiceLineRequest(1, 1001, 3m, "EA", 32m, "VAT14")
            ]));

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

        var validationPack = etaService.GetSubmissionValidationPack(postedInvoice.AccountsReceivableInvoiceId);

        Assert.NotNull(validationPack);
        Assert.True(validationPack!.IsValid);
        Assert.Equal("TAX_INVOICE", validationPack.NormalizedDocumentType);
        Assert.Equal("HO", validationPack.SellerBranchCode);
        Assert.Equal("001", validationPack.SellerTaxBranchCode);
        Assert.Equal("CUS-0001", validationPack.BuyerCode);
        Assert.Contains(validationPack.Rules, rule => rule.RuleGroup == "BRANCH" && rule.IsSatisfied);
        Assert.Contains(validationPack.Rules, rule => rule.RuleGroup == "BUYER" && rule.IsSatisfied);
        Assert.Contains(validationPack.Rules, rule => rule.RuleGroup == "TAX_TOTALS" && rule.IsSatisfied);
        Assert.Contains(validationPack.Rules, rule => rule.RuleGroup == "DOCUMENT_TYPE" && rule.IsSatisfied);
    }

    [Fact]
    public void Eta_Validation_Pack_Flags_Unsupported_Document_Type_Missing_Branch_And_Tax_Total_Issues()
    {
        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: null,
            DispatchOrderId: null,
            InvoiceType: "PROFORMA",
            InvoiceDate: new DateOnly(2026, 4, 14),
            TaxDate: null,
            DueDate: new DateOnly(2026, 4, 24),
            CurrencyCode: "EGP",
            Lines:
            [
                new CreateAccountsReceivableInvoiceLineRequest(null, 1001, 1m, "EA", 32m, "VAT14")
            ]));

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

        var trackedInvoice = dbContext.AccountsReceivableInvoices.Single(entry => entry.Id == postedInvoice.AccountsReceivableInvoiceId);
        dbContext.Entry(trackedInvoice).Property(nameof(CashLineERP.Domain.Finance.AccountsReceivableInvoice.TotalAmount)).CurrentValue = postedInvoice.TotalAmount + 5m;
        dbContext.SaveChanges();

        var validationPack = etaService.GetSubmissionValidationPack(postedInvoice.AccountsReceivableInvoiceId);

        Assert.NotNull(validationPack);
        Assert.False(validationPack!.IsValid);
        Assert.Equal("UNSUPPORTED", validationPack.NormalizedDocumentType);
        Assert.Contains(validationPack.Rules, rule => rule.RuleCode == "DOC-001" && !rule.IsSatisfied);
        Assert.Contains(validationPack.Rules, rule => rule.RuleCode == "BRN-001" && !rule.IsSatisfied);
        Assert.Contains(validationPack.Rules, rule => rule.RuleCode == "TAX-004" && !rule.IsSatisfied);
    }

    [Fact]
    public void Posted_Invoice_Can_Be_Submitted_To_Eta_And_Receive_Response()
    {
        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, 10),
            TaxDate: new DateOnly(2026, 4, 10),
            DueDate: new DateOnly(2026, 4, 20),
            CurrencyCode: "EGP",
            Lines:
            [
                new CreateAccountsReceivableInvoiceLineRequest(1, 1001, 10m, "EA", 32m, "VAT14"),
                new CreateAccountsReceivableInvoiceLineRequest(2, 1002, 5m, "BOX", 49m, "VAT14")
            ]));

        var postedInvoice = AccountsReceivableWorkflowTestHelper.SubmitApproveAndPostInvoice(invoiceService, invoice.AccountsReceivableInvoiceId);
        Assert.Equal("POSTED", postedInvoice.StatusCode);

        var submission = etaService.SubmitInvoice(postedInvoice.AccountsReceivableInvoiceId, new SubmitInvoiceToEtaRequest(
            ActorUserId: 4,
            PayloadText: null));

        Assert.Equal("SUBMITTED", submission.StatusCode);
        Assert.NotNull(submission.SubmissionReference);
        Assert.Contains("\"documentType\": \"TAX_INVOICE\"", submission.PayloadText);

        var response = etaService.RecordInvoiceResponse(
            postedInvoice.AccountsReceivableInvoiceId,
            submission.EtaSubmissionLogId,
            new RecordEtaInvoiceResponseRequest(
                ActorUserId: 4,
                StatusCode: "ACCEPTED",
                EtaUuid: "ETA-UUID-000001",
                ResponseText: "{\"status\":\"accepted\"}",
                ErrorCode: null,
                ErrorMessage: null,
                NotesText: "ETA accepted the invoice."));

        Assert.Equal("ACCEPTED", response.StatusCode);
        Assert.Equal("ETA-UUID-000001", response.EtaUuid);

        var logs = etaService.GetSubmissionLogs(postedInvoice.AccountsReceivableInvoiceId);
        var events = etaService.GetDocumentEvents(postedInvoice.AccountsReceivableInvoiceId);

        Assert.Single(logs);
        Assert.Equal(2, events.Count);
        Assert.Contains(events, evt => evt.EventType == "SUBMISSION" && evt.EventStatus == "SUBMITTED");
        Assert.Contains(events, evt => evt.EventType == "RESPONSE" && evt.EventStatus == "ACCEPTED");
    }

    [Fact]
    public void Rejected_Eta_Submission_Can_Be_Corrected_And_Resubmitted()
    {
        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, 15),
            TaxDate: new DateOnly(2026, 4, 15),
            DueDate: new DateOnly(2026, 4, 25),
            CurrencyCode: "EGP",
            Lines:
            [
                new CreateAccountsReceivableInvoiceLineRequest(1, 1001, 2m, "EA", 32m, "VAT14")
            ]));

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

        var firstSubmission = etaService.SubmitInvoice(postedInvoice.AccountsReceivableInvoiceId, new SubmitInvoiceToEtaRequest(
            ActorUserId: 4,
            PayloadText: null));

        var rejectedResponse = etaService.RecordInvoiceResponse(
            postedInvoice.AccountsReceivableInvoiceId,
            firstSubmission.EtaSubmissionLogId,
            new RecordEtaInvoiceResponseRequest(
                ActorUserId: 4,
                StatusCode: "REJECTED",
                EtaUuid: null,
                ResponseText: "{\"status\":\"rejected\"}",
                ErrorCode: "ETA-422",
                ErrorMessage: "Buyer branch details require correction.",
                NotesText: "ETA rejected the first submission."));

        Assert.Equal("REJECTED", rejectedResponse.StatusCode);

        var correctedSubmission = etaService.ResubmitRejectedInvoice(
            postedInvoice.AccountsReceivableInvoiceId,
            firstSubmission.EtaSubmissionLogId,
            new ResubmitEtaInvoiceRequest(
                ActorUserId: 4,
                PayloadText: null,
                CorrectionNotes: "Buyer evidence checked and payload regenerated before resubmission."));

        Assert.Equal("SUBMITTED", correctedSubmission.StatusCode);
        Assert.NotEqual(firstSubmission.EtaSubmissionLogId, correctedSubmission.EtaSubmissionLogId);
        Assert.StartsWith("ETA-RESUB-", correctedSubmission.SubmissionReference);

        var logs = etaService.GetSubmissionLogs(postedInvoice.AccountsReceivableInvoiceId);
        var events = etaService.GetDocumentEvents(postedInvoice.AccountsReceivableInvoiceId);

        Assert.Equal(2, logs.Count);
        Assert.Contains(logs, log => log.EtaSubmissionLogId == firstSubmission.EtaSubmissionLogId && log.StatusCode == "REJECTED");
        Assert.Contains(logs, log => log.EtaSubmissionLogId == correctedSubmission.EtaSubmissionLogId && log.StatusCode == "SUBMITTED");
        Assert.Contains(events, evt => evt.EventType == "CORRECTION" && evt.EventStatus == "PREPARED");
        Assert.Contains(events, evt => evt.EventType == "RESUBMISSION" && evt.EventStatus == "SUBMITTED");
    }
}
