using CashLineERP.Contracts.External;
using CashLineERP.Contracts.Parties;

namespace CashLineERP.Application.External;

public sealed class PublicOnboardingPolicyService : IPublicOnboardingPolicyService
{
    private const string PublicSelfInitiatedPathCode = "PUBLIC_SELF_INITIATED";
    private const string InternalManualPathCode = "INTERNAL_MANUAL";

    public OnboardingPolicyResponse GetSupplierPolicy(string onboardingPathCode) =>
        BuildPolicy("SUPPLIER", NormalizeOnboardingPathCode(onboardingPathCode), BuildSupplierFieldDefinitions(), BuildSupplierChecklistDefinitions());

    public OnboardingPolicyResponse GetCustomerPolicy(string onboardingPathCode) =>
        BuildPolicy("CUSTOMER", NormalizeOnboardingPathCode(onboardingPathCode), BuildCustomerFieldDefinitions(), BuildCustomerChecklistDefinitions());

    public OnboardingCompletenessEvaluationResponse EvaluateSupplierApplication(CreatePublicSupplierOnboardingApplicationRequest request)
    {
        var fields = BuildSupplierFieldDefinitions();
        var documents = BuildSupplierChecklistDefinitions();
        var fieldEvaluations = fields.Select(field => EvaluateField(field, GetSupplierFieldValue(request, field.FieldCode))).ToArray();
        var documentEvaluations = documents.Select(item => EvaluateChecklistItem(item, request.ComplianceDocuments)).ToArray();
        return BuildEvaluation("SUPPLIER", PublicSelfInitiatedPathCode, fieldEvaluations, documentEvaluations);
    }

    public OnboardingCompletenessEvaluationResponse EvaluateCustomerApplication(CreatePublicCustomerOnboardingApplicationRequest request)
    {
        var fields = BuildCustomerFieldDefinitions();
        var documents = BuildCustomerChecklistDefinitions();
        var fieldEvaluations = fields.Select(field => EvaluateField(field, GetCustomerFieldValue(request, field.FieldCode))).ToArray();
        var documentEvaluations = documents.Select(item => EvaluateChecklistItem(item, request.ComplianceDocuments)).ToArray();
        return BuildEvaluation("CUSTOMER", PublicSelfInitiatedPathCode, fieldEvaluations, documentEvaluations);
    }

    private static string NormalizeOnboardingPathCode(string onboardingPathCode) =>
        string.IsNullOrWhiteSpace(onboardingPathCode)
            ? PublicSelfInitiatedPathCode
            : onboardingPathCode.Trim().ToUpperInvariant() switch
            {
                InternalManualPathCode => InternalManualPathCode,
                _ => PublicSelfInitiatedPathCode
            };

    private static OnboardingPolicyResponse BuildPolicy(
        string subjectType,
        string onboardingPathCode,
        IReadOnlyCollection<OnboardingKycFieldDefinitionResponse> fields,
        IReadOnlyCollection<OnboardingDocumentChecklistItemResponse> documents) =>
        new(
            subjectType,
            onboardingPathCode,
            fields.Where(field => AppliesToPath(field.Notes, onboardingPathCode)).ToArray(),
            documents.Where(document => AppliesToPath(document.Notes, onboardingPathCode)).ToArray());

    private static bool AppliesToPath(string notes, string onboardingPathCode)
    {
        if (onboardingPathCode == PublicSelfInitiatedPathCode)
        {
            return !notes.Contains("[INTERNAL_ONLY]", StringComparison.OrdinalIgnoreCase);
        }

        return !notes.Contains("[PUBLIC_ONLY]", StringComparison.OrdinalIgnoreCase);
    }

    private static IReadOnlyCollection<OnboardingKycFieldDefinitionResponse> BuildSupplierFieldDefinitions() =>
    [
        Required("PARTY_NAME", "Legal Organization Name", "SUPPORTED", "Current public intake captures the party legal name."),
        Required("COUNTRY_CODE", "Country", "SUPPORTED", "Current public intake captures country."),
        Required("TAX_REGISTRATION_NUMBER", "Tax Registration Number", "SUPPORTED", "Current public intake captures tax registration number."),
        Required("EMAIL", "Primary Email", "SUPPORTED", "Current public intake captures primary email when provided."),
        Required("MOBILE_NUMBER", "Primary Mobile Number", "SUPPORTED", "Current public intake captures primary mobile when provided."),
        Required("ADDRESS_LINE1", "Address", "SUPPORTED", "Current public intake captures a single address line."),
        Required("SUPPLIER_CATEGORY_CODE", "Supplier Category", "SUPPORTED", "Current supplier profile captures category."),
        Required("DEFAULT_CURRENCY_CODE", "Default Currency", "SUPPORTED", "Current supplier profile captures default currency."),
        Required("PREFERRED_PAYMENT_METHOD_CODE", "Preferred Payment Method", "SUPPORTED", "Current supplier profile captures preferred payment method."),
        Required("BANK_NAME", "Bank Name", "SUPPORTED", "Current supplier profile captures bank name."),
        Required("BANK_ACCOUNT_NUMBER", "Bank Account Number", "SUPPORTED", "Current supplier profile captures bank account number."),
        Required("TRADE_NAME", "Trade Name", "MISSING", "URD-required field; not present in the current public supplier intake contract."),
        Required("REGISTRATION_LICENSE_REFERENCE", "Commercial Registration Or License Reference", "MISSING", "URD-required field; not present in the current public supplier intake contract."),
        Required("CITY", "City", "MISSING", "URD-required structured city field; current contract only has a free-form address."),
        Required("PREFERRED_COMMUNICATION_CHANNEL", "Preferred Communication Channel", "MISSING", "URD-required communication preference; not present in the current contract."),
        Required("AUTHORIZED_SIGNATORY_NAME", "Authorized Signatory Name", "MISSING", "URD-required signatory data; not present in the current contract."),
        Required("AUTHORIZED_SIGNATORY_TITLE", "Authorized Signatory Title", "MISSING", "URD-required signatory role data; not present in the current contract."),
        Required("REPRESENTATIVE_ID_REFERENCE", "Representative Identity Reference", "MISSING", "URD-required representative data; not present in the current contract."),
        Required("LICENSE_EXPIRY_DATE", "License Expiry Date", "MISSING", "URD-required expiry control; not present in the current contract."),
        Required("FIRST_ADMIN_USER_NAME", "First Admin User Name", "PARTIAL", "Activation creates an external admin user, but the intake contract does not capture a named initial admin."),
        Required("FIRST_ADMIN_USER_EMAIL", "First Admin User Email", "PARTIAL", "Email can be reused operationally, but the intake contract does not separate organization contact and first admin user."),
        Optional("ALTERNATE_CONTACT", "Alternate Contact", "MISSING", "URD-required alternate contact is not present in the current contract."),
        Optional("ESCALATION_CONTACT", "Escalation Contact", "MISSING", "URD-required escalation contact is not present in the current contract."),
        Optional("OPERATIONAL_REGION", "Operational Region Or Branch Coverage", "MISSING", "URD-required operational coverage is not present in the current contract."),
        Optional("LANGUAGE_PREFERENCE", "Language Preference", "MISSING", "URD-required language preference is not present in the current contract.")
    ];

    private static IReadOnlyCollection<OnboardingKycFieldDefinitionResponse> BuildCustomerFieldDefinitions() =>
    [
        Required("PARTY_NAME", "Legal Organization Name", "SUPPORTED", "Current public intake captures the party legal name."),
        Required("COUNTRY_CODE", "Country", "SUPPORTED", "Current public intake captures country."),
        Required("TAX_REGISTRATION_NUMBER", "Tax Registration Number", "SUPPORTED", "Current public intake captures tax registration number."),
        Required("EMAIL", "Primary Email", "SUPPORTED", "Current public intake captures primary email when provided."),
        Required("MOBILE_NUMBER", "Primary Mobile Number", "SUPPORTED", "Current public intake captures primary mobile when provided."),
        Required("ADDRESS_LINE1", "Address", "SUPPORTED", "Current public intake captures a single address line."),
        Required("CUSTOMER_CLASSIFICATION_CODE", "Customer Classification", "SUPPORTED", "Current customer profile captures classification."),
        Required("CUSTOMER_SEGMENT_CODE", "Customer Segment", "SUPPORTED", "Current customer profile captures segment."),
        Required("DEFAULT_CURRENCY_CODE", "Default Currency", "SUPPORTED", "Current customer profile captures default currency."),
        Required("INVOICE_DELIVERY_METHOD_CODE", "Invoice Delivery Method", "SUPPORTED", "Current customer profile captures invoice delivery method."),
        Required("TRADE_NAME", "Trade Name", "MISSING", "URD-required field; not present in the current public customer intake contract."),
        Required("REGISTRATION_LICENSE_REFERENCE", "Commercial Registration Or License Reference", "MISSING", "URD-required field; not present in the current public customer intake contract."),
        Required("CITY", "City", "MISSING", "URD-required structured city field; current contract only has a free-form address."),
        Required("PREFERRED_COMMUNICATION_CHANNEL", "Preferred Communication Channel", "MISSING", "URD-required communication preference; not present in the current contract."),
        Required("AUTHORIZED_SIGNATORY_NAME", "Authorized Signatory Name", "MISSING", "URD-required signatory data; not present in the current contract."),
        Required("AUTHORIZED_SIGNATORY_TITLE", "Authorized Signatory Title", "MISSING", "URD-required signatory role data; not present in the current contract."),
        Required("REPRESENTATIVE_ID_REFERENCE", "Representative Identity Reference", "MISSING", "URD-required representative data; not present in the current contract."),
        Required("LICENSE_EXPIRY_DATE", "License Expiry Date", "MISSING", "URD-required expiry control; not present in the current contract."),
        Required("FIRST_ADMIN_USER_NAME", "First Admin User Name", "PARTIAL", "Activation creates an external admin user, but the intake contract does not capture a named initial admin."),
        Required("FIRST_ADMIN_USER_EMAIL", "First Admin User Email", "PARTIAL", "Email can be reused operationally, but the intake contract does not separate organization contact and first admin user."),
        Optional("ALTERNATE_CONTACT", "Alternate Contact", "MISSING", "URD-required alternate contact is not present in the current contract."),
        Optional("ESCALATION_CONTACT", "Escalation Contact", "MISSING", "URD-required escalation contact is not present in the current contract."),
        Optional("LANGUAGE_PREFERENCE", "Language Preference", "MISSING", "URD-required language preference is not present in the current contract.")
    ];

    private static IReadOnlyCollection<OnboardingDocumentChecklistItemResponse> BuildSupplierChecklistDefinitions() =>
    [
        RequiredDocument("COMMERCIAL_REGISTRATION", "Commercial Registration", "PARTIAL", ["COMMERCIAL_REGISTER"], false, true, "Current intake can capture document metadata, but checklist enforcement and quality outcomes are not yet implemented."),
        RequiredDocument("TAX_CERTIFICATE", "Tax Certificate", "PARTIAL", ["TAX_CERTIFICATE", "VAT_CERTIFICATE"], false, true, "Current intake can capture document metadata, but checklist enforcement and quality outcomes are not yet implemented."),
        RequiredDocument("BANK_CONFIRMATION", "Bank Confirmation", "PARTIAL", ["BANK_CONFIRMATION", "BANK_LETTER"], false, true, "Current intake can capture document metadata, but checklist enforcement and quality outcomes are not yet implemented."),
        RequiredDocument("SIGNATORY_AUTHORITY", "Authorization Letter Or Board Resolution", "PARTIAL", ["AUTHORIZATION_LETTER", "BOARD_RESOLUTION"], false, true, "Current intake can capture document metadata, but checklist enforcement and signatory review are not yet implemented."),
        RequiredDocument("SIGNATORY_ID", "Signatory Identity Document", "PARTIAL", ["SIGNATORY_ID", "PASSPORT"], true, true, "Current intake can capture document metadata, but expiry-aware evaluation is not yet implemented.")
    ];

    private static IReadOnlyCollection<OnboardingDocumentChecklistItemResponse> BuildCustomerChecklistDefinitions() =>
    [
        RequiredDocument("COMMERCIAL_REGISTRATION", "Commercial Registration", "PARTIAL", ["COMMERCIAL_REGISTER"], false, true, "Current intake can capture document metadata, but checklist enforcement and quality outcomes are not yet implemented."),
        RequiredDocument("TAX_CERTIFICATE", "Tax Certificate", "PARTIAL", ["TAX_CERTIFICATE", "VAT_CERTIFICATE"], false, true, "Current intake can capture document metadata, but checklist enforcement and quality outcomes are not yet implemented."),
        RequiredDocument("SIGNATORY_AUTHORITY", "Authorization Letter Or Board Resolution", "PARTIAL", ["AUTHORIZATION_LETTER", "BOARD_RESOLUTION"], false, true, "Current intake can capture document metadata, but checklist enforcement and signatory review are not yet implemented."),
        RequiredDocument("SIGNATORY_ID", "Signatory Identity Document", "PARTIAL", ["SIGNATORY_ID", "PASSPORT"], true, true, "Current intake can capture document metadata, but expiry-aware evaluation is not yet implemented."),
        RequiredDocument("CUSTOMER_CLASS_SUPPORT", "Customer Classification Support Document", "PARTIAL", ["CUSTOMER_CLASSIFICATION_SUPPORT", "REGULATORY_CLASSIFICATION"], false, true, "URD expects classification support; current intake can accept metadata references but does not enforce this checklist.")
    ];

    private static OnboardingKycFieldDefinitionResponse Required(string fieldCode, string displayName, string coverageCode, string notes) => new(fieldCode, displayName, true, coverageCode, notes);
    private static OnboardingKycFieldDefinitionResponse Optional(string fieldCode, string displayName, string coverageCode, string notes) => new(fieldCode, displayName, false, coverageCode, notes);

    private static OnboardingDocumentChecklistItemResponse RequiredDocument(string checklistItemCode, string displayName, string coverageCode, IReadOnlyCollection<string> acceptedDocumentTypes, bool expiryRequired, bool reviewerVerificationRequired, string notes) =>
        new(checklistItemCode, displayName, true, coverageCode, acceptedDocumentTypes, expiryRequired, reviewerVerificationRequired, notes);

    private static string? GetSupplierFieldValue(CreatePublicSupplierOnboardingApplicationRequest request, string fieldCode) =>
        fieldCode switch
        {
            "PARTY_NAME" => request.PartyName,
            "COUNTRY_CODE" => request.CountryCode,
            "TAX_REGISTRATION_NUMBER" => request.TaxRegistrationNumber,
            "EMAIL" => request.Email,
            "MOBILE_NUMBER" => request.MobileNumber,
            "ADDRESS_LINE1" => request.AddressLine1,
            "SUPPLIER_CATEGORY_CODE" => request.SupplierCategoryCode,
            "DEFAULT_CURRENCY_CODE" => request.DefaultCurrencyCode,
            "PREFERRED_PAYMENT_METHOD_CODE" => request.PreferredPaymentMethodCode,
            "BANK_NAME" => request.BankName,
            "BANK_ACCOUNT_NUMBER" => request.BankAccountNumber,
            "FIRST_ADMIN_USER_EMAIL" => request.Email,
            _ => null
        };

    private static string? GetCustomerFieldValue(CreatePublicCustomerOnboardingApplicationRequest request, string fieldCode) =>
        fieldCode switch
        {
            "PARTY_NAME" => request.PartyName,
            "COUNTRY_CODE" => request.CountryCode,
            "TAX_REGISTRATION_NUMBER" => request.TaxRegistrationNumber,
            "EMAIL" => request.Email,
            "MOBILE_NUMBER" => request.MobileNumber,
            "ADDRESS_LINE1" => request.AddressLine1,
            "CUSTOMER_CLASSIFICATION_CODE" => request.CustomerClassificationCode,
            "CUSTOMER_SEGMENT_CODE" => request.CustomerSegmentCode,
            "DEFAULT_CURRENCY_CODE" => request.DefaultCurrencyCode,
            "INVOICE_DELIVERY_METHOD_CODE" => request.InvoiceDeliveryMethodCode,
            "FIRST_ADMIN_USER_EMAIL" => request.Email,
            _ => null
        };

    private static OnboardingKycFieldEvaluationResponse EvaluateField(OnboardingKycFieldDefinitionResponse field, string? value)
    {
        var isSatisfied = !string.IsNullOrWhiteSpace(value);
        var statusCode = isSatisfied
            ? "SATISFIED"
            : field.CoverageCode switch
            {
                "MISSING" => "MISSING_FROM_CURRENT_IMPLEMENTATION",
                "PARTIAL" => "PARTIALLY_SUPPORTED_AND_UNFILLED",
                _ => "REQUIRED_VALUE_MISSING"
            };

        var detail = isSatisfied
            ? "Value provided in the current onboarding intake."
            : field.CoverageCode switch
            {
                "MISSING" => "This URD-required field is not present in the current onboarding contract.",
                "PARTIAL" => "This field is only partially supported by the current implementation and is not fully captured as a distinct onboarding control.",
                _ => "This field is supported by the current intake contract but no value was provided."
            };

        return new OnboardingKycFieldEvaluationResponse(field.FieldCode, field.DisplayName, field.IsRequired, field.CoverageCode, isSatisfied, statusCode, detail);
    }

    private static OnboardingDocumentChecklistEvaluationResponse EvaluateChecklistItem(OnboardingDocumentChecklistItemResponse checklistItem, IReadOnlyCollection<RecordBusinessPartyComplianceDocumentRequest>? documents)
    {
        var matchedDocument = documents?.FirstOrDefault(document => checklistItem.AcceptedDocumentTypes.Contains(document.DocumentType, StringComparer.OrdinalIgnoreCase));
        var isSatisfied = matchedDocument is not null;
        var statusCode = isSatisfied ? "SATISFIED" : "CHECKLIST_ITEM_MISSING";
        var detail = isSatisfied
            ? $"Matched document type '{matchedDocument!.DocumentType}'."
            : "No matching compliance-document metadata was supplied for this required checklist item.";

        return new OnboardingDocumentChecklistEvaluationResponse(checklistItem.ChecklistItemCode, checklistItem.DisplayName, checklistItem.IsRequired, checklistItem.CoverageCode, isSatisfied, statusCode, detail);
    }

    private static OnboardingCompletenessEvaluationResponse BuildEvaluation(
        string subjectType,
        string onboardingPathCode,
        IReadOnlyCollection<OnboardingKycFieldEvaluationResponse> fieldEvaluations,
        IReadOnlyCollection<OnboardingDocumentChecklistEvaluationResponse> documentEvaluations)
    {
        var requiredFields = fieldEvaluations.Where(entry => entry.IsRequired).ToArray();
        var requiredDocuments = documentEvaluations.Where(entry => entry.IsRequired).ToArray();
        var blockingIssues = requiredFields.Where(entry => !entry.IsSatisfied).Select(entry => $"Required KYC field '{entry.DisplayName}' is unresolved ({entry.StatusCode}).")
            .Concat(requiredDocuments.Where(entry => !entry.IsSatisfied).Select(entry => $"Required document '{entry.DisplayName}' is missing."))
            .ToArray();

        return new OnboardingCompletenessEvaluationResponse(
            subjectType,
            onboardingPathCode,
            requiredFields.Length,
            requiredFields.Count(entry => entry.IsSatisfied),
            requiredDocuments.Length,
            requiredDocuments.Count(entry => entry.IsSatisfied),
            blockingIssues.Length == 0,
            blockingIssues,
            fieldEvaluations,
            documentEvaluations);
    }
}
