using CashLineERP.Application.Customers;
using CashLineERP.Application.Parties;
using CashLineERP.Application.Identity;
using CashLineERP.Application.Notifications;
using CashLineERP.Contracts.Customers;
using CashLineERP.Contracts.Parties;
using CashLineERP.Infrastructure.Persistence;
using CashLineERP.Infrastructure.Persistence.Repositories;

namespace CashLineERP.Api.Tests;

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

        var partyService = new PartyOnboardingService(new BusinessPartyRepository(dbContext));
        var customerService = CreateCustomerService(dbContext);

        var customerParty = CreateCustomerParty(partyService, "CUS-SVC-001", hasTaxRegistration: true);
        partyService.RecordComplianceDocument(
            actorUserId: 3,
            partyId: customerParty.PartyId,
            new RecordBusinessPartyComplianceDocumentRequest(
                "TAX_CARD",
                "customer-tax-card.pdf",
                "managed://customer/tax-card.pdf",
                "application/pdf",
                2048));

        var created = customerService.CreateCustomerProfile(new CreateCustomerProfileRequest(
            customerParty.PartyId,
            null,
            "COMPANY",
            "WHOLESALE",
            "EGP",
            "DIRECT_SALES",
            "STANDARD",
            "EMAIL",
            "Primary B2B customer"));

        Assert.Equal("DRAFT", created.StatusCode);
        Assert.Equal("PENDING", created.TaxProfileStatusCode);

        customerService.SubmitCustomerProfile(created.CustomerProfileId, 3);
        customerService.ApproveCustomerProfile(created.CustomerProfileId, 4);
        var activated = customerService.ActivateCustomerProfile(created.CustomerProfileId, 4);

        Assert.True(activated.IsActive);
        Assert.Equal("APPROVED", activated.StatusCode);
        Assert.Equal("APPROVED", activated.TaxProfileStatusCode);

        var detail = customerService.GetCustomerProfile(created.CustomerProfileId);
        Assert.NotNull(detail);
        Assert.Equal("COMPANY", detail!.CustomerClassificationCode);
        Assert.Equal(1, detail.ComplianceDocumentCount);

        var filtered = customerService.GetCustomerProfiles(activeOnly: true, classificationCode: "COMPANY");
        Assert.Single(filtered);
    }

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

        var partyService = new PartyOnboardingService(new BusinessPartyRepository(dbContext));
        var customerService = CreateCustomerService(dbContext);

        var customerParty = CreateCustomerParty(partyService, "CUS-SVC-002", hasTaxRegistration: false);

        var created = customerService.CreateCustomerProfile(new CreateCustomerProfileRequest(
            customerParty.PartyId,
            null,
            "GOVERNMENT",
            "PUBLIC_SECTOR",
            "USD",
            "KEY_ACCOUNT",
            "PREFERRED",
            "PORTAL",
            null));

        customerService.SubmitCustomerProfile(created.CustomerProfileId, 3);

        var exception = Assert.Throws<InvalidOperationException>(() => customerService.ApproveCustomerProfile(created.CustomerProfileId, 4));
        Assert.Contains("tax registration", exception.Message, StringComparison.OrdinalIgnoreCase);
    }

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

        var partyService = new PartyOnboardingService(new BusinessPartyRepository(dbContext));
        var customerService = CreateCustomerService(dbContext);
        var reviewService = CreateOnboardingReviewService(dbContext);

        var customerParty = CreateCustomerParty(partyService, "CUS-SVC-004", hasTaxRegistration: true);
        partyService.RecordComplianceDocument(
            actorUserId: 3,
            partyId: customerParty.PartyId,
            new RecordBusinessPartyComplianceDocumentRequest(
                "VAT_CERTIFICATE",
                "vat-certificate.pdf",
                "managed://customer/vat-certificate-four.pdf",
                "application/pdf",
                2048));

        var created = customerService.CreateCustomerProfile(new CreateCustomerProfileRequest(
            customerParty.PartyId,
            null,
            "COMPANY",
            "WHOLESALE",
            "EGP",
            "DIRECT_SALES",
            "STANDARD",
            "EMAIL",
            null));

        customerService.SubmitCustomerProfile(created.CustomerProfileId, 3);
        reviewService.AddCustomerFinding(created.CustomerProfileId, 4, new CreateOnboardingReviewFindingRequest(
            "SIGNATORY_MISMATCH",
            "HIGH",
            "SIGNATORY_ID",
            "OPEN",
            "Signatory document does not match the submitted contact details."));

        var exception = Assert.Throws<InvalidOperationException>(() => customerService.ApproveCustomerProfile(created.CustomerProfileId, 4));
        Assert.Contains("open onboarding findings", exception.Message, StringComparison.OrdinalIgnoreCase);
    }

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

        var partyService = new PartyOnboardingService(new BusinessPartyRepository(dbContext));
        var customerService = CreateCustomerService(dbContext);

        var customerParty = CreateCustomerParty(partyService, "CUS-SVC-005", hasTaxRegistration: true);

        var created = customerService.CreateCustomerProfile(new CreateCustomerProfileRequest(
            customerParty.PartyId,
            null,
            "COMPANY",
            "WHOLESALE",
            "EGP",
            "DIRECT_SALES",
            "STANDARD",
            "EMAIL",
            null));

        customerService.SubmitCustomerProfile(created.CustomerProfileId, 3);
        var held = customerService.HoldCustomerProfile(created.CustomerProfileId, 4, new HoldCustomerProfileRequest("Awaiting compliance confirmation."));
        Assert.True(held.IsOnHold);
        Assert.Equal("Awaiting compliance confirmation.", held.HoldReason);
        Assert.Equal("SUBMITTED", held.StatusCode);

        var reopened = customerService.ReopenCustomerProfile(created.CustomerProfileId, 4, new ReopenCustomerProfileRequest("Customer corrected submitted data."));
        Assert.False(reopened.IsOnHold);
        Assert.Equal("DRAFT", reopened.StatusCode);

        customerService.SubmitCustomerProfile(created.CustomerProfileId, 3);
        var returned = customerService.ReturnCustomerProfile(created.CustomerProfileId, 4, new ReviewCustomerProfileRequest("Please upload an updated tax certificate."));
        Assert.Equal("RETURNED", returned.StatusCode);
        Assert.Equal("Please upload an updated tax certificate.", returned.ReviewDecisionNotes);
    }

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

        var partyService = new PartyOnboardingService(new BusinessPartyRepository(dbContext));
        var customerService = CreateCustomerService(dbContext);

        var customerParty = CreateCustomerParty(partyService, "CUS-SVC-006", hasTaxRegistration: true);

        var created = customerService.CreateCustomerProfile(new CreateCustomerProfileRequest(
            customerParty.PartyId,
            null,
            "COMPANY",
            "WHOLESALE",
            "EGP",
            "DIRECT_SALES",
            "STANDARD",
            "EMAIL",
            null));

        customerService.SubmitCustomerProfile(created.CustomerProfileId, 3);
        var rejected = customerService.RejectCustomerProfile(created.CustomerProfileId, 4, new ReviewCustomerProfileRequest("KYC screening result is not acceptable."));

        Assert.Equal("REJECTED", rejected.StatusCode);
        Assert.Equal("KYC screening result is not acceptable.", rejected.ReviewDecisionNotes);
    }

    private static BusinessPartyResponse CreateCustomerParty(PartyOnboardingService partyService, string partyCode, bool hasTaxRegistration)
    {
        return partyService.CreateParty(new CreateBusinessPartyRequest(
            partyCode,
            $"Customer {partyCode}",
            "CUSTOMER",
            "EG",
            null,
            hasTaxRegistration ? $"TAX-{partyCode}" : null,
            "001",
            $"{partyCode.ToLowerInvariant()}@example.com",
            "+201000000199",
            "Alexandria",
            10000m,
            30,
            false,
            null));
    }

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

        var partyService = new PartyOnboardingService(new BusinessPartyRepository(dbContext));
        var customerService = CreateCustomerService(dbContext);

        var customerParty = CreateCustomerParty(partyService, "CUS-SVC-003", hasTaxRegistration: true);
        partyService.RecordComplianceDocument(
            actorUserId: 3,
            partyId: customerParty.PartyId,
            new RecordBusinessPartyComplianceDocumentRequest(
                "VAT_CERTIFICATE",
                "vat-certificate.pdf",
                "managed://customer/vat-certificate.pdf",
                "application/pdf",
                4096));

        var created = customerService.CreateCustomerProfile(new CreateCustomerProfileRequest(
            customerParty.PartyId,
            null,
            "COMPANY",
            "WHOLESALE",
            "EGP",
            "DIRECT_SALES",
            "STANDARD",
            "EMAIL",
            "Provision access on activation"));

        customerService.SubmitCustomerProfile(created.CustomerProfileId, 3);
        customerService.ApproveCustomerProfile(created.CustomerProfileId, 4);
        customerService.ActivateCustomerProfile(created.CustomerProfileId, 4);

        var provisionedUser = dbContext.Users.Single(entry => entry.PartyId == customerParty.PartyId && entry.UserTypeCode == "EXTERNAL");
        Assert.StartsWith("CUS_", provisionedUser.LoginName, StringComparison.OrdinalIgnoreCase);

        var roleAssignment = (
            from userRole in dbContext.UserRoles
            join role in dbContext.Roles on userRole.RoleId equals role.Id
            where userRole.UserId == provisionedUser.Id && userRole.IsActive
            select role.RoleCode)
            .Single();
        Assert.Equal("CUSTOMER_ADMIN", roleAssignment);

        var queueItems = dbContext.NotificationQueueItems
            .Where(entry => entry.RelatedEntityType == "CUSTOMER" && entry.RelatedEntityId == customerParty.PartyId)
            .OrderBy(entry => entry.Id)
            .ToArray();
        Assert.Equal(3, queueItems.Length);
        Assert.Contains(queueItems, item => item.ChannelCode == "EMAIL");
        Assert.Contains(queueItems, item => item.ChannelCode == "SMS");
        Assert.Contains(queueItems, item => item.ChannelCode == "WHATSAPP");
    }

    private static CustomerProfileService CreateCustomerService(CashLineErpDbContext dbContext)
    {
        var notificationTemplateService = new NotificationTemplateService(new NotificationTemplateRepository(dbContext));
        var notificationOrchestrationService = new NotificationOrchestrationService(new NotificationOrchestrationRepository(dbContext));
        var provisioningService = new OnboardingAccessProvisioningService(
            new OnboardingAccessProvisioningRepository(dbContext),
            notificationTemplateService,
            notificationOrchestrationService,
            new IdentityReadRepository(dbContext),
            new SupplierProfileRepository(dbContext),
            new CustomerProfileRepository(dbContext),
            new AccountingOfficeClientAssignmentRepository(dbContext));
        var reviewGateService = new OnboardingReviewApprovalGateService(new OnboardingReviewRepository(dbContext));
        return new CustomerProfileService(new CustomerProfileRepository(dbContext), provisioningService, reviewGateService);
    }

    private static OnboardingReviewService CreateOnboardingReviewService(CashLineErpDbContext dbContext) =>
        new(new OnboardingReviewRepository(dbContext));
}
