// filepath: crates/frisk_core/src/otel/effect_reason.rs
use crate::policy_types::ConditionEvaluationResult;
use crate::telemetry_constants as telemetry;

// Returns (effect, reason)
pub fn effect_reason_for_allow(result: &ConditionEvaluationResult) -> (&'static str, String) {
    match result {
        ConditionEvaluationResult::Pass => (
            telemetry::EFFECT_ALLOW,
            telemetry::EFFECT_REASON_ALLOW_MATCHED.to_string(),
        ),
        ConditionEvaluationResult::Fail => (
            telemetry::EFFECT_DENY,
            telemetry::EFFECT_REASON_ALLOW_MISMATCHED.to_string(),
        ),
        ConditionEvaluationResult::Unknown { reason } => (
            telemetry::EFFECT_UNKNOWN,
            format!("{} {}", telemetry::EVAL_FAIL_ALLOW_PREFIX, reason),
        ),
    }
}

pub fn effect_reason_for_deny(result: &ConditionEvaluationResult) -> (&'static str, String) {
    match result {
        ConditionEvaluationResult::Pass => (
            telemetry::EFFECT_DENY,
            telemetry::EFFECT_REASON_DENY_MATCHED.to_string(),
        ),
        ConditionEvaluationResult::Fail => (
            telemetry::EFFECT_ALLOW,
            telemetry::EFFECT_REASON_DENY_MISMATCHED.to_string(),
        ),
        ConditionEvaluationResult::Unknown { reason } => (
            telemetry::EFFECT_UNKNOWN,
            format!("{} {}", telemetry::EVAL_FAIL_DENY_PREFIX, reason),
        ),
    }
}

pub fn effect_reason_for_modify(result: &ConditionEvaluationResult) -> (&'static str, String) {
    match result {
        ConditionEvaluationResult::Pass => (telemetry::EFFECT_MODIFY, "".to_string()),
        ConditionEvaluationResult::Fail => (telemetry::EFFECT_NONE, "".to_string()),
        ConditionEvaluationResult::Unknown { reason } => (
            telemetry::EFFECT_UNKNOWN,
            format!("{} {}", telemetry::EVAL_FAIL_MODIFY_PREFIX, reason),
        ),
    }
}

#[cfg(test)]
mod tests {
    use crate::errors::PolicyEngineError::MissingActualValueAtPath;
    use crate::otel::effect_reason::{
        effect_reason_for_allow, effect_reason_for_deny, effect_reason_for_modify,
    };
    use crate::policy_types::ConditionEvaluationResult;
    use crate::telemetry_constants;

    #[test]
    fn effect_reason_mapping_allow() {
        assert_eq!(
            effect_reason_for_allow(&ConditionEvaluationResult::Pass),
            (
                telemetry_constants::EFFECT_ALLOW,
                telemetry_constants::EFFECT_REASON_ALLOW_MATCHED.to_string()
            )
        );
        assert_eq!(
            effect_reason_for_allow(&ConditionEvaluationResult::Fail),
            (
                telemetry_constants::EFFECT_DENY,
                telemetry_constants::EFFECT_REASON_ALLOW_MISMATCHED.to_string()
            )
        );
        let (effect, reason) = effect_reason_for_allow(&ConditionEvaluationResult::Unknown {
            reason: MissingActualValueAtPath {
                source_tag: "state".into(),
                path: "path".into(),
            },
        });
        assert_eq!(effect, telemetry_constants::EFFECT_UNKNOWN);
        assert!(reason.starts_with(telemetry_constants::EVAL_FAIL_ALLOW_PREFIX));
    }

    #[test]
    fn effect_reason_mapping_deny() {
        assert_eq!(
            effect_reason_for_deny(&ConditionEvaluationResult::Pass),
            (
                telemetry_constants::EFFECT_DENY,
                telemetry_constants::EFFECT_REASON_DENY_MATCHED.to_string()
            )
        );
        assert_eq!(
            effect_reason_for_deny(&ConditionEvaluationResult::Fail),
            (
                telemetry_constants::EFFECT_ALLOW,
                telemetry_constants::EFFECT_REASON_DENY_MISMATCHED.to_string()
            )
        );
        let (effect, reason) = effect_reason_for_deny(&ConditionEvaluationResult::Unknown {
            reason: MissingActualValueAtPath {
                source_tag: "args".into(),
                path: "path".into(),
            },
        });
        assert_eq!(effect, telemetry_constants::EFFECT_UNKNOWN);
        assert!(reason.starts_with(telemetry_constants::EVAL_FAIL_DENY_PREFIX));
    }

    #[test]
    fn effect_reason_mapping_modify() {
        assert_eq!(
            effect_reason_for_modify(&ConditionEvaluationResult::Pass),
            (telemetry_constants::EFFECT_MODIFY, "".to_string())
        );
        assert_eq!(
            effect_reason_for_modify(&ConditionEvaluationResult::Fail),
            (telemetry_constants::EFFECT_NONE, "".to_string())
        );
        let (effect, reason) = effect_reason_for_modify(&ConditionEvaluationResult::Unknown {
            reason: MissingActualValueAtPath {
                source_tag: "state".into(),
                path: "path".into(),
            },
        });
        assert_eq!(effect, telemetry_constants::EFFECT_UNKNOWN);
        assert!(reason.starts_with(telemetry_constants::EVAL_FAIL_MODIFY_PREFIX));
    }
}
