use std::{collections::HashMap, sync::LazyLock};

use super::Val;

pub(crate) type LogicalPredType = fn(Val, Val) -> bool;

pub(crate) struct LogicalPred;

impl LogicalPred {
    const LOGICAL_PRED_MAP: LazyLock<HashMap<&'static str, LogicalPredType>> =
        LazyLock::new(|| HashMap::from([("-and", and as _), ("-or", or as _), ("-xor", xor as _)]));

    pub(crate) fn get(name: &str) -> Option<LogicalPredType> {
        Self::LOGICAL_PRED_MAP
            .get(name.to_ascii_lowercase().as_str())
            .copied()
    }
}

pub fn and(a: Val, b: Val) -> bool {
    a.cast_to_bool() && b.cast_to_bool()
}

pub fn or(a: Val, b: Val) -> bool {
    a.cast_to_bool() || b.cast_to_bool()
}

pub fn xor(a: Val, b: Val) -> bool {
    a.cast_to_bool() != b.cast_to_bool()
}

#[cfg(test)]
mod tests {
    use crate::PowerShellSession;

    #[test]
    fn test_and() {
        let mut p = PowerShellSession::new();
        assert_eq!(
            p.safe_eval(r#" $true -AND $true "#).unwrap(),
            "True".to_string()
        );
        assert_eq!(
            p.safe_eval(r#" $true -And $false "#).unwrap(),
            "False".to_string()
        );
        assert_eq!(
            p.safe_eval(r#" $false -and $true "#).unwrap(),
            "False".to_string()
        );
        assert_eq!(
            p.safe_eval(r#" $false -and $false "#).unwrap(),
            "False".to_string()
        );

        assert_eq!(
            p.safe_eval(r#" $true -and $false -and $true "#).unwrap(),
            "False".to_string()
        );
    }

    #[test]
    fn test_or() {
        let mut p = PowerShellSession::new();
        assert_eq!(
            p.safe_eval(r#" $true -oR $true "#).unwrap(),
            "True".to_string()
        );
        assert_eq!(
            p.safe_eval(r#" $true -or $false "#).unwrap(),
            "True".to_string()
        );
        assert_eq!(
            p.safe_eval(r#" $false -or $true "#).unwrap(),
            "True".to_string()
        );
        assert_eq!(
            p.safe_eval(r#" $false -or $false "#).unwrap(),
            "False".to_string()
        );

        assert_eq!(
            p.safe_eval(r#" $true -Or $false -or $true "#).unwrap(),
            "True".to_string()
        );
    }

    #[test]
    fn test_xor() {
        let mut p = PowerShellSession::new();
        assert_eq!(
            p.safe_eval(r#" $true -Xor $true "#).unwrap(),
            "False".to_string()
        );
        assert_eq!(
            p.safe_eval(r#" $true -xOr $false "#).unwrap(),
            "True".to_string()
        );
        assert_eq!(
            p.safe_eval(r#" $false -XOR $true "#).unwrap(),
            "True".to_string()
        );
        assert_eq!(
            p.safe_eval(r#" $false -xor $false "#).unwrap(),
            "False".to_string()
        );
    }

    #[test]
    fn test_not() {
        let mut p = PowerShellSession::new();
        assert_eq!(p.safe_eval(r#" -Not 4 "#).unwrap(), "False".to_string());
        assert_eq!(p.safe_eval(r#" -nOt "" "#).unwrap(), "True".to_string());
        assert_eq!(p.safe_eval(r#" -not "asd" "#).unwrap(), "False".to_string());
        assert_eq!(
            p.safe_eval(r#" -nOt "96.5" "#).unwrap(),
            "False".to_string()
        );
        assert_eq!(
            p.safe_eval(r#" -Not "+96.5" "#).unwrap(),
            "False".to_string()
        );
        assert_eq!(
            p.safe_eval(r#" -NOT "96.5as" "#).unwrap(),
            "False".to_string()
        );
    }
}
