//! Factory for creating condition evaluators by variant name and format version.

use automapper_validation::{
    ConditionEvaluator, ConditionExprEvaluator, ConditionResult, EvaluationContext,
};
use mig_bo4e::pid_requirements::PidRequirements;
use mig_bo4e::pid_validation::PidValidationError;
use serde_json::Value;

/// Run condition-aware validation using a boxed evaluator.
///
/// Internally dispatches to a concrete generic call to satisfy `Sized` bounds.
pub fn validate_with_boxed_evaluator(
    evaluator: &dyn ConditionEvaluator,
    json: &Value,
    requirements: &PidRequirements,
    pid: &str,
    segments: &[mig_types::segment::OwnedSegment],
) -> Vec<PidValidationError> {
    // We need a Sized type to pass to ConditionExprEvaluator.
    // Use a newtype wrapper around &dyn ConditionEvaluator.
    struct EvalRef<'a>(&'a dyn ConditionEvaluator);

    impl ConditionEvaluator for EvalRef<'_> {
        fn evaluate(&self, condition: u32, ctx: &EvaluationContext) -> ConditionResult {
            self.0.evaluate(condition, ctx)
        }

        fn is_external(&self, condition: u32) -> bool {
            self.0.is_external(condition)
        }

        fn message_type(&self) -> &str {
            self.0.message_type()
        }

        fn format_version(&self) -> &str {
            self.0.format_version()
        }
    }

    let wrapper = EvalRef(evaluator);
    let external = automapper_validation::MapExternalProvider::new(Default::default());
    let ctx = EvaluationContext::new(pid, &external, segments);
    let expr_eval = ConditionExprEvaluator::new(&wrapper);
    crate::conditional_validation::validate_with_conditions(json, requirements, &expr_eval, &ctx)
}

/// Create a condition evaluator for the given variant and format version.
///
/// Returns `None` if no evaluator exists for the combination.
pub fn create_evaluator(variant: &str, fv: &str) -> Option<Box<dyn ConditionEvaluator>> {
    match (variant, fv) {
        // FV2504
        ("UTILMD_Strom", "FV2504") => Some(Box::new(
            automapper_validation::UtilmdStromConditionEvaluatorFV2504::default(),
        )),
        ("UTILMD_Gas", "FV2504") => Some(Box::new(
            automapper_validation::UtilmdGasConditionEvaluatorFV2504::default(),
        )),
        ("MSCONS", "FV2504") => Some(Box::new(
            automapper_validation::MsconsConditionEvaluatorFV2504::default(),
        )),
        ("ORDERS", "FV2504") => Some(Box::new(
            automapper_validation::OrdersConditionEvaluatorFV2504::default(),
        )),
        ("ORDRSP", "FV2504") => Some(Box::new(
            automapper_validation::OrdrspConditionEvaluatorFV2504::default(),
        )),
        ("INVOIC", "FV2504") => Some(Box::new(
            automapper_validation::InvoicConditionEvaluatorFV2504::default(),
        )),
        ("IFTSTA", "FV2504") => Some(Box::new(
            automapper_validation::IftstaConditionEvaluatorFV2504::default(),
        )),
        ("REMADV", "FV2504") => Some(Box::new(
            automapper_validation::RemadvConditionEvaluatorFV2504::default(),
        )),
        ("COMDIS", "FV2504") => Some(Box::new(
            automapper_validation::ComdisConditionEvaluatorFV2504::default(),
        )),
        ("ORDCHG", "FV2504") => Some(Box::new(
            automapper_validation::OrdchgConditionEvaluatorFV2504::default(),
        )),
        ("PRICAT", "FV2504") => Some(Box::new(
            automapper_validation::PricatConditionEvaluatorFV2504::default(),
        )),
        ("UTILTS", "FV2504") => Some(Box::new(
            automapper_validation::UtiltsConditionEvaluatorFV2504::default(),
        )),
        ("PARTIN", "FV2504") => Some(Box::new(
            automapper_validation::PartinConditionEvaluatorFV2504::default(),
        )),
        ("APERAK", "FV2504") => Some(Box::new(
            automapper_validation::AperakConditionEvaluatorFV2504::default(),
        )),
        ("CONTRL", "FV2504") => Some(Box::new(
            automapper_validation::ContrlConditionEvaluatorFV2504::default(),
        )),
        ("REQOTE", "FV2504") => Some(Box::new(
            automapper_validation::ReqoteConditionEvaluatorFV2504::default(),
        )),
        ("QUOTES", "FV2504") => Some(Box::new(
            automapper_validation::QuotesConditionEvaluatorFV2504::default(),
        )),
        // FV2510
        ("UTILMD_Strom", "FV2510") => Some(Box::new(
            automapper_validation::UtilmdStromConditionEvaluatorFV2510::default(),
        )),
        ("UTILMD_Gas", "FV2510") => Some(Box::new(
            automapper_validation::UtilmdGasConditionEvaluatorFV2510::default(),
        )),
        ("MSCONS", "FV2510") => Some(Box::new(
            automapper_validation::MsconsConditionEvaluatorFV2510::default(),
        )),
        ("ORDERS", "FV2510") => Some(Box::new(
            automapper_validation::OrdersConditionEvaluatorFV2510::default(),
        )),
        ("ORDRSP", "FV2510") => Some(Box::new(
            automapper_validation::OrdrspConditionEvaluatorFV2510::default(),
        )),
        ("INVOIC", "FV2510") => Some(Box::new(
            automapper_validation::InvoicConditionEvaluatorFV2510::default(),
        )),
        ("IFTSTA", "FV2510") => Some(Box::new(
            automapper_validation::IftstaConditionEvaluatorFV2510::default(),
        )),
        ("REMADV", "FV2510") => Some(Box::new(
            automapper_validation::RemadvConditionEvaluatorFV2510::default(),
        )),
        ("COMDIS", "FV2510") => Some(Box::new(
            automapper_validation::ComdisConditionEvaluatorFV2510::default(),
        )),
        ("ORDCHG", "FV2510") => Some(Box::new(
            automapper_validation::OrdchgConditionEvaluatorFV2510::default(),
        )),
        ("PRICAT", "FV2510") => Some(Box::new(
            automapper_validation::PricatConditionEvaluatorFV2510::default(),
        )),
        ("UTILTS", "FV2510") => Some(Box::new(
            automapper_validation::UtiltsConditionEvaluatorFV2510::default(),
        )),
        ("PARTIN", "FV2510") => Some(Box::new(
            automapper_validation::PartinConditionEvaluatorFV2510::default(),
        )),
        ("APERAK", "FV2510") => Some(Box::new(
            automapper_validation::AperakConditionEvaluatorFV2510::default(),
        )),
        ("CONTRL", "FV2510") => Some(Box::new(
            automapper_validation::ContrlConditionEvaluatorFV2510::default(),
        )),
        ("INSRPT", "FV2510") => Some(Box::new(
            automapper_validation::InsrptConditionEvaluatorFV2510::default(),
        )),
        ("REQOTE", "FV2510") => Some(Box::new(
            automapper_validation::ReqoteConditionEvaluatorFV2510::default(),
        )),
        ("QUOTES", "FV2510") => Some(Box::new(
            automapper_validation::QuotesConditionEvaluatorFV2510::default(),
        )),
        // FV2604
        ("UTILMD_Strom", "FV2604") => Some(Box::new(
            automapper_validation::UtilmdStromConditionEvaluatorFV2604::default(),
        )),
        ("UTILMD_Gas", "FV2604") => Some(Box::new(
            automapper_validation::UtilmdGasConditionEvaluatorFV2604::default(),
        )),
        ("MSCONS", "FV2604") => Some(Box::new(
            automapper_validation::MsconsConditionEvaluatorFV2604::default(),
        )),
        ("ORDERS", "FV2604") => Some(Box::new(
            automapper_validation::OrdersConditionEvaluatorFV2604::default(),
        )),
        ("ORDRSP", "FV2604") => Some(Box::new(
            automapper_validation::OrdrspConditionEvaluatorFV2604::default(),
        )),
        ("INVOIC", "FV2604") => Some(Box::new(
            automapper_validation::InvoicConditionEvaluatorFV2604::default(),
        )),
        ("IFTSTA", "FV2604") => Some(Box::new(
            automapper_validation::IftstaConditionEvaluatorFV2604::default(),
        )),
        ("REMADV", "FV2604") => Some(Box::new(
            automapper_validation::RemadvConditionEvaluatorFV2604::default(),
        )),
        ("COMDIS", "FV2604") => Some(Box::new(
            automapper_validation::ComdisConditionEvaluatorFV2604::default(),
        )),
        ("ORDCHG", "FV2604") => Some(Box::new(
            automapper_validation::OrdchgConditionEvaluatorFV2604::default(),
        )),
        ("PRICAT", "FV2604") => Some(Box::new(
            automapper_validation::PricatConditionEvaluatorFV2604::default(),
        )),
        ("UTILTS", "FV2604") => Some(Box::new(
            automapper_validation::UtiltsConditionEvaluatorFV2604::default(),
        )),
        ("PARTIN", "FV2604") => Some(Box::new(
            automapper_validation::PartinConditionEvaluatorFV2604::default(),
        )),
        ("APERAK", "FV2604") => Some(Box::new(
            automapper_validation::AperakConditionEvaluatorFV2604::default(),
        )),
        ("CONTRL", "FV2604") => Some(Box::new(
            automapper_validation::ContrlConditionEvaluatorFV2604::default(),
        )),
        ("INSRPT", "FV2604") => Some(Box::new(
            automapper_validation::InsrptConditionEvaluatorFV2604::default(),
        )),
        ("REQOTE", "FV2604") => Some(Box::new(
            automapper_validation::ReqoteConditionEvaluatorFV2604::default(),
        )),
        ("QUOTES", "FV2604") => Some(Box::new(
            automapper_validation::QuotesConditionEvaluatorFV2604::default(),
        )),
        _ => None,
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_known_evaluators_exist() {
        assert!(create_evaluator("UTILMD_Strom", "FV2504").is_some());
        assert!(create_evaluator("MSCONS", "FV2504").is_some());
        assert!(create_evaluator("ORDERS", "FV2510").is_some());
        assert!(create_evaluator("UTILMD_Gas", "FV2604").is_some());
    }

    #[test]
    fn test_unknown_evaluator_returns_none() {
        assert!(create_evaluator("NONEXISTENT", "FV2504").is_none());
        assert!(create_evaluator("UTILMD_Strom", "FV9999").is_none());
    }
}
