// File: crates/euclid/src/frontend/ast/parser.rs

use common_utils::types::MinorUnit;
use nom::{
    branch, bytes::complete, character::complete as pchar, combinator, error, multi, sequence,
};

use crate::{frontend::ast, types::DummyOutput};
pub type ParseResult<T, U> = nom::IResult<T, U, error::VerboseError<T>>;

pub enum EuclidError {
    InvalidPercentage(String),
    InvalidConnector(String),
    InvalidOperator(String),
    InvalidNumber(String),
}

pub trait EuclidParsable: Sized {
    fn parse_output(input: &str) -> ParseResult<&str, Self>;
}

impl EuclidParsable for DummyOutput {
    fn parse_output(input: &str) -> ParseResult<&str, Self> {
        let string_w = sequence::delimited(
            skip_ws(complete::tag("\"")),
            complete::take_while(|c| c != '"'),
            skip_ws(complete::tag("\"")),
        );
        let full_sequence = multi::many0(sequence::preceded(
            skip_ws(complete::tag(",")),
            sequence::delimited(
                skip_ws(complete::tag("\"")),
                complete::take_while(|c| c != '"'),
                skip_ws(complete::tag("\"")),
            ),
        ));
        let sequence = sequence::pair(string_w, full_sequence);
        error::context(
            "dummy_strings",
            combinator::map(
                sequence::delimited(
                    skip_ws(complete::tag("[")),
                    sequence,
                    skip_ws(complete::tag("]")),
                ),
                |out: (&str, Vec<&str>)| {
                    let mut first = out.1;
                    first.insert(0, out.0);
                    let v = first.iter().map(|s| s.to_string()).collect();
                    Self { outputs: v }
                },
            ),
        )(input)
    }
}
pub fn skip_ws<'a, F, O>(inner: F) -> impl FnMut(&'a str) -> ParseResult<&'a str, O>
where
    F: FnMut(&'a str) -> ParseResult<&'a str, O> + 'a,
{
    sequence::preceded(pchar::multispace0, inner)
}

pub fn num_i64(input: &str) -> ParseResult<&str, i64> {
    error::context(
        "num_i32",
        combinator::map_res(
            complete::take_while1(|c: char| c.is_ascii_digit()),
            |o: &str| {
                o.parse::<i64>()
                    .map_err(|_| EuclidError::InvalidNumber(o.to_string()))
            },
        ),
    )(input)
}

pub fn string_str(input: &str) -> ParseResult<&str, String> {
    error::context(
        "String",
        combinator::map(
            sequence::delimited(
                complete::tag("\""),
                complete::take_while1(|c: char| c != '"'),
                complete::tag("\""),
            ),
            |val: &str| val.to_string(),
        ),
    )(input)
}

pub fn identifier(input: &str) -> ParseResult<&str, String> {
    error::context(
        "identifier",
        combinator::map(
            sequence::pair(
                complete::take_while1(|c: char| c.is_ascii_alphabetic() || c == '_'),
                complete::take_while(|c: char| c.is_ascii_alphanumeric() || c == '_'),
            ),
            |out: (&str, &str)| out.0.to_string() + out.1,
        ),
    )(input)
}
pub fn percentage(input: &str) -> ParseResult<&str, u8> {
    error::context(
        "volume_split_percentage",
        combinator::map_res(
            sequence::terminated(
                complete::take_while_m_n(1, 2, |c: char| c.is_ascii_digit()),
                complete::tag("%"),
            ),
            |o: &str| {
                o.parse::<u8>()
                    .map_err(|_| EuclidError::InvalidPercentage(o.to_string()))
            },
        ),
    )(input)
}

pub fn number_value(input: &str) -> ParseResult<&str, ast::ValueType> {
    error::context(
        "number_value",
        combinator::map(num_i64, |n| ast::ValueType::Number(MinorUnit::new(n))),
    )(input)
}

pub fn str_value(input: &str) -> ParseResult<&str, ast::ValueType> {
    error::context(
        "str_value",
        combinator::map(string_str, ast::ValueType::StrValue),
    )(input)
}
pub fn enum_value_string(input: &str) -> ParseResult<&str, String> {
    combinator::map(
        sequence::pair(
            complete::take_while1(|c: char| c.is_ascii_alphabetic() || c == '_'),
            complete::take_while(|c: char| c.is_ascii_alphanumeric() || c == '_'),
        ),
        |out: (&str, &str)| out.0.to_string() + out.1,
    )(input)
}

pub fn enum_variant_value(input: &str) -> ParseResult<&str, ast::ValueType> {
    error::context(
        "enum_variant_value",
        combinator::map(enum_value_string, ast::ValueType::EnumVariant),
    )(input)
}

pub fn number_array_value(input: &str) -> ParseResult<&str, ast::ValueType> {
    fn num_minor_unit(input: &str) -> ParseResult<&str, MinorUnit> {
        combinator::map(num_i64, MinorUnit::new)(input)
    }
    let many_with_comma = multi::many0(sequence::preceded(
        skip_ws(complete::tag(",")),
        skip_ws(num_minor_unit),
    ));

    let full_sequence = sequence::pair(skip_ws(num_minor_unit), many_with_comma);

    error::context(
        "number_array_value",
        combinator::map(
            sequence::delimited(
                skip_ws(complete::tag("(")),
                full_sequence,
                skip_ws(complete::tag(")")),
            ),
            |tup: (MinorUnit, Vec<MinorUnit>)| {
                let mut rest = tup.1;
                rest.insert(0, tup.0);
                ast::ValueType::NumberArray(rest)
            },
        ),
    )(input)
}

pub fn enum_variant_array_value(input: &str) -> ParseResult<&str, ast::ValueType> {
    let many_with_comma = multi::many0(sequence::preceded(
        skip_ws(complete::tag(",")),
        skip_ws(enum_value_string),
    ));

    let full_sequence = sequence::pair(skip_ws(enum_value_string), many_with_comma);

    error::context(
        "enum_variant_array_value",
        combinator::map(
            sequence::delimited(
                skip_ws(complete::tag("(")),
                full_sequence,
                skip_ws(complete::tag(")")),
            ),
            |tup: (String, Vec<String>)| {
                let mut rest = tup.1;
                rest.insert(0, tup.0);
                ast::ValueType::EnumVariantArray(rest)
            },
        ),
    )(input)
}

pub fn number_comparison(input: &str) -> ParseResult<&str, ast::NumberComparison> {
    let operator = combinator::map_res(
        branch::alt((
            complete::tag(">="),
            complete::tag("<="),
            complete::tag(">"),
            complete::tag("<"),
        )),
        |s: &str| match s {
            ">=" => Ok(ast::ComparisonType::GreaterThanEqual),
            "<=" => Ok(ast::ComparisonType::LessThanEqual),
            ">" => Ok(ast::ComparisonType::GreaterThan),
            "<" => Ok(ast::ComparisonType::LessThan),
            _ => Err(EuclidError::InvalidOperator(s.to_string())),
        },
    );

    error::context(
        "number_comparison",
        combinator::map(
            sequence::pair(operator, num_i64),
            |tup: (ast::ComparisonType, i64)| ast::NumberComparison {
                comparison_type: tup.0,
                number: MinorUnit::new(tup.1),
            },
        ),
    )(input)
}

pub fn number_comparison_array_value(input: &str) -> ParseResult<&str, ast::ValueType> {
    let many_with_comma = multi::many0(sequence::preceded(
        skip_ws(complete::tag(",")),
        skip_ws(number_comparison),
    ));

    let full_sequence = sequence::pair(skip_ws(number_comparison), many_with_comma);

    error::context(
        "number_comparison_array_value",
        combinator::map(
            sequence::delimited(
                skip_ws(complete::tag("(")),
                full_sequence,
                skip_ws(complete::tag(")")),
            ),
            |tup: (ast::NumberComparison, Vec<ast::NumberComparison>)| {
                let mut rest = tup.1;
                rest.insert(0, tup.0);
                ast::ValueType::NumberComparisonArray(rest)
            },
        ),
    )(input)
}

pub fn value_type(input: &str) -> ParseResult<&str, ast::ValueType> {
    error::context(
        "value_type",
        branch::alt((
            number_value,
            enum_variant_value,
            enum_variant_array_value,
            number_array_value,
            number_comparison_array_value,
            str_value,
        )),
    )(input)
}

pub fn comparison_type(input: &str) -> ParseResult<&str, ast::ComparisonType> {
    error::context(
        "comparison_operator",
        combinator::map_res(
            branch::alt((
                complete::tag("/="),
                complete::tag(">="),
                complete::tag("<="),
                complete::tag("="),
                complete::tag(">"),
                complete::tag("<"),
            )),
            |s: &str| match s {
                "/=" => Ok(ast::ComparisonType::NotEqual),
                ">=" => Ok(ast::ComparisonType::GreaterThanEqual),
                "<=" => Ok(ast::ComparisonType::LessThanEqual),
                "=" => Ok(ast::ComparisonType::Equal),
                ">" => Ok(ast::ComparisonType::GreaterThan),
                "<" => Ok(ast::ComparisonType::LessThan),
                _ => Err(EuclidError::InvalidOperator(s.to_string())),
            },
        ),
    )(input)
}

pub fn comparison(input: &str) -> ParseResult<&str, ast::Comparison> {
    error::context(
        "condition",
        combinator::map(
            sequence::tuple((
                skip_ws(complete::take_while1(|c: char| {
                    c.is_ascii_alphabetic() || c == '.' || c == '_'
                })),
                skip_ws(comparison_type),
                skip_ws(value_type),
            )),
            |tup: (&str, ast::ComparisonType, ast::ValueType)| ast::Comparison {
                lhs: tup.0.to_string(),
                comparison: tup.1,
                value: tup.2,
                metadata: std::collections::HashMap::new(),
            },
        ),
    )(input)
}

pub fn arbitrary_comparison(input: &str) -> ParseResult<&str, ast::Comparison> {
    error::context(
        "condition",
        combinator::map(
            sequence::tuple((
                skip_ws(string_str),
                skip_ws(comparison_type),
                skip_ws(string_str),
            )),
            |tup: (String, ast::ComparisonType, String)| ast::Comparison {
                lhs: "metadata".to_string(),
                comparison: tup.1,
                value: ast::ValueType::MetadataVariant(ast::MetadataValue {
                    key: tup.0,
                    value: tup.2,
                }),
                metadata: std::collections::HashMap::new(),
            },
        ),
    )(input)
}

pub fn comparison_array(input: &str) -> ParseResult<&str, Vec<ast::Comparison>> {
    let many_with_ampersand = error::context(
        "many_with_amp",
        multi::many0(sequence::preceded(skip_ws(complete::tag("&")), comparison)),
    );

    let full_sequence = sequence::pair(
        skip_ws(branch::alt((comparison, arbitrary_comparison))),
        many_with_ampersand,
    );

    error::context(
        "comparison_array",
        combinator::map(
            full_sequence,
            |tup: (ast::Comparison, Vec<ast::Comparison>)| {
                let mut rest = tup.1;
                rest.insert(0, tup.0);
                rest
            },
        ),
    )(input)
}

pub fn if_statement(input: &str) -> ParseResult<&str, ast::IfStatement> {
    let nested_block = sequence::delimited(
        skip_ws(complete::tag("{")),
        multi::many0(if_statement),
        skip_ws(complete::tag("}")),
    );

    error::context(
        "if_statement",
        combinator::map(
            sequence::pair(comparison_array, combinator::opt(nested_block)),
            |tup: (ast::IfCondition, Option<Vec<ast::IfStatement>>)| ast::IfStatement {
                condition: tup.0,
                nested: tup.1,
            },
        ),
    )(input)
}

pub fn rule_conditions_array(input: &str) -> ParseResult<&str, Vec<ast::IfStatement>> {
    error::context(
        "rules_array",
        sequence::delimited(
            skip_ws(complete::tag("{")),
            multi::many1(if_statement),
            skip_ws(complete::tag("}")),
        ),
    )(input)
}

pub fn rule<O: EuclidParsable>(input: &str) -> ParseResult<&str, ast::Rule<O>> {
    let rule_name = error::context(
        "rule_name",
        combinator::map(
            skip_ws(sequence::pair(
                complete::take_while1(|c: char| c.is_ascii_alphabetic() || c == '_'),
                complete::take_while(|c: char| c.is_ascii_alphanumeric() || c == '_'),
            )),
            |out: (&str, &str)| out.0.to_string() + out.1,
        ),
    );

    let connector_selection = error::context(
        "parse_output",
        sequence::preceded(skip_ws(complete::tag(":")), output),
    );

    error::context(
        "rule",
        combinator::map(
            sequence::tuple((rule_name, connector_selection, rule_conditions_array)),
            |tup: (String, O, Vec<ast::IfStatement>)| ast::Rule {
                name: tup.0,
                connector_selection: tup.1,
                statements: tup.2,
            },
        ),
    )(input)
}

pub fn output<O: EuclidParsable>(input: &str) -> ParseResult<&str, O> {
    O::parse_output(input)
}

pub fn default_output<O: EuclidParsable + 'static>(input: &str) -> ParseResult<&str, O> {
    error::context(
        "default_output",
        sequence::preceded(
            sequence::pair(skip_ws(complete::tag("default")), skip_ws(pchar::char(':'))),
            skip_ws(output),
        ),
    )(input)
}

pub fn program<O: EuclidParsable + 'static>(input: &str) -> ParseResult<&str, ast::Program<O>> {
    error::context(
        "program",
        combinator::map(
            sequence::pair(default_output, multi::many1(skip_ws(rule::<O>))),
            |tup: (O, Vec<ast::Rule<O>>)| ast::Program {
                default_selection: tup.0,
                rules: tup.1,
                metadata: std::collections::HashMap::new(),
            },
        ),
    )(input)
}


// File: crates/euclid/src/backend/vir_interpreter.rs

pub mod types;

use std::fmt::Debug;

use serde::{Deserialize, Serialize};

use crate::{
    backend::{self, inputs, EuclidBackend},
    frontend::{
        ast,
        dir::{self, EuclidDirFilter},
        vir,
    },
};

#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct VirInterpreterBackend<O> {
    program: vir::ValuedProgram<O>,
}

impl<O> VirInterpreterBackend<O>
where
    O: Clone,
{
    #[inline]
    fn eval_comparison(comp: &vir::ValuedComparison, ctx: &types::Context) -> bool {
        match &comp.logic {
            vir::ValuedComparisonLogic::PositiveDisjunction => {
                comp.values.iter().any(|v| ctx.check_presence(v))
            }
            vir::ValuedComparisonLogic::NegativeConjunction => {
                comp.values.iter().all(|v| !ctx.check_presence(v))
            }
        }
    }

    #[inline]
    fn eval_condition(cond: &vir::ValuedIfCondition, ctx: &types::Context) -> bool {
        cond.iter().all(|comp| Self::eval_comparison(comp, ctx))
    }

    fn eval_statement(stmt: &vir::ValuedIfStatement, ctx: &types::Context) -> bool {
        if Self::eval_condition(&stmt.condition, ctx) {
            {
                stmt.nested.as_ref().is_none_or(|nested_stmts| {
                    nested_stmts.iter().any(|s| Self::eval_statement(s, ctx))
                })
            }
        } else {
            false
        }
    }

    fn eval_rule(rule: &vir::ValuedRule<O>, ctx: &types::Context) -> bool {
        rule.statements
            .iter()
            .any(|stmt| Self::eval_statement(stmt, ctx))
    }

    fn eval_program(
        program: &vir::ValuedProgram<O>,
        ctx: &types::Context,
    ) -> backend::BackendOutput<O> {
        program
            .rules
            .iter()
            .find(|rule| Self::eval_rule(rule, ctx))
            .map_or_else(
                || backend::BackendOutput {
                    connector_selection: program.default_selection.clone(),
                    rule_name: None,
                },
                |rule| backend::BackendOutput {
                    connector_selection: rule.connector_selection.clone(),
                    rule_name: Some(rule.name.clone()),
                },
            )
    }
}

impl<O> EuclidBackend<O> for VirInterpreterBackend<O>
where
    O: Clone + EuclidDirFilter,
{
    type Error = types::VirInterpreterError;

    fn with_program(program: ast::Program<O>) -> Result<Self, Self::Error> {
        let dir_program = ast::lowering::lower_program(program)
            .map_err(types::VirInterpreterError::LoweringError)?;

        let vir_program = dir::lowering::lower_program(dir_program)
            .map_err(types::VirInterpreterError::LoweringError)?;

        Ok(Self {
            program: vir_program,
        })
    }

    fn execute(
        &self,
        input: inputs::BackendInput,
    ) -> Result<backend::BackendOutput<O>, Self::Error> {
        let ctx = types::Context::from_input(input);
        Ok(Self::eval_program(&self.program, &ctx))
    }
}
#[cfg(all(test, feature = "ast_parser"))]
mod test {
    use common_utils::types::MinorUnit;
    use rustc_hash::FxHashMap;

    use super::*;
    use crate::{enums, types::DummyOutput};

    #[test]
    fn test_execution() {
        let program_str = r#"
        default: [ "stripe",  "adyen"]

        rule_1: ["stripe"]
        {
            pay_later = klarna
        }

        rule_2: ["adyen"]
        {
            pay_later = affirm
        }
        "#;

        let (_, program) = ast::parser::program::<DummyOutput>(program_str).expect("Program");
        let inp = inputs::BackendInput {
            metadata: None,
            payment: inputs::PaymentInput {
                amount: MinorUnit::new(32),
                card_bin: None,
                extended_card_bin: None,
                currency: enums::Currency::USD,
                authentication_type: Some(enums::AuthenticationType::NoThreeDs),
                capture_method: Some(enums::CaptureMethod::Automatic),
                business_country: Some(enums::Country::UnitedStatesOfAmerica),
                billing_country: Some(enums::Country::France),
                business_label: None,
                setup_future_usage: None,
            },
            payment_method: inputs::PaymentMethodInput {
                payment_method: Some(enums::PaymentMethod::PayLater),
                payment_method_type: Some(enums::PaymentMethodType::Affirm),
                card_network: None,
            },
            mandate: inputs::MandateData {
                mandate_acceptance_type: None,
                mandate_type: None,
                payment_type: None,
            },
            acquirer_data: None,
            customer_device_data: None,
            issuer_data: None,
        };

        let backend = VirInterpreterBackend::<DummyOutput>::with_program(program).expect("Program");
        let result = backend.execute(inp).expect("Execution");
        assert_eq!(result.rule_name.expect("Rule Name").as_str(), "rule_2");
    }
    #[test]
    fn test_payment_type() {
        let program_str = r#"
        default: ["stripe", "adyen"]
        rule_1: ["stripe"]
        {
           payment_type = setup_mandate
        }
        "#;

        let (_, program) = ast::parser::program::<DummyOutput>(program_str).expect("Program");
        let inp = inputs::BackendInput {
            metadata: None,
            payment: inputs::PaymentInput {
                amount: MinorUnit::new(32),
                currency: enums::Currency::USD,
                card_bin: Some("123456".to_string()),
                extended_card_bin: None,
                authentication_type: Some(enums::AuthenticationType::NoThreeDs),
                capture_method: Some(enums::CaptureMethod::Automatic),
                business_country: Some(enums::Country::UnitedStatesOfAmerica),
                billing_country: Some(enums::Country::France),
                business_label: None,
                setup_future_usage: None,
            },
            payment_method: inputs::PaymentMethodInput {
                payment_method: Some(enums::PaymentMethod::PayLater),
                payment_method_type: Some(enums::PaymentMethodType::Affirm),
                card_network: None,
            },
            mandate: inputs::MandateData {
                mandate_acceptance_type: None,
                mandate_type: None,
                payment_type: Some(enums::PaymentType::SetupMandate),
            },
            acquirer_data: None,
            customer_device_data: None,
            issuer_data: None,
        };

        let backend = VirInterpreterBackend::<DummyOutput>::with_program(program).expect("Program");
        let result = backend.execute(inp).expect("Execution");
        assert_eq!(result.rule_name.expect("Rule Name").as_str(), "rule_1");
    }

    #[test]
    fn test_ppt_flow() {
        let program_str = r#"
        default: ["stripe", "adyen"]
        rule_1: ["stripe"]
        {
           payment_type = ppt_mandate
        }
        "#;

        let (_, program) = ast::parser::program::<DummyOutput>(program_str).expect("Program");
        let inp = inputs::BackendInput {
            metadata: None,
            payment: inputs::PaymentInput {
                amount: MinorUnit::new(32),
                currency: enums::Currency::USD,
                card_bin: Some("123456".to_string()),
                extended_card_bin: None,
                authentication_type: Some(enums::AuthenticationType::NoThreeDs),
                capture_method: Some(enums::CaptureMethod::Automatic),
                business_country: Some(enums::Country::UnitedStatesOfAmerica),
                billing_country: Some(enums::Country::France),
                business_label: None,
                setup_future_usage: None,
            },
            payment_method: inputs::PaymentMethodInput {
                payment_method: Some(enums::PaymentMethod::PayLater),
                payment_method_type: Some(enums::PaymentMethodType::Affirm),
                card_network: None,
            },
            mandate: inputs::MandateData {
                mandate_acceptance_type: None,
                mandate_type: None,
                payment_type: Some(enums::PaymentType::PptMandate),
            },
            acquirer_data: None,
            customer_device_data: None,
            issuer_data: None,
        };

        let backend = VirInterpreterBackend::<DummyOutput>::with_program(program).expect("Program");
        let result = backend.execute(inp).expect("Execution");
        assert_eq!(result.rule_name.expect("Rule Name").as_str(), "rule_1");
    }

    #[test]
    fn test_mandate_type() {
        let program_str = r#"
        default: ["stripe", "adyen"]
        rule_1: ["stripe"]
        {
           mandate_type = single_use
        }
        "#;

        let (_, program) = ast::parser::program::<DummyOutput>(program_str).expect("Program");
        let inp = inputs::BackendInput {
            metadata: None,
            payment: inputs::PaymentInput {
                amount: MinorUnit::new(32),
                currency: enums::Currency::USD,
                card_bin: Some("123456".to_string()),
                extended_card_bin: None,
                authentication_type: Some(enums::AuthenticationType::NoThreeDs),
                capture_method: Some(enums::CaptureMethod::Automatic),
                business_country: Some(enums::Country::UnitedStatesOfAmerica),
                billing_country: Some(enums::Country::France),
                business_label: None,
                setup_future_usage: None,
            },
            payment_method: inputs::PaymentMethodInput {
                payment_method: Some(enums::PaymentMethod::PayLater),
                payment_method_type: Some(enums::PaymentMethodType::Affirm),
                card_network: None,
            },
            mandate: inputs::MandateData {
                mandate_acceptance_type: None,
                mandate_type: Some(enums::MandateType::SingleUse),
                payment_type: None,
            },
            acquirer_data: None,
            customer_device_data: None,
            issuer_data: None,
        };

        let backend = VirInterpreterBackend::<DummyOutput>::with_program(program).expect("Program");
        let result = backend.execute(inp).expect("Execution");
        assert_eq!(result.rule_name.expect("Rule Name").as_str(), "rule_1");
    }

    #[test]
    fn test_mandate_acceptance_type() {
        let program_str = r#"
        default: ["stripe","adyen"]
        rule_1: ["stripe"]
        {
           mandate_acceptance_type = online
        }
        "#;

        let (_, program) = ast::parser::program::<DummyOutput>(program_str).expect("Program");
        let inp = inputs::BackendInput {
            metadata: None,
            payment: inputs::PaymentInput {
                amount: MinorUnit::new(32),
                currency: enums::Currency::USD,
                card_bin: Some("123456".to_string()),
                extended_card_bin: None,
                authentication_type: Some(enums::AuthenticationType::NoThreeDs),
                capture_method: Some(enums::CaptureMethod::Automatic),
                business_country: Some(enums::Country::UnitedStatesOfAmerica),
                billing_country: Some(enums::Country::France),
                business_label: None,
                setup_future_usage: None,
            },
            payment_method: inputs::PaymentMethodInput {
                payment_method: Some(enums::PaymentMethod::PayLater),
                payment_method_type: Some(enums::PaymentMethodType::Affirm),
                card_network: None,
            },
            mandate: inputs::MandateData {
                mandate_acceptance_type: Some(enums::MandateAcceptanceType::Online),
                mandate_type: None,
                payment_type: None,
            },
            acquirer_data: None,
            customer_device_data: None,
            issuer_data: None,
        };

        let backend = VirInterpreterBackend::<DummyOutput>::with_program(program).expect("Program");
        let result = backend.execute(inp).expect("Execution");
        assert_eq!(result.rule_name.expect("Rule Name").as_str(), "rule_1");
    }
    #[test]
    fn test_card_bin() {
        let program_str = r#"
        default: ["stripe", "adyen"]

        rule_1: ["stripe"]
        {
           card_bin="123456"
        }
        "#;

        let (_, program) = ast::parser::program::<DummyOutput>(program_str).expect("Program");
        let inp = inputs::BackendInput {
            metadata: None,
            payment: inputs::PaymentInput {
                amount: MinorUnit::new(32),
                currency: enums::Currency::USD,
                card_bin: Some("123456".to_string()),
                extended_card_bin: None,
                authentication_type: Some(enums::AuthenticationType::NoThreeDs),
                capture_method: Some(enums::CaptureMethod::Automatic),
                business_country: Some(enums::Country::UnitedStatesOfAmerica),
                billing_country: Some(enums::Country::France),
                business_label: None,
                setup_future_usage: None,
            },
            payment_method: inputs::PaymentMethodInput {
                payment_method: Some(enums::PaymentMethod::PayLater),
                payment_method_type: Some(enums::PaymentMethodType::Affirm),
                card_network: None,
            },
            mandate: inputs::MandateData {
                mandate_acceptance_type: None,
                mandate_type: None,
                payment_type: None,
            },
            acquirer_data: None,
            customer_device_data: None,
            issuer_data: None,
        };

        let backend = VirInterpreterBackend::<DummyOutput>::with_program(program).expect("Program");
        let result = backend.execute(inp).expect("Execution");
        assert_eq!(result.rule_name.expect("Rule Name").as_str(), "rule_1");
    }
    #[test]
    fn test_payment_amount() {
        let program_str = r#"
        default: ["stripe", "adyen"]

        rule_1: ["stripe"]
        {
           amount = 32
        }
        "#;

        let (_, program) = ast::parser::program::<DummyOutput>(program_str).expect("Program");
        let inp = inputs::BackendInput {
            metadata: None,
            payment: inputs::PaymentInput {
                amount: MinorUnit::new(32),
                currency: enums::Currency::USD,
                card_bin: None,
                extended_card_bin: None,
                authentication_type: Some(enums::AuthenticationType::NoThreeDs),
                capture_method: Some(enums::CaptureMethod::Automatic),
                business_country: Some(enums::Country::UnitedStatesOfAmerica),
                billing_country: Some(enums::Country::France),
                business_label: None,
                setup_future_usage: None,
            },
            payment_method: inputs::PaymentMethodInput {
                payment_method: Some(enums::PaymentMethod::PayLater),
                payment_method_type: Some(enums::PaymentMethodType::Affirm),
                card_network: None,
            },
            mandate: inputs::MandateData {
                mandate_acceptance_type: None,
                mandate_type: None,
                payment_type: None,
            },
            acquirer_data: None,
            customer_device_data: None,
            issuer_data: None,
        };

        let backend = VirInterpreterBackend::<DummyOutput>::with_program(program).expect("Program");
        let result = backend.execute(inp).expect("Execution");
        assert_eq!(result.rule_name.expect("Rule Name").as_str(), "rule_1");
    }
    #[test]
    fn test_payment_method() {
        let program_str = r#"
        default: ["stripe", "adyen"]

        rule_1: ["stripe"]
        {
           payment_method = pay_later
        }
        "#;

        let (_, program) = ast::parser::program::<DummyOutput>(program_str).expect("Program");
        let inp = inputs::BackendInput {
            metadata: None,
            payment: inputs::PaymentInput {
                amount: MinorUnit::new(32),
                currency: enums::Currency::USD,
                card_bin: None,
                extended_card_bin: None,
                authentication_type: Some(enums::AuthenticationType::NoThreeDs),
                capture_method: Some(enums::CaptureMethod::Automatic),
                business_country: Some(enums::Country::UnitedStatesOfAmerica),
                billing_country: Some(enums::Country::France),
                business_label: None,
                setup_future_usage: None,
            },
            payment_method: inputs::PaymentMethodInput {
                payment_method: Some(enums::PaymentMethod::PayLater),
                payment_method_type: Some(enums::PaymentMethodType::Affirm),
                card_network: None,
            },
            mandate: inputs::MandateData {
                mandate_acceptance_type: None,
                mandate_type: None,
                payment_type: None,
            },
            acquirer_data: None,
            customer_device_data: None,
            issuer_data: None,
        };

        let backend = VirInterpreterBackend::<DummyOutput>::with_program(program).expect("Program");
        let result = backend.execute(inp).expect("Execution");
        assert_eq!(result.rule_name.expect("Rule Name").as_str(), "rule_1");
    }
    #[test]
    fn test_future_usage() {
        let program_str = r#"
        default: ["stripe", "adyen"]

        rule_1: ["stripe"]
        {
           setup_future_usage = off_session
        }
        "#;

        let (_, program) = ast::parser::program::<DummyOutput>(program_str).expect("Program");
        let inp = inputs::BackendInput {
            metadata: None,
            payment: inputs::PaymentInput {
                amount: MinorUnit::new(32),
                currency: enums::Currency::USD,
                card_bin: None,
                extended_card_bin: None,
                authentication_type: Some(enums::AuthenticationType::NoThreeDs),
                capture_method: Some(enums::CaptureMethod::Automatic),
                business_country: Some(enums::Country::UnitedStatesOfAmerica),
                billing_country: Some(enums::Country::France),
                business_label: None,
                setup_future_usage: Some(enums::SetupFutureUsage::OffSession),
            },
            payment_method: inputs::PaymentMethodInput {
                payment_method: Some(enums::PaymentMethod::PayLater),
                payment_method_type: Some(enums::PaymentMethodType::Affirm),
                card_network: None,
            },
            mandate: inputs::MandateData {
                mandate_acceptance_type: None,
                mandate_type: None,
                payment_type: None,
            },
            acquirer_data: None,
            customer_device_data: None,
            issuer_data: None,
        };

        let backend = VirInterpreterBackend::<DummyOutput>::with_program(program).expect("Program");
        let result = backend.execute(inp).expect("Execution");
        assert_eq!(result.rule_name.expect("Rule Name").as_str(), "rule_1");
    }

    #[test]
    fn test_metadata_execution() {
        let program_str = r#"
        default: ["stripe"," adyen"]

        rule_1: ["stripe"]
        {
        "metadata_key" = "arbitrary meta"
        }
        "#;
        let mut meta_map = FxHashMap::default();
        meta_map.insert("metadata_key".to_string(), "arbitrary meta".to_string());
        let (_, program) = ast::parser::program::<DummyOutput>(program_str).expect("Program");
        let inp = inputs::BackendInput {
            metadata: Some(meta_map),
            payment: inputs::PaymentInput {
                amount: MinorUnit::new(32),
                card_bin: None,
                extended_card_bin: None,
                currency: enums::Currency::USD,
                authentication_type: Some(enums::AuthenticationType::NoThreeDs),
                capture_method: Some(enums::CaptureMethod::Automatic),
                business_country: Some(enums::Country::UnitedStatesOfAmerica),
                billing_country: Some(enums::Country::France),
                business_label: None,
                setup_future_usage: None,
            },
            payment_method: inputs::PaymentMethodInput {
                payment_method: Some(enums::PaymentMethod::PayLater),
                payment_method_type: Some(enums::PaymentMethodType::Affirm),
                card_network: None,
            },
            mandate: inputs::MandateData {
                mandate_acceptance_type: None,
                mandate_type: None,
                payment_type: None,
            },
            acquirer_data: None,
            customer_device_data: None,
            issuer_data: None,
        };

        let backend = VirInterpreterBackend::<DummyOutput>::with_program(program).expect("Program");
        let result = backend.execute(inp).expect("Execution");
        assert_eq!(result.rule_name.expect("Rule Name").as_str(), "rule_1");
    }

    #[test]
    fn test_less_than_operator() {
        let program_str = r#"
        default: ["stripe", "adyen"]

        rule_1: ["stripe"]
        {
           amount>=123
        }
        "#;
        let (_, program) = ast::parser::program::<DummyOutput>(program_str).expect("Program");
        let inp_greater = inputs::BackendInput {
            metadata: None,
            payment: inputs::PaymentInput {
                amount: MinorUnit::new(150),
                card_bin: None,
                extended_card_bin: None,
                currency: enums::Currency::USD,
                authentication_type: Some(enums::AuthenticationType::NoThreeDs),
                capture_method: Some(enums::CaptureMethod::Automatic),
                business_country: Some(enums::Country::UnitedStatesOfAmerica),
                billing_country: Some(enums::Country::France),
                business_label: None,
                setup_future_usage: None,
            },
            payment_method: inputs::PaymentMethodInput {
                payment_method: Some(enums::PaymentMethod::PayLater),
                payment_method_type: Some(enums::PaymentMethodType::Affirm),
                card_network: None,
            },
            mandate: inputs::MandateData {
                mandate_acceptance_type: None,
                mandate_type: None,
                payment_type: None,
            },
            acquirer_data: None,
            customer_device_data: None,
            issuer_data: None,
        };
        let mut inp_equal = inp_greater.clone();
        inp_equal.payment.amount = MinorUnit::new(123);
        let backend = VirInterpreterBackend::<DummyOutput>::with_program(program).expect("Program");
        let result_greater = backend.execute(inp_greater).expect("Execution");
        let result_equal = backend.execute(inp_equal).expect("Execution");
        assert_eq!(
            result_equal.rule_name.expect("Rule Name").as_str(),
            "rule_1"
        );
        assert_eq!(
            result_greater.rule_name.expect("Rule Name").as_str(),
            "rule_1"
        );
    }

    #[test]
    fn test_greater_than_operator() {
        let program_str = r#"
        default: ["stripe", "adyen"]

        rule_1: ["stripe"]
        {
           amount<=123
        }
        "#;
        let (_, program) = ast::parser::program::<DummyOutput>(program_str).expect("Program");
        let inp_lower = inputs::BackendInput {
            metadata: None,
            payment: inputs::PaymentInput {
                amount: MinorUnit::new(120),
                card_bin: None,
                extended_card_bin: None,
                currency: enums::Currency::USD,
                authentication_type: Some(enums::AuthenticationType::NoThreeDs),
                capture_method: Some(enums::CaptureMethod::Automatic),
                business_country: Some(enums::Country::UnitedStatesOfAmerica),
                billing_country: Some(enums::Country::France),
                business_label: None,
                setup_future_usage: None,
            },
            payment_method: inputs::PaymentMethodInput {
                payment_method: Some(enums::PaymentMethod::PayLater),
                payment_method_type: Some(enums::PaymentMethodType::Affirm),
                card_network: None,
            },
            mandate: inputs::MandateData {
                mandate_acceptance_type: None,
                mandate_type: None,
                payment_type: None,
            },
            acquirer_data: None,
            customer_device_data: None,
            issuer_data: None,
        };
        let mut inp_equal = inp_lower.clone();
        inp_equal.payment.amount = MinorUnit::new(123);
        let backend = VirInterpreterBackend::<DummyOutput>::with_program(program).expect("Program");
        let result_equal = backend.execute(inp_equal).expect("Execution");
        let result_lower = backend.execute(inp_lower).expect("Execution");
        assert_eq!(
            result_equal.rule_name.expect("Rule Name").as_str(),
            "rule_1"
        );
        assert_eq!(
            result_lower.rule_name.expect("Rule Name").as_str(),
            "rule_1"
        );
    }
}


// File: crates/euclid/src/backend/inputs.rs

use rustc_hash::FxHashMap;
use serde::{Deserialize, Serialize};

use crate::{
    enums,
    frontend::dir::enums::{CustomerDeviceDisplaySize, CustomerDevicePlatform, CustomerDeviceType},
};

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct MandateData {
    pub mandate_acceptance_type: Option<enums::MandateAcceptanceType>,
    pub mandate_type: Option<enums::MandateType>,
    pub payment_type: Option<enums::PaymentType>,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PaymentMethodInput {
    pub payment_method: Option<enums::PaymentMethod>,
    pub payment_method_type: Option<enums::PaymentMethodType>,
    pub card_network: Option<enums::CardNetwork>,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PaymentInput {
    pub amount: common_utils::types::MinorUnit,
    pub currency: enums::Currency,
    pub authentication_type: Option<enums::AuthenticationType>,
    pub card_bin: Option<String>,
    pub extended_card_bin: Option<String>,
    pub capture_method: Option<enums::CaptureMethod>,
    pub business_country: Option<enums::Country>,
    pub billing_country: Option<enums::Country>,
    pub business_label: Option<String>,
    pub setup_future_usage: Option<enums::SetupFutureUsage>,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AcquirerDataInput {
    pub country: Option<enums::Country>,
    pub fraud_rate: Option<f64>,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CustomerDeviceDataInput {
    pub platform: Option<CustomerDevicePlatform>,
    pub device_type: Option<CustomerDeviceType>,
    pub display_size: Option<CustomerDeviceDisplaySize>,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct IssuerDataInput {
    pub name: Option<String>,
    pub country: Option<enums::Country>,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct BackendInput {
    pub metadata: Option<FxHashMap<String, String>>,
    pub payment: PaymentInput,
    pub payment_method: PaymentMethodInput,
    pub acquirer_data: Option<AcquirerDataInput>,
    pub customer_device_data: Option<CustomerDeviceDataInput>,
    pub issuer_data: Option<IssuerDataInput>,
    pub mandate: MandateData,
}


// File: crates/euclid/src/backend/interpreter.rs

pub mod types;

use common_utils::types::MinorUnit;

use crate::{
    backend::{self, inputs, EuclidBackend},
    frontend::ast,
};

pub struct InterpreterBackend<O> {
    program: ast::Program<O>,
}

impl<O> InterpreterBackend<O>
where
    O: Clone,
{
    fn eval_number_comparison_array(
        num: MinorUnit,
        array: &[ast::NumberComparison],
    ) -> Result<bool, types::InterpreterError> {
        for comparison in array {
            let other = comparison.number;
            let res = match comparison.comparison_type {
                ast::ComparisonType::GreaterThan => num > other,
                ast::ComparisonType::LessThan => num < other,
                ast::ComparisonType::LessThanEqual => num <= other,
                ast::ComparisonType::GreaterThanEqual => num >= other,
                ast::ComparisonType::Equal => num == other,
                ast::ComparisonType::NotEqual => num != other,
            };

            if res {
                return Ok(true);
            }
        }

        Ok(false)
    }

    fn eval_comparison(
        comparison: &ast::Comparison,
        ctx: &types::Context,
    ) -> Result<bool, types::InterpreterError> {
        use ast::{ComparisonType::*, ValueType::*};

        let value = ctx
            .get(&comparison.lhs)
            .ok_or_else(|| types::InterpreterError {
                error_type: types::InterpreterErrorType::InvalidKey(comparison.lhs.clone()),
                metadata: comparison.metadata.clone(),
            })?;

        if let Some(val) = value {
            match (val, &comparison.comparison, &comparison.value) {
                (EnumVariant(e1), Equal, EnumVariant(e2)) => Ok(e1 == e2),
                (EnumVariant(e1), NotEqual, EnumVariant(e2)) => Ok(e1 != e2),
                (EnumVariant(e), Equal, EnumVariantArray(evec)) => Ok(evec.iter().any(|v| e == v)),
                (EnumVariant(e), NotEqual, EnumVariantArray(evec)) => {
                    Ok(evec.iter().all(|v| e != v))
                }
                (Number(n1), Equal, Number(n2)) => Ok(n1 == n2),
                (Number(n1), NotEqual, Number(n2)) => Ok(n1 != n2),
                (Number(n1), LessThanEqual, Number(n2)) => Ok(n1 <= n2),
                (Number(n1), GreaterThanEqual, Number(n2)) => Ok(n1 >= n2),
                (Number(n1), LessThan, Number(n2)) => Ok(n1 < n2),
                (Number(n1), GreaterThan, Number(n2)) => Ok(n1 > n2),
                (Number(n), Equal, NumberArray(nvec)) => Ok(nvec.iter().any(|v| v == n)),
                (Number(n), NotEqual, NumberArray(nvec)) => Ok(nvec.iter().all(|v| v != n)),
                (Number(n), Equal, NumberComparisonArray(ncvec)) => {
                    Self::eval_number_comparison_array(*n, ncvec)
                }
                _ => Err(types::InterpreterError {
                    error_type: types::InterpreterErrorType::InvalidComparison,
                    metadata: comparison.metadata.clone(),
                }),
            }
        } else {
            Ok(false)
        }
    }

    fn eval_if_condition(
        condition: &ast::IfCondition,
        ctx: &types::Context,
    ) -> Result<bool, types::InterpreterError> {
        for comparison in condition {
            let res = Self::eval_comparison(comparison, ctx)?;

            if !res {
                return Ok(false);
            }
        }

        Ok(true)
    }

    fn eval_if_statement(
        stmt: &ast::IfStatement,
        ctx: &types::Context,
    ) -> Result<bool, types::InterpreterError> {
        let cond_res = Self::eval_if_condition(&stmt.condition, ctx)?;

        if !cond_res {
            return Ok(false);
        }

        if let Some(ref nested) = stmt.nested {
            for nested_if in nested {
                let res = Self::eval_if_statement(nested_if, ctx)?;

                if res {
                    return Ok(true);
                }
            }

            return Ok(false);
        }

        Ok(true)
    }

    fn eval_rule_statements(
        statements: &[ast::IfStatement],
        ctx: &types::Context,
    ) -> Result<bool, types::InterpreterError> {
        for stmt in statements {
            let res = Self::eval_if_statement(stmt, ctx)?;

            if res {
                return Ok(true);
            }
        }

        Ok(false)
    }

    #[inline]
    fn eval_rule(
        rule: &ast::Rule<O>,
        ctx: &types::Context,
    ) -> Result<bool, types::InterpreterError> {
        Self::eval_rule_statements(&rule.statements, ctx)
    }

    fn eval_program(
        program: &ast::Program<O>,
        ctx: &types::Context,
    ) -> Result<backend::BackendOutput<O>, types::InterpreterError> {
        for rule in &program.rules {
            let res = Self::eval_rule(rule, ctx)?;

            if res {
                return Ok(backend::BackendOutput {
                    connector_selection: rule.connector_selection.clone(),
                    rule_name: Some(rule.name.clone()),
                });
            }
        }

        Ok(backend::BackendOutput {
            connector_selection: program.default_selection.clone(),
            rule_name: None,
        })
    }
}

impl<O> EuclidBackend<O> for InterpreterBackend<O>
where
    O: Clone,
{
    type Error = types::InterpreterError;

    fn with_program(program: ast::Program<O>) -> Result<Self, Self::Error> {
        Ok(Self { program })
    }

    fn execute(&self, input: inputs::BackendInput) -> Result<super::BackendOutput<O>, Self::Error> {
        let ctx: types::Context = input.into();
        Self::eval_program(&self.program, &ctx)
    }
}


// File: crates/euclid/src/backend/interpreter/types.rs

use std::{collections::HashMap, fmt, ops::Deref, string::ToString};

use serde::Serialize;

use crate::{backend::inputs, frontend::ast::ValueType, types::EuclidKey};

#[derive(Debug, Clone, Serialize, thiserror::Error)]
#[serde(tag = "type", content = "data", rename_all = "snake_case")]
pub enum InterpreterErrorType {
    #[error("Invalid key received '{0}'")]
    InvalidKey(String),
    #[error("Invalid Comparison")]
    InvalidComparison,
}

#[derive(Debug, Clone, Serialize, thiserror::Error)]
pub struct InterpreterError {
    pub error_type: InterpreterErrorType,
    pub metadata: HashMap<String, serde_json::Value>,
}

impl fmt::Display for InterpreterError {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        InterpreterErrorType::fmt(&self.error_type, f)
    }
}

pub struct Context(HashMap<String, Option<ValueType>>);

impl Deref for Context {
    type Target = HashMap<String, Option<ValueType>>;

    fn deref(&self) -> &Self::Target {
        &self.0
    }
}

impl From<inputs::BackendInput> for Context {
    fn from(input: inputs::BackendInput) -> Self {
        let ctx = HashMap::<String, Option<ValueType>>::from_iter([
            (
                EuclidKey::PaymentMethod.to_string(),
                input
                    .payment_method
                    .payment_method
                    .map(|pm| ValueType::EnumVariant(pm.to_string())),
            ),
            (
                EuclidKey::PaymentMethodType.to_string(),
                input
                    .payment_method
                    .payment_method_type
                    .map(|pt| ValueType::EnumVariant(pt.to_string())),
            ),
            (
                EuclidKey::AuthenticationType.to_string(),
                input
                    .payment
                    .authentication_type
                    .map(|at| ValueType::EnumVariant(at.to_string())),
            ),
            (
                EuclidKey::CaptureMethod.to_string(),
                input
                    .payment
                    .capture_method
                    .map(|cm| ValueType::EnumVariant(cm.to_string())),
            ),
            (
                EuclidKey::PaymentAmount.to_string(),
                Some(ValueType::Number(input.payment.amount)),
            ),
            (
                EuclidKey::PaymentCurrency.to_string(),
                Some(ValueType::EnumVariant(input.payment.currency.to_string())),
            ),
        ]);

        Self(ctx)
    }
}


// File: crates/euclid/src/backend/vir_interpreter/types.rs

use rustc_hash::{FxHashMap, FxHashSet};

use crate::{
    backend::inputs::BackendInput,
    dssa,
    types::{self, EuclidKey, EuclidValue, MetadataValue, NumValueRefinement, StrValue},
};

#[derive(Debug, Clone, serde::Serialize, thiserror::Error)]
pub enum VirInterpreterError {
    #[error("Error when lowering the program: {0:?}")]
    LoweringError(dssa::types::AnalysisError),
}

pub struct Context {
    atomic_values: FxHashSet<EuclidValue>,
    numeric_values: FxHashMap<EuclidKey, EuclidValue>,
}

impl Context {
    pub fn check_presence(&self, value: &EuclidValue) -> bool {
        let key = value.get_key();

        match key.key_type() {
            types::DataType::MetadataValue => self.atomic_values.contains(value),
            types::DataType::StrValue => self.atomic_values.contains(value),
            types::DataType::EnumVariant => self.atomic_values.contains(value),
            types::DataType::Number => {
                let ctx_num_value = self
                    .numeric_values
                    .get(&key)
                    .and_then(|value| value.get_num_value());

                value.get_num_value().zip(ctx_num_value).is_some_and(
                    |(program_value, ctx_value)| {
                        let program_num = program_value.number;
                        let ctx_num = ctx_value.number;

                        match &program_value.refinement {
                            None => program_num == ctx_num,
                            Some(NumValueRefinement::NotEqual) => ctx_num != program_num,
                            Some(NumValueRefinement::GreaterThan) => ctx_num > program_num,
                            Some(NumValueRefinement::GreaterThanEqual) => ctx_num >= program_num,
                            Some(NumValueRefinement::LessThanEqual) => ctx_num <= program_num,
                            Some(NumValueRefinement::LessThan) => ctx_num < program_num,
                        }
                    },
                )
            }
        }
    }

    pub fn from_input(input: BackendInput) -> Self {
        let payment = input.payment;
        let payment_method = input.payment_method;
        let meta_data = input.metadata;
        let acquirer_data = input.acquirer_data;
        let customer_device_data = input.customer_device_data;
        let issuer_data = input.issuer_data;
        let payment_mandate = input.mandate;

        let mut enum_values: FxHashSet<EuclidValue> =
            FxHashSet::from_iter([EuclidValue::PaymentCurrency(payment.currency)]);

        if let Some(pm) = payment_method.payment_method {
            enum_values.insert(EuclidValue::PaymentMethod(pm));
        }

        if let Some(pmt) = payment_method.payment_method_type {
            enum_values.insert(EuclidValue::PaymentMethodType(pmt));
        }

        if let Some(met) = meta_data {
            for (key, value) in met.into_iter() {
                enum_values.insert(EuclidValue::Metadata(MetadataValue { key, value }));
            }
        }

        if let Some(card_network) = payment_method.card_network {
            enum_values.insert(EuclidValue::CardNetwork(card_network));
        }

        if let Some(at) = payment.authentication_type {
            enum_values.insert(EuclidValue::AuthenticationType(at));
        }

        if let Some(capture_method) = payment.capture_method {
            enum_values.insert(EuclidValue::CaptureMethod(capture_method));
        }

        if let Some(country) = payment.business_country {
            enum_values.insert(EuclidValue::BusinessCountry(country));
        }

        if let Some(country) = payment.billing_country {
            enum_values.insert(EuclidValue::BillingCountry(country));
        }
        if let Some(card_bin) = payment.card_bin {
            enum_values.insert(EuclidValue::CardBin(StrValue { value: card_bin }));
        }
        if let Some(extended_card_bin) = payment.extended_card_bin {
            enum_values.insert(EuclidValue::ExtendedCardBin(StrValue {
                value: extended_card_bin,
            }));
        }
        if let Some(business_label) = payment.business_label {
            enum_values.insert(EuclidValue::BusinessLabel(StrValue {
                value: business_label,
            }));
        }
        if let Some(setup_future_usage) = payment.setup_future_usage {
            enum_values.insert(EuclidValue::SetupFutureUsage(setup_future_usage));
        }
        if let Some(payment_type) = payment_mandate.payment_type {
            enum_values.insert(EuclidValue::PaymentType(payment_type));
        }
        if let Some(mandate_type) = payment_mandate.mandate_type {
            enum_values.insert(EuclidValue::MandateType(mandate_type));
        }
        if let Some(mandate_acceptance_type) = payment_mandate.mandate_acceptance_type {
            enum_values.insert(EuclidValue::MandateAcceptanceType(mandate_acceptance_type));
        }

        if let Some(acquirer_country) = acquirer_data.clone().and_then(|data| data.country) {
            enum_values.insert(EuclidValue::AcquirerCountry(acquirer_country));
        }

        // Handle customer device data
        if let Some(device_data) = customer_device_data {
            if let Some(platform) = device_data.platform {
                enum_values.insert(EuclidValue::CustomerDevicePlatform(platform));
            }
            if let Some(device_type) = device_data.device_type {
                enum_values.insert(EuclidValue::CustomerDeviceType(device_type));
            }
            if let Some(display_size) = device_data.display_size {
                enum_values.insert(EuclidValue::CustomerDeviceDisplaySize(display_size));
            }
        }

        // Handle issuer data
        if let Some(issuer) = issuer_data {
            if let Some(name) = issuer.name {
                enum_values.insert(EuclidValue::IssuerName(StrValue { value: name }));
            }
            if let Some(country) = issuer.country {
                enum_values.insert(EuclidValue::IssuerCountry(country));
            }
        }

        let numeric_values: FxHashMap<EuclidKey, EuclidValue> = FxHashMap::from_iter([(
            EuclidKey::PaymentAmount,
            EuclidValue::PaymentAmount(types::NumValue {
                number: payment.amount,
                refinement: None,
            }),
        )]);

        Self {
            atomic_values: enum_values,
            numeric_values,
        }
    }
}


// File: crates/euclid/src/dssa/state_machine.rs

use super::types::EuclidAnalysable;
use crate::{dssa::types, frontend::dir, types::Metadata};

#[derive(Debug, Clone, serde::Serialize, thiserror::Error)]
#[serde(tag = "type", content = "info", rename_all = "snake_case")]
pub enum StateMachineError {
    #[error("Index out of bounds: {0}")]
    IndexOutOfBounds(&'static str),
}

#[derive(Debug)]
struct ComparisonStateMachine<'a> {
    values: &'a [dir::DirValue],
    logic: &'a dir::DirComparisonLogic,
    metadata: &'a Metadata,
    count: usize,
    ctx_idx: usize,
}

impl<'a> ComparisonStateMachine<'a> {
    #[inline]
    fn is_finished(&self) -> bool {
        self.count + 1 >= self.values.len()
            || matches!(self.logic, dir::DirComparisonLogic::NegativeConjunction)
    }

    #[inline]
    fn advance(&mut self) {
        if let dir::DirComparisonLogic::PositiveDisjunction = self.logic {
            self.count = (self.count + 1) % self.values.len();
        }
    }

    #[inline]
    fn reset(&mut self) {
        self.count = 0;
    }

    #[inline]
    fn put(&self, context: &mut types::ConjunctiveContext<'a>) -> Result<(), StateMachineError> {
        if let dir::DirComparisonLogic::PositiveDisjunction = self.logic {
            *context
                .get_mut(self.ctx_idx)
                .ok_or(StateMachineError::IndexOutOfBounds(
                    "in ComparisonStateMachine while indexing into context",
                ))? = types::ContextValue::assertion(
                self.values
                    .get(self.count)
                    .ok_or(StateMachineError::IndexOutOfBounds(
                        "in ComparisonStateMachine while indexing into values",
                    ))?,
                self.metadata,
            );
        }
        Ok(())
    }

    #[inline]
    fn push(&self, context: &mut types::ConjunctiveContext<'a>) -> Result<(), StateMachineError> {
        match self.logic {
            dir::DirComparisonLogic::PositiveDisjunction => {
                context.push(types::ContextValue::assertion(
                    self.values
                        .get(self.count)
                        .ok_or(StateMachineError::IndexOutOfBounds(
                            "in ComparisonStateMachine while pushing",
                        ))?,
                    self.metadata,
                ));
            }

            dir::DirComparisonLogic::NegativeConjunction => {
                context.push(types::ContextValue::negation(self.values, self.metadata));
            }
        }
        Ok(())
    }
}

#[derive(Debug)]
struct ConditionStateMachine<'a> {
    state_machines: Vec<ComparisonStateMachine<'a>>,
    start_ctx_idx: usize,
}

impl<'a> ConditionStateMachine<'a> {
    fn new(condition: &'a [dir::DirComparison], start_idx: usize) -> Self {
        let mut machines = Vec::<ComparisonStateMachine<'a>>::with_capacity(condition.len());

        let mut machine_idx = start_idx;
        for cond in condition {
            let machine = ComparisonStateMachine {
                values: &cond.values,
                logic: &cond.logic,
                metadata: &cond.metadata,
                count: 0,
                ctx_idx: machine_idx,
            };
            machines.push(machine);
            machine_idx += 1;
        }

        Self {
            state_machines: machines,
            start_ctx_idx: start_idx,
        }
    }

    fn init(&self, context: &mut types::ConjunctiveContext<'a>) -> Result<(), StateMachineError> {
        for machine in &self.state_machines {
            machine.push(context)?;
        }
        Ok(())
    }

    #[inline]
    fn destroy(&self, context: &mut types::ConjunctiveContext<'a>) {
        context.truncate(self.start_ctx_idx);
    }

    #[inline]
    fn is_finished(&self) -> bool {
        !self
            .state_machines
            .iter()
            .any(|machine| !machine.is_finished())
    }

    #[inline]
    fn get_next_ctx_idx(&self) -> usize {
        self.start_ctx_idx + self.state_machines.len()
    }

    fn advance(
        &mut self,
        context: &mut types::ConjunctiveContext<'a>,
    ) -> Result<(), StateMachineError> {
        for machine in self.state_machines.iter_mut().rev() {
            if machine.is_finished() {
                machine.reset();
                machine.put(context)?;
            } else {
                machine.advance();
                machine.put(context)?;
                break;
            }
        }
        Ok(())
    }
}

#[derive(Debug)]
struct IfStmtStateMachine<'a> {
    condition_machine: ConditionStateMachine<'a>,
    nested: Vec<&'a dir::DirIfStatement>,
    nested_idx: usize,
}

impl<'a> IfStmtStateMachine<'a> {
    fn new(stmt: &'a dir::DirIfStatement, ctx_start_idx: usize) -> Self {
        let condition_machine = ConditionStateMachine::new(&stmt.condition, ctx_start_idx);
        let nested: Vec<&'a dir::DirIfStatement> = match &stmt.nested {
            None => Vec::new(),
            Some(nested_stmts) => nested_stmts.iter().collect(),
        };

        Self {
            condition_machine,
            nested,
            nested_idx: 0,
        }
    }

    fn init(
        &self,
        context: &mut types::ConjunctiveContext<'a>,
    ) -> Result<Option<Self>, StateMachineError> {
        self.condition_machine.init(context)?;
        Ok(self
            .nested
            .first()
            .map(|nested| Self::new(nested, self.condition_machine.get_next_ctx_idx())))
    }

    #[inline]
    fn is_finished(&self) -> bool {
        self.nested_idx + 1 >= self.nested.len()
    }

    #[inline]
    fn is_condition_machine_finished(&self) -> bool {
        self.condition_machine.is_finished()
    }

    #[inline]
    fn destroy(&self, context: &mut types::ConjunctiveContext<'a>) {
        self.condition_machine.destroy(context);
    }

    #[inline]
    fn advance_condition_machine(
        &mut self,
        context: &mut types::ConjunctiveContext<'a>,
    ) -> Result<(), StateMachineError> {
        self.condition_machine.advance(context)?;
        Ok(())
    }

    fn advance(&mut self) -> Result<Option<Self>, StateMachineError> {
        if self.nested.is_empty() {
            Ok(None)
        } else {
            self.nested_idx = (self.nested_idx + 1) % self.nested.len();
            Ok(Some(Self::new(
                self.nested
                    .get(self.nested_idx)
                    .ok_or(StateMachineError::IndexOutOfBounds(
                        "in IfStmtStateMachine while advancing",
                    ))?,
                self.condition_machine.get_next_ctx_idx(),
            )))
        }
    }
}

#[derive(Debug)]
struct RuleStateMachine<'a> {
    connector_selection_data: &'a [(dir::DirValue, Metadata)],
    connectors_added: bool,
    if_stmt_machines: Vec<IfStmtStateMachine<'a>>,
    running_stack: Vec<IfStmtStateMachine<'a>>,
}

impl<'a> RuleStateMachine<'a> {
    fn new<O>(
        rule: &'a dir::DirRule<O>,
        connector_selection_data: &'a [(dir::DirValue, Metadata)],
    ) -> Self {
        let mut if_stmt_machines: Vec<IfStmtStateMachine<'a>> =
            Vec::with_capacity(rule.statements.len());

        for stmt in rule.statements.iter().rev() {
            if_stmt_machines.push(IfStmtStateMachine::new(
                stmt,
                connector_selection_data.len(),
            ));
        }

        Self {
            connector_selection_data,
            connectors_added: false,
            if_stmt_machines,
            running_stack: Vec::new(),
        }
    }

    fn is_finished(&self) -> bool {
        self.if_stmt_machines.is_empty() && self.running_stack.is_empty()
    }

    fn init_next(
        &mut self,
        context: &mut types::ConjunctiveContext<'a>,
    ) -> Result<(), StateMachineError> {
        if self.if_stmt_machines.is_empty() || !self.running_stack.is_empty() {
            return Ok(());
        }

        if !self.connectors_added {
            for (dir_val, metadata) in self.connector_selection_data {
                context.push(types::ContextValue::assertion(dir_val, metadata));
            }
            self.connectors_added = true;
        }

        context.truncate(self.connector_selection_data.len());

        if let Some(mut next_running) = self.if_stmt_machines.pop() {
            while let Some(nested_running) = next_running.init(context)? {
                self.running_stack.push(next_running);
                next_running = nested_running;
            }

            self.running_stack.push(next_running);
        }

        Ok(())
    }

    fn advance(
        &mut self,
        context: &mut types::ConjunctiveContext<'a>,
    ) -> Result<(), StateMachineError> {
        let mut condition_machines_finished = true;

        for stmt_machine in self.running_stack.iter_mut().rev() {
            if !stmt_machine.is_condition_machine_finished() {
                condition_machines_finished = false;
                stmt_machine.advance_condition_machine(context)?;
                break;
            } else {
                stmt_machine.advance_condition_machine(context)?;
            }
        }

        if !condition_machines_finished {
            return Ok(());
        }

        let mut maybe_next_running: Option<IfStmtStateMachine<'a>> = None;

        while let Some(last) = self.running_stack.last_mut() {
            if !last.is_finished() {
                maybe_next_running = last.advance()?;
                break;
            } else {
                last.destroy(context);
                self.running_stack.pop();
            }
        }

        if let Some(mut next_running) = maybe_next_running {
            while let Some(nested_running) = next_running.init(context)? {
                self.running_stack.push(next_running);
                next_running = nested_running;
            }

            self.running_stack.push(next_running);
        } else {
            self.init_next(context)?;
        }

        Ok(())
    }
}

#[derive(Debug)]
pub struct RuleContextManager<'a> {
    context: types::ConjunctiveContext<'a>,
    machine: RuleStateMachine<'a>,
    init: bool,
}

impl<'a> RuleContextManager<'a> {
    pub fn new<O>(
        rule: &'a dir::DirRule<O>,
        connector_selection_data: &'a [(dir::DirValue, Metadata)],
    ) -> Self {
        Self {
            context: Vec::new(),
            machine: RuleStateMachine::new(rule, connector_selection_data),
            init: false,
        }
    }

    pub fn advance(&mut self) -> Result<Option<&types::ConjunctiveContext<'a>>, StateMachineError> {
        if !self.init {
            self.init = true;
            self.machine.init_next(&mut self.context)?;
            Ok(Some(&self.context))
        } else if self.machine.is_finished() {
            Ok(None)
        } else {
            self.machine.advance(&mut self.context)?;

            if self.machine.is_finished() {
                Ok(None)
            } else {
                Ok(Some(&self.context))
            }
        }
    }

    pub fn advance_mut(
        &mut self,
    ) -> Result<Option<&mut types::ConjunctiveContext<'a>>, StateMachineError> {
        if !self.init {
            self.init = true;
            self.machine.init_next(&mut self.context)?;
            Ok(Some(&mut self.context))
        } else if self.machine.is_finished() {
            Ok(None)
        } else {
            self.machine.advance(&mut self.context)?;

            if self.machine.is_finished() {
                Ok(None)
            } else {
                Ok(Some(&mut self.context))
            }
        }
    }
}

#[derive(Debug)]
pub struct ProgramStateMachine<'a> {
    rule_machines: Vec<RuleStateMachine<'a>>,
    current_rule_machine: Option<RuleStateMachine<'a>>,
    is_init: bool,
}

impl<'a> ProgramStateMachine<'a> {
    pub fn new<O>(
        program: &'a dir::DirProgram<O>,
        connector_selection_data: &'a [Vec<(dir::DirValue, Metadata)>],
    ) -> Self {
        let mut rule_machines: Vec<RuleStateMachine<'a>> = program
            .rules
            .iter()
            .zip(connector_selection_data.iter())
            .rev()
            .map(|(rule, connector_selection_data)| {
                RuleStateMachine::new(rule, connector_selection_data)
            })
            .collect();

        Self {
            current_rule_machine: rule_machines.pop(),
            rule_machines,
            is_init: false,
        }
    }

    pub fn is_finished(&self) -> bool {
        self.current_rule_machine
            .as_ref()
            .is_none_or(|rsm| rsm.is_finished())
            && self.rule_machines.is_empty()
    }

    pub fn init(
        &mut self,
        context: &mut types::ConjunctiveContext<'a>,
    ) -> Result<(), StateMachineError> {
        if !self.is_init {
            if let Some(rsm) = self.current_rule_machine.as_mut() {
                rsm.init_next(context)?;
            }
            self.is_init = true;
        }

        Ok(())
    }

    pub fn advance(
        &mut self,
        context: &mut types::ConjunctiveContext<'a>,
    ) -> Result<(), StateMachineError> {
        if self
            .current_rule_machine
            .as_ref()
            .is_none_or(|rsm| rsm.is_finished())
        {
            self.current_rule_machine = self.rule_machines.pop();
            context.clear();
            if let Some(rsm) = self.current_rule_machine.as_mut() {
                rsm.init_next(context)?;
            }
        } else if let Some(rsm) = self.current_rule_machine.as_mut() {
            rsm.advance(context)?;
        }

        Ok(())
    }
}

pub struct AnalysisContextManager<'a> {
    context: types::ConjunctiveContext<'a>,
    machine: ProgramStateMachine<'a>,
    init: bool,
}

impl<'a> AnalysisContextManager<'a> {
    pub fn new<O>(
        program: &'a dir::DirProgram<O>,
        connector_selection_data: &'a [Vec<(dir::DirValue, Metadata)>],
    ) -> Self {
        let machine = ProgramStateMachine::new(program, connector_selection_data);
        let context: types::ConjunctiveContext<'a> = Vec::new();

        Self {
            context,
            machine,
            init: false,
        }
    }

    pub fn advance(&mut self) -> Result<Option<&types::ConjunctiveContext<'a>>, StateMachineError> {
        if !self.init {
            self.init = true;
            self.machine.init(&mut self.context)?;
            Ok(Some(&self.context))
        } else if self.machine.is_finished() {
            Ok(None)
        } else {
            self.machine.advance(&mut self.context)?;

            if self.machine.is_finished() {
                Ok(None)
            } else {
                Ok(Some(&self.context))
            }
        }
    }
}

pub fn make_connector_selection_data<O: EuclidAnalysable>(
    program: &dir::DirProgram<O>,
) -> Vec<Vec<(dir::DirValue, Metadata)>> {
    program
        .rules
        .iter()
        .map(|rule| {
            rule.connector_selection
                .get_dir_value_for_analysis(rule.name.clone())
        })
        .collect()
}

#[cfg(all(test, feature = "ast_parser"))]
mod tests {
    use super::*;
    use crate::{dirval, frontend::ast, types::DummyOutput};

    #[test]
    fn test_correct_contexts() {
        let program_str = r#"
            default: ["stripe", "adyen"]

            stripe_first: ["stripe", "adyen"]
            {
                payment_method = wallet {
                    payment_method = (card, bank_redirect) {
                        currency = USD
                        currency = GBP
                    }

                    payment_method = pay_later {
                        capture_method = automatic
                        capture_method = manual
                    }
                }

                payment_method = card {
                    payment_method = (card, bank_redirect) & capture_method = (automatic, manual) {
                        currency = (USD, GBP)
                    }
                }
            }
        "#;
        let (_, program) = ast::parser::program::<DummyOutput>(program_str).expect("Program");
        let lowered = ast::lowering::lower_program(program).expect("Lowering");

        let selection_data = make_connector_selection_data(&lowered);
        let mut state_machine = ProgramStateMachine::new(&lowered, &selection_data);
        let mut ctx: types::ConjunctiveContext<'_> = Vec::new();
        state_machine.init(&mut ctx).expect("State machine init");

        let expected_contexts: Vec<Vec<dir::DirValue>> = vec![
            vec![
                dirval!("MetadataKey" = "stripe"),
                dirval!("MetadataKey" = "adyen"),
                dirval!(PaymentMethod = Wallet),
                dirval!(PaymentMethod = Card),
                dirval!(PaymentCurrency = USD),
            ],
            vec![
                dirval!("MetadataKey" = "stripe"),
                dirval!("MetadataKey" = "adyen"),
                dirval!(PaymentMethod = Wallet),
                dirval!(PaymentMethod = BankRedirect),
                dirval!(PaymentCurrency = USD),
            ],
            vec![
                dirval!("MetadataKey" = "stripe"),
                dirval!("MetadataKey" = "adyen"),
                dirval!(PaymentMethod = Wallet),
                dirval!(PaymentMethod = Card),
                dirval!(PaymentCurrency = GBP),
            ],
            vec![
                dirval!("MetadataKey" = "stripe"),
                dirval!("MetadataKey" = "adyen"),
                dirval!(PaymentMethod = Wallet),
                dirval!(PaymentMethod = BankRedirect),
                dirval!(PaymentCurrency = GBP),
            ],
            vec![
                dirval!("MetadataKey" = "stripe"),
                dirval!("MetadataKey" = "adyen"),
                dirval!(PaymentMethod = Wallet),
                dirval!(PaymentMethod = PayLater),
                dirval!(CaptureMethod = Automatic),
            ],
            vec![
                dirval!("MetadataKey" = "stripe"),
                dirval!("MetadataKey" = "adyen"),
                dirval!(PaymentMethod = Wallet),
                dirval!(PaymentMethod = PayLater),
                dirval!(CaptureMethod = Manual),
            ],
            vec![
                dirval!("MetadataKey" = "stripe"),
                dirval!("MetadataKey" = "adyen"),
                dirval!(PaymentMethod = Card),
                dirval!(PaymentMethod = Card),
                dirval!(CaptureMethod = Automatic),
                dirval!(PaymentCurrency = USD),
            ],
            vec![
                dirval!("MetadataKey" = "stripe"),
                dirval!("MetadataKey" = "adyen"),
                dirval!(PaymentMethod = Card),
                dirval!(PaymentMethod = Card),
                dirval!(CaptureMethod = Automatic),
                dirval!(PaymentCurrency = GBP),
            ],
            vec![
                dirval!("MetadataKey" = "stripe"),
                dirval!("MetadataKey" = "adyen"),
                dirval!(PaymentMethod = Card),
                dirval!(PaymentMethod = Card),
                dirval!(CaptureMethod = Manual),
                dirval!(PaymentCurrency = USD),
            ],
            vec![
                dirval!("MetadataKey" = "stripe"),
                dirval!("MetadataKey" = "adyen"),
                dirval!(PaymentMethod = Card),
                dirval!(PaymentMethod = Card),
                dirval!(CaptureMethod = Manual),
                dirval!(PaymentCurrency = GBP),
            ],
            vec![
                dirval!("MetadataKey" = "stripe"),
                dirval!("MetadataKey" = "adyen"),
                dirval!(PaymentMethod = Card),
                dirval!(PaymentMethod = BankRedirect),
                dirval!(CaptureMethod = Automatic),
                dirval!(PaymentCurrency = USD),
            ],
            vec![
                dirval!("MetadataKey" = "stripe"),
                dirval!("MetadataKey" = "adyen"),
                dirval!(PaymentMethod = Card),
                dirval!(PaymentMethod = BankRedirect),
                dirval!(CaptureMethod = Automatic),
                dirval!(PaymentCurrency = GBP),
            ],
            vec![
                dirval!("MetadataKey" = "stripe"),
                dirval!("MetadataKey" = "adyen"),
                dirval!(PaymentMethod = Card),
                dirval!(PaymentMethod = BankRedirect),
                dirval!(CaptureMethod = Manual),
                dirval!(PaymentCurrency = USD),
            ],
            vec![
                dirval!("MetadataKey" = "stripe"),
                dirval!("MetadataKey" = "adyen"),
                dirval!(PaymentMethod = Card),
                dirval!(PaymentMethod = BankRedirect),
                dirval!(CaptureMethod = Manual),
                dirval!(PaymentCurrency = GBP),
            ],
        ];

        let mut expected_idx = 0usize;
        while !state_machine.is_finished() {
            let values = ctx
                .iter()
                .flat_map(|c| match c.value {
                    types::CtxValueKind::Assertion(val) => vec![val],
                    types::CtxValueKind::Negation(vals) => vals.iter().collect(),
                })
                .collect::<Vec<&dir::DirValue>>();
            assert_eq!(
                values,
                expected_contexts
                    .get(expected_idx)
                    .expect("Error deriving contexts")
                    .iter()
                    .collect::<Vec<&dir::DirValue>>()
            );
            expected_idx += 1;
            state_machine
                .advance(&mut ctx)
                .expect("State Machine advance");
        }

        assert_eq!(expected_idx, 14);

        let mut ctx_manager = AnalysisContextManager::new(&lowered, &selection_data);
        expected_idx = 0;
        while let Some(ctx) = ctx_manager.advance().expect("Context Manager Context") {
            let values = ctx
                .iter()
                .flat_map(|c| match c.value {
                    types::CtxValueKind::Assertion(val) => vec![val],
                    types::CtxValueKind::Negation(vals) => vals.iter().collect(),
                })
                .collect::<Vec<&dir::DirValue>>();
            assert_eq!(
                values,
                expected_contexts
                    .get(expected_idx)
                    .expect("Error deriving contexts")
                    .iter()
                    .collect::<Vec<&dir::DirValue>>()
            );
            expected_idx += 1;
        }

        assert_eq!(expected_idx, 14);
    }
}


// File: crates/euclid/src/dssa/types.rs

use std::{collections::HashMap, fmt};

use serde::Serialize;

use crate::{
    dssa::{self, graph},
    frontend::{ast, dir},
    types::{DataType, EuclidValue, Metadata},
};

pub trait EuclidAnalysable: Sized {
    fn get_dir_value_for_analysis(&self, rule_name: String) -> Vec<(dir::DirValue, Metadata)>;
}

#[derive(Debug, Clone)]
pub enum CtxValueKind<'a> {
    Assertion(&'a dir::DirValue),
    Negation(&'a [dir::DirValue]),
}

impl CtxValueKind<'_> {
    pub fn get_assertion(&self) -> Option<&dir::DirValue> {
        if let Self::Assertion(val) = self {
            Some(val)
        } else {
            None
        }
    }

    pub fn get_negation(&self) -> Option<&[dir::DirValue]> {
        if let Self::Negation(vals) = self {
            Some(vals)
        } else {
            None
        }
    }

    pub fn get_key(&self) -> Option<dir::DirKey> {
        match self {
            Self::Assertion(val) => Some(val.get_key()),
            Self::Negation(vals) => vals.first().map(|v| (*v).get_key()),
        }
    }
}

#[derive(Debug, Clone)]
pub struct ContextValue<'a> {
    pub value: CtxValueKind<'a>,
    pub metadata: &'a Metadata,
}

impl<'a> ContextValue<'a> {
    #[inline]
    pub fn assertion(value: &'a dir::DirValue, metadata: &'a Metadata) -> Self {
        Self {
            value: CtxValueKind::Assertion(value),
            metadata,
        }
    }

    #[inline]
    pub fn negation(values: &'a [dir::DirValue], metadata: &'a Metadata) -> Self {
        Self {
            value: CtxValueKind::Negation(values),
            metadata,
        }
    }
}

pub type ConjunctiveContext<'a> = Vec<ContextValue<'a>>;

#[derive(Clone, Serialize)]
pub enum AnalyzeResult {
    AllOk,
}

#[derive(Debug, Clone, Serialize, thiserror::Error)]
pub struct AnalysisError {
    #[serde(flatten)]
    pub error_type: AnalysisErrorType,
    pub metadata: Metadata,
}
impl fmt::Display for AnalysisError {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        self.error_type.fmt(f)
    }
}
#[derive(Debug, Clone, Serialize)]
pub struct ValueData {
    pub value: dir::DirValue,
    pub metadata: Metadata,
}

#[derive(Debug, Clone, Serialize, thiserror::Error)]
#[serde(tag = "type", content = "info", rename_all = "snake_case")]
pub enum AnalysisErrorType {
    #[error("Invalid program key given: '{0}'")]
    InvalidKey(String),
    #[error("Invalid variant '{got}' received for key '{key}'")]
    InvalidVariant {
        key: String,
        expected: Vec<String>,
        got: String,
    },
    #[error(
        "Invalid data type for value '{}' (expected {expected}, got {got})",
        key
    )]
    InvalidType {
        key: String,
        expected: DataType,
        got: DataType,
    },
    #[error("Invalid comparison '{operator:?}' for value type {value_type}")]
    InvalidComparison {
        operator: ast::ComparisonType,
        value_type: DataType,
    },
    #[error("Invalid value received for length as '{value}: {:?}'", message)]
    InvalidValue {
        key: dir::DirKeyKind,
        value: String,
        message: Option<String>,
    },
    #[error("Conflicting assertions received for key '{}'", .key.kind)]
    ConflictingAssertions {
        key: dir::DirKey,
        values: Vec<ValueData>,
    },

    #[error("Key '{}' exhaustively negated", .key.kind)]
    ExhaustiveNegation {
        key: dir::DirKey,
        metadata: Vec<Metadata>,
    },
    #[error("The condition '{value}' was asserted and negated in the same condition")]
    NegatedAssertion {
        value: dir::DirValue,
        assertion_metadata: Metadata,
        negation_metadata: Metadata,
    },
    #[error("Graph analysis error: {0:#?}")]
    GraphAnalysis(
        graph::AnalysisError<dir::DirValue>,
        hyperswitch_constraint_graph::Memoization<dir::DirValue>,
    ),
    #[error("State machine error")]
    StateMachine(dssa::state_machine::StateMachineError),
    #[error("Unsupported program key '{0}'")]
    UnsupportedProgramKey(dir::DirKeyKind),
    #[error("Ran into an unimplemented feature")]
    NotImplemented,
    #[error("The payment method type is not supported under the payment method")]
    NotSupported,
}

#[derive(Debug, Clone)]
pub enum ValueType {
    EnumVariants(Vec<EuclidValue>),
    Number,
}

impl EuclidAnalysable for common_enums::AuthenticationType {
    fn get_dir_value_for_analysis(&self, rule_name: String) -> Vec<(dir::DirValue, Metadata)> {
        let auth = self.to_string();

        let dir_value = match self {
            Self::ThreeDs => dir::DirValue::AuthenticationType(Self::ThreeDs),
            Self::NoThreeDs => dir::DirValue::AuthenticationType(Self::NoThreeDs),
        };

        vec![(
            dir_value,
            HashMap::from_iter([(
                "AUTHENTICATION_TYPE".to_string(),
                serde_json::json!({
                    "rule_name": rule_name,
                    "Authentication_type": auth,
                }),
            )]),
        )]
    }
}