use api_models::three_ds_decision_rule as api_threedsecure;
use common_types::three_ds_decision_rule_engine::ThreeDSDecision;
use euclid::backend::inputs as dsl_inputs;

use crate::{consts::SCA_MANDATED_COUNTRIES, types::transformers::ForeignFrom};

// Apply SCA validations to the decision for regulated issuer/acquirer combinations.
pub fn apply_sca_validations_during_execute(
    decision: ThreeDSDecision,
    request: &api_models::three_ds_decision_rule::ThreeDsDecisionRuleExecuteRequest,
) -> ThreeDSDecision {
    let issuer_in_sca_mandated_country = request
        .issuer
        .as_ref()
        .and_then(|issuer| issuer.country)
        .map(|country| SCA_MANDATED_COUNTRIES.contains(&country))
        .unwrap_or(false);
    let acquirer_in_sca_mandated_country = request
        .acquirer
        .as_ref()
        .and_then(|acquirer| acquirer.country)
        .map(|country| SCA_MANDATED_COUNTRIES.contains(&country))
        .unwrap_or(false);
    apply_sca_validations_to_decision(
        decision,
        issuer_in_sca_mandated_country && acquirer_in_sca_mandated_country,
    )
}

fn apply_sca_validations_to_decision(
    decision: ThreeDSDecision,
    sca_mandate_applies: bool,
) -> ThreeDSDecision {
    if sca_mandate_applies {
        // NoThreeDs is not allowed when both PSPs are in an SCA-mandated region.
        match decision {
            // NoPreference requests 3DS without inventing an exemption preference.
            ThreeDSDecision::NoThreeDs => ThreeDSDecision::NoPreference,
            _ => decision,
        }
    } else {
        // PSD2/UK SCA exemptions are not applicable outside regulated regions, but
        // merchants may explicitly choose no 3DS there.
        match decision {
            ThreeDSDecision::NoThreeDs => ThreeDSDecision::NoThreeDs,
            ThreeDSDecision::NoPreference => ThreeDSDecision::NoPreference,
            // Exemption and challenge-preference decisions are forced to a challenge outside
            // PSD2. NoPreference is preserved because it does not request an exemption.
            _ => ThreeDSDecision::ChallengeRequested,
        }
    }
}

impl ForeignFrom<api_threedsecure::PaymentData> for dsl_inputs::PaymentInput {
    fn foreign_from(request_payment_data: api_threedsecure::PaymentData) -> Self {
        Self {
            amount: request_payment_data.amount,
            currency: request_payment_data.currency,
            transaction_initiator: None,
            authentication_type: None,
            capture_method: None,
            business_country: request_payment_data.business_country,
            billing_country: None,
            business_label: None,
            setup_future_usage: None,
            card_bin: request_payment_data.card_bin,
            extended_card_bin: request_payment_data.extended_card_bin,
            surcharge_amount: None,
        }
    }
}

impl ForeignFrom<Option<api_threedsecure::PaymentMethodMetaData>>
    for dsl_inputs::PaymentMethodInput
{
    fn foreign_from(
        request_payment_method_metadata: Option<api_threedsecure::PaymentMethodMetaData>,
    ) -> Self {
        Self {
            payment_method: None,
            payment_method_type: None,
            card_network: request_payment_method_metadata
                .as_ref()
                .and_then(|pm| pm.card_network.clone()),
            card_discovery: request_payment_method_metadata.and_then(|pm| pm.card_discovery),
        }
    }
}

impl ForeignFrom<api_threedsecure::CustomerDeviceData> for dsl_inputs::CustomerDeviceDataInput {
    fn foreign_from(request_customer_device_data: api_threedsecure::CustomerDeviceData) -> Self {
        Self {
            platform: request_customer_device_data.platform,
            device_type: request_customer_device_data.device_type,
            display_size: request_customer_device_data.display_size,
        }
    }
}

impl ForeignFrom<api_threedsecure::IssuerData> for dsl_inputs::IssuerDataInput {
    fn foreign_from(request_issuer_data: api_threedsecure::IssuerData) -> Self {
        Self {
            name: request_issuer_data.name,
            country: request_issuer_data.country,
        }
    }
}

impl ForeignFrom<api_threedsecure::AcquirerData> for dsl_inputs::AcquirerDataInput {
    fn foreign_from(request_acquirer_data: api_threedsecure::AcquirerData) -> Self {
        Self {
            country: request_acquirer_data.country,
            fraud_rate: request_acquirer_data.fraud_rate,
        }
    }
}

impl ForeignFrom<api_threedsecure::ThreeDsDecisionRuleExecuteRequest> for dsl_inputs::BackendInput {
    fn foreign_from(request: api_threedsecure::ThreeDsDecisionRuleExecuteRequest) -> Self {
        Self {
            metadata: request.metadata.and_then(|meta| {
                // Extract metadata from "routing_parameters" namespace if nested (during standard transaction confirmations flow),
                // or fall back to parsing the raw root metadata value if flat (during standalone test execution API calls).
                meta.get("routing_parameters")
                    .cloned()
                    .and_then(|params| {
                        serde_json::from_value::<rustc_hash::FxHashMap<String, String>>(params)
                            .map_err(|err| {
                                router_env::logger::error!(
                                    "Error deserializing routing_parameters from metadata: {:?}",
                                    err
                                );
                                err
                            })
                            .ok()
                    })
                    .or_else(|| {
                        serde_json::from_value::<rustc_hash::FxHashMap<String, String>>(meta)
                            .map_err(|err| {
                                router_env::logger::error!(
                                    "Error deserializing flat metadata: {:?}",
                                    err
                                );
                                err
                            })
                            .ok()
                    })
            }),
            payment: dsl_inputs::PaymentInput::foreign_from(request.payment),
            payment_method: dsl_inputs::PaymentMethodInput::foreign_from(request.payment_method),
            mandate: dsl_inputs::MandateData {
                mandate_acceptance_type: None,
                mandate_type: None,
                payment_type: None,
            },
            acquirer_data: request.acquirer.map(ForeignFrom::foreign_from),
            customer_device_data: request.customer_device.map(ForeignFrom::foreign_from),
            issuer_data: request.issuer.map(ForeignFrom::foreign_from),
        }
    }
}
