mod modifier;
mod transformation;
mod value;
pub use modifier::*;
pub use value::*;
use crate::error::ParserError;
use crate::error::ParserError::{IPParsing, InvalidYAML};
use crate::event::{Event, EventValue};
use crate::field::transformation::{encode_base64, encode_base64_offset, windash_variations};
use crate::field::ValueTransformer::{Base64, Base64offset, Windash};
use cidr::IpCidr;
use regex::Regex;
use serde_yml::Value;
use std::str::FromStr;
#[derive(Debug)]
pub struct Field {
    pub name: String,
    pub values: Vec<FieldValue>,
    pub(crate) modifier: Modifier,
}
macro_rules! conditional_lowercase {
    ($value:expr, $cased:expr) => {
        if $cased {
            $value
        } else if let FieldValue::String(s) = $value {
            &FieldValue::String(s.to_lowercase())
        } else {
            $value
        }
    };
}
impl FromStr for Field {
    type Err = ParserError;
    fn from_str(s: &str) -> Result<Self, Self::Err> {
        let result = Self {
            name: s.split("|").next().unwrap_or("").to_string(),
            values: vec![],
            modifier: Modifier::from_str(s)?,
        };
        Ok(result)
    }
}
impl Field {
    pub(crate) fn new<S: AsRef<str>>(
        name_with_modifiers: S,
        values: Vec<FieldValue>,
    ) -> Result<Field, ParserError> {
        match Self::from_str(name_with_modifiers.as_ref()) {
            Ok(mut field) => {
                field.values = values;
                match field.bootstrap() {
                    Ok(_) => Ok(field),
                    Err(err) => Err(err),
                }
            }
            Err(err) => Err(err),
        }
    }
    pub(crate) fn from_yaml<S: AsRef<str>>(name: S, value: Value) -> Result<Field, ParserError> {
        let field_values = match value {
            Value::Bool(_) | Value::Number(_) | Value::String(_) | Value::Null => {
                vec![FieldValue::try_from(value)?]
            }
            Value::Sequence(seq) => {
                let mut result = Vec::with_capacity(seq.len());
                for item in seq {
                    result.push(FieldValue::try_from(item)?);
                }
                result
            }
            _ => return Err(InvalidYAML(format!("{:?}", value))),
        };
        Self::new(name, field_values)
    }
    fn bootstrap(&mut self) -> Result<(), ParserError> {
        if self.values.is_empty() {
            return Err(ParserError::EmptyValues(self.name.to_string()));
        }
        if self.modifier.exists.is_some() {
            if self.values.len() != 1 {
                return Err(ParserError::InvalidValueForExists());
            }
            if let FieldValue::Boolean(b) = self.values[0] {
                self.modifier.exists = Some(b);
            } else {
                return Err(ParserError::InvalidValueForExists());
            }
        }
        match self.modifier.match_modifier {
            Some(MatchModifier::Contains)
            | Some(MatchModifier::StartsWith)
            | Some(MatchModifier::EndsWith) => {
                for v in self.values.iter() {
                    match v {
                        FieldValue::String(_) => {}
                        _ => {
                            return Err(ParserError::InvalidValueForStringModifier(format!(
                                "{:?}",
                                v
                            )))
                        }
                    }
                }
            }
            Some(MatchModifier::Cidr) => {
                for i in 0..self.values.len() {
                    let val_str = self.values[i].value_to_string();
                    match IpCidr::from_str(val_str.as_str()) {
                        Ok(ip) => self.values[i] = FieldValue::Cidr(ip),
                        Err(err) => return Err(IPParsing(val_str, err.to_string())),
                    }
                }
            }
            Some(MatchModifier::Re) => {
                for i in 0..self.values.len() {
                    match Regex::new(self.values[i].value_to_string().as_str()) {
                        Ok(re) => self.values[i] = FieldValue::Regex(re),
                        Err(err) => return Err(ParserError::RegexParsing(err)),
                    }
                }
            }
            _ => {}
        }
        match &self.modifier.value_transformer {
            Some(Base64(utf16)) => {
                self.values = self
                    .values
                    .iter()
                    .map(|val| FieldValue::String(encode_base64(val, utf16)))
                    .collect();
            }
            Some(Base64offset(utf16)) => {
                self.values = self
                    .values
                    .iter()
                    .flat_map(|val| encode_base64_offset(val, utf16))
                    .map(FieldValue::String)
                    .collect();
            }
            Some(Windash) => {
                self.values = self
                    .values
                    .iter()
                    .flat_map(windash_variations)
                    .map(FieldValue::String)
                    .collect();
            }
            None => {}
        }
        Ok(())
    }
    pub(crate) fn compare(&self, target: &FieldValue, value: &FieldValue) -> bool {
        match self.modifier.match_modifier {
            Some(MatchModifier::Contains) => target.contains(value),
            Some(MatchModifier::StartsWith) => target.starts_with(value),
            Some(MatchModifier::EndsWith) => target.ends_with(value),
            Some(MatchModifier::Gt) => target > value,
            Some(MatchModifier::Gte) => target >= value,
            Some(MatchModifier::Lt) => target < value,
            Some(MatchModifier::Lte) => target <= value,
            Some(MatchModifier::Re) => value.is_regex_match(target.value_to_string().as_str()),
            Some(MatchModifier::Cidr) => value.cidr_contains(target),
            None => value == target,
        }
    }
    pub(crate) fn evaluate(&self, event: &Event) -> bool {
        let Some(event_value) = event.get(&self.name) else {
            return matches!(self.modifier.exists, Some(false));
        };
        if matches!(self.modifier.exists, Some(true)) {
            return true;
        };
        let EventValue::Value(target) = event_value else {
            return false;
        };
        if self.values.is_empty() {
            return true;
        }
        let target = conditional_lowercase!(target, self.modifier.cased);
        for val in self.values.iter() {
            let cmp = if self.modifier.fieldref {
                if let Some(EventValue::Value(value)) = event.get(val.value_to_string().as_str()) {
                    conditional_lowercase!(value, self.modifier.cased)
                } else {
                    continue;
                }
            } else {
                conditional_lowercase!(val, self.modifier.cased)
            };
            let fired = self.compare(target, cmp);
            if fired && !self.modifier.match_all {
                return true;
            } else if !fired && self.modifier.match_all {
                return false;
            }
        }
        self.modifier.match_all
    }
}
#[cfg(test)]
mod tests {
    use super::*;
    #[test]
    fn test_parse_name_only() {
        let field = Field::from_str("a").unwrap();
        assert_eq!(field.name, "a");
        assert!(field.modifier.match_modifier.is_none());
        assert!(field.modifier.value_transformer.is_none());
        assert!(!field.modifier.match_all);
    }
    #[test]
    fn test_parse_contains_modifier() {
        let field = Field::from_str("hello|contains").unwrap();
        assert_eq!(field.name, "hello");
        assert_eq!(
            field.modifier.match_modifier.unwrap(),
            MatchModifier::Contains
        );
        assert!(field.modifier.value_transformer.is_none());
        assert!(!field.modifier.match_all);
    }
    #[test]
    fn test_parse_value_transformer_modifier() {
        let field = Field::from_str("hello|windash|contains").unwrap();
        assert_eq!(field.name, "hello");
        assert_eq!(field.modifier.match_modifier, Some(MatchModifier::Contains));
        assert_eq!(field.modifier.value_transformer, Some(Windash));
    }
    #[test]
    fn test_parse_base64_modifier() {
        let field = Field::from_str("hello|base64|endswith").unwrap();
        assert_eq!(field.name, "hello");
        assert_eq!(field.modifier.match_modifier, Some(MatchModifier::EndsWith));
        assert_eq!(field.modifier.value_transformer, Some(Base64(None)));
    }
    #[test]
    fn test_parse_utf16_modifier() {
        let field = Field::from_str("hello|base64offset|utf16le|endswith").unwrap();
        assert_eq!(field.name, "hello");
        assert_eq!(field.modifier.match_modifier, Some(MatchModifier::EndsWith));
        assert_eq!(
            field.modifier.value_transformer,
            Some(Base64offset(Some(Utf16Modifier::Utf16le)))
        );
    }
    #[test]
    fn test_evaluate_equals() {
        let field = Field::new(
            "test",
            vec![
                FieldValue::from("zsh"),
                FieldValue::from("BASH"),
                FieldValue::from("pwsh"),
            ],
        )
        .unwrap();
        let event_no_match = Event::from([("test", "zsh shutdown")]);
        assert!(!field.evaluate(&event_no_match));
        let matching_event = Event::from([("test", "bash")]);
        assert!(field.evaluate(&matching_event));
    }
    #[test]
    fn test_evaluate_equals_cased() {
        let field = Field::new("test|cased", vec![FieldValue::from("bash")]).unwrap();
        let event_no_match = Event::from([("test", "BASH")]);
        assert!(!field.evaluate(&event_no_match));
        let matching_event = Event::from([("test", "bash")]);
        assert!(field.evaluate(&matching_event));
    }
    #[test]
    fn test_evaluate_startswith() {
        let mut field = Field::new(
            "test|startswith",
            vec![
                FieldValue::from("zsh"),
                FieldValue::from("bash"),
                FieldValue::from("pwsh"),
            ],
        )
        .unwrap();
        let event = Event::from([("test", "zsh shutdown")]);
        assert!(field.evaluate(&event));
        field.modifier.match_all = true;
        assert!(!field.evaluate(&event));
    }
    #[test]
    fn test_evaluate_endswith() {
        let field = Field::new(
            "test|endswith",
            vec![FieldValue::from("h"), FieldValue::from("sh")],
        )
        .unwrap();
        let event = Event::from([("test", "zsh")]);
        assert!(field.evaluate(&event));
        let field = Field::new(
            "test|endswith|all",
            vec![FieldValue::from("h"), FieldValue::from("sh")],
        )
        .unwrap();
        assert!(field.evaluate(&event));
    }
    #[test]
    fn test_evaluate_contains() {
        let field = Field::new(
            "test|contains",
            vec![FieldValue::from("zsh"), FieldValue::from("python2")],
        )
        .unwrap();
        let event = Event::from([("test", "zsh python3 -c os.remove('/')")]);
        assert!(field.evaluate(&event));
        let field = Field::new(
            "test|contains|all",
            vec![FieldValue::from("zsh"), FieldValue::from("python2")],
        )
        .unwrap();
        assert!(!field.evaluate(&event));
    }
    #[test]
    fn test_evaluate_lt() {
        let mut field =
            Field::new("test|lt", vec![FieldValue::Int(10), FieldValue::Int(15)]).unwrap();
        let event = Event::from([("test", 10)]);
        assert!(field.evaluate(&event));
        field.modifier.match_all = true;
        assert!(!field.evaluate(&event));
    }
    #[test]
    fn test_evaluate_lte() {
        let mut field =
            Field::new("test|lte", vec![FieldValue::Int(15), FieldValue::Int(20)]).unwrap();
        let event = Event::from([("test", 15)]);
        assert!(field.evaluate(&event));
        field.modifier.match_all = true;
        assert!(field.evaluate(&event));
    }
    #[test]
    fn test_evaluate_gt() {
        let mut field = Field::new("test|gt", vec![FieldValue::Float(10.1)]).unwrap();
        let event = Event::from([("test", 10.2)]);
        assert!(field.evaluate(&event));
        field.modifier.match_all = true;
        assert!(field.evaluate(&event));
    }
    #[test]
    fn test_evaluate_gte() {
        let mut field =
            Field::new("test|gte", vec![FieldValue::Int(15), FieldValue::Int(10)]).unwrap();
        let event = Event::from([("test", 15)]);
        assert!(field.evaluate(&event));
        field.modifier.match_all = true;
        assert!(field.evaluate(&event));
        field.modifier.match_all = false;
        let event = Event::from([("test", 14.0)]);
        assert!(!field.evaluate(&event));
        field.values.push(FieldValue::Float(12.34));
        assert!(field.evaluate(&event));
        field.modifier.match_all = true;
        assert!(!field.evaluate(&event));
    }
    #[test]
    fn test_evaluate_regex() {
        let mut field = Field::new(
            "test|re",
            vec![
                FieldValue::from(r"hello (.*)d"),
                FieldValue::from(r"goodbye (.*)"),
            ],
        )
        .unwrap();
        for val in &field.values {
            assert!(matches!(val, FieldValue::Regex(_)));
        }
        let event = Event::from([("test", "hello world")]);
        assert!(field.evaluate(&event));
        field.modifier.match_all = true;
        assert!(!field.evaluate(&event));
    }
    #[test]
    fn test_compare() {
        let mut field = Field {
            name: "test".to_string(),
            values: vec![],
            modifier: Modifier::default(),
        };
        assert!(field.compare(&FieldValue::from("zsh"), &FieldValue::from("zsh")));
        assert!(!field.compare(&FieldValue::from("zsh"), &FieldValue::from("bash")));
        field.modifier.match_modifier = Some(MatchModifier::StartsWith);
        assert!(field.compare(&FieldValue::from("zsh"), &FieldValue::from("z")));
        assert!(!field.compare(&FieldValue::from("zsh"), &FieldValue::from("sd")));
        field.modifier.match_modifier = Some(MatchModifier::EndsWith);
        assert!(field.compare(&FieldValue::from("zsh"), &FieldValue::from("sh")));
        assert!(!field.compare(&FieldValue::from("zsh"), &FieldValue::from("sd")));
        field.modifier.match_modifier = Some(MatchModifier::Contains);
        assert!(field.compare(&FieldValue::from("zsh"), &FieldValue::from("s")));
        assert!(!field.compare(&FieldValue::from("zsh"), &FieldValue::from("d")));
    }
    #[test]
    fn test_cidr() {
        let cidrs = ["10.0.0.0/16", "10.0.0.0/24"];
        let mut field = Field::new(
            "test|cidr",
            cidrs.into_iter().map(FieldValue::from).collect(),
        )
        .unwrap();
        let event = Event::from([("test", "10.0.1.1")]);
        assert!(field.evaluate(&event));
        field.modifier.match_all = true;
        assert!(!field.evaluate(&event));
        let event = Event::from([("test", "10.1.2.3")]);
        field.modifier.match_all = false;
        assert!(!field.evaluate(&event));
    }
    #[test]
    fn test_base64_utf16le() {
        let patterns = ["Add-MpPreference ", "Set-MpPreference "];
        let field = Field::new(
            "test|base64|utf16le|contains",
            patterns
                .iter()
                .map(|x| FieldValue::from(x.to_string()))
                .collect(),
        )
        .unwrap();
        let event = Event::from([(
            "test",
            "jkdfgnhjkQQBkAGQALQBNAHAAUAByAGUAZgBlAHIAZQBuAGMAZQAgAioskdfgjk",
        )]);
        assert!(field.evaluate(&event));
        let event = Event::from([(
            "test",
            "23234345UwBlAHQALQBNAHAAUAByAGUAZgBlAHIAZQBuAGMAZQAgA3535446d",
        )]);
        assert!(field.evaluate(&event));
    }
    #[test]
    fn test_base64offset_utf16le() {
        let patterns = [
            "Add-MpPreference ",
            "Set-MpPreference ",
            "add-mppreference ",
            "set-mppreference ",
        ];
        let field = Field::new(
            "test|base64offset|utf16le|contains",
            patterns.into_iter().map(FieldValue::from).collect(),
        )
        .unwrap();
        let expected = [
            "QQBkAGQALQBNAHAAUAByAGUAZgBlAHIAZQBuAGMAZQAgA",
            "EAZABkAC0ATQBwAFAAcgBlAGYAZQByAGUAbgBjAGUAIA",
            "BAGQAZAAtAE0AcABQAHIAZQBmAGUAcgBlAG4AYwBlACAA",
            "UwBlAHQALQBNAHAAUAByAGUAZgBlAHIAZQBuAGMAZQAgA",
            "MAZQB0AC0ATQBwAFAAcgBlAGYAZQByAGUAbgBjAGUAIA",
            "TAGUAdAAtAE0AcABQAHIAZQBmAGUAcgBlAG4AYwBlACAA",
            "YQBkAGQALQBtAHAAcAByAGUAZgBlAHIAZQBuAGMAZQAgA",
            "EAZABkAC0AbQBwAHAAcgBlAGYAZQByAGUAbgBjAGUAIA",
            "hAGQAZAAtAG0AcABwAHIAZQBmAGUAcgBlAG4AYwBlACAA",
            "cwBlAHQALQBtAHAAcAByAGUAZgBlAHIAZQBuAGMAZQAgA",
            "MAZQB0AC0AbQBwAHAAcgBlAGYAZQByAGUAbgBjAGUAIA",
            "zAGUAdAAtAG0AcABwAHIAZQBmAGUAcgBlAG4AYwBlACAA",
        ];
        for pattern in expected.into_iter() {
            let mut scrambled_pattern = pattern.to_string().clone();
            scrambled_pattern.insert_str(0, "klsenf");
            scrambled_pattern.insert_str(scrambled_pattern.len(), "scvfv");
            let event = Event::from([("test", scrambled_pattern.clone())]);
            assert!(
                field.evaluate(&event),
                "pattern: {} || values: {:?}",
                scrambled_pattern,
                field.values
            );
        }
    }
    #[test]
    fn test_windash() {
        let patterns = ["-my-param", "/another-param"];
        let field = Field::new(
            "test|windash|contains",
            patterns.into_iter().map(FieldValue::from).collect(),
        )
        .unwrap();
        let event = Event::from([("test", "program.exe /my-param")]);
        assert!(field.evaluate(&event));
        let event = Event::from([("test", "another.exe -another-param")]);
        assert!(field.evaluate(&event));
    }
    #[test]
    fn test_empty_values() {
        let values: Vec<FieldValue> = vec![];
        let err = Field::new("test|contains", values).unwrap_err();
        assert!(matches!(err, ParserError::EmptyValues(a) if a == "test"));
    }
    #[test]
    fn test_invalid_contains() {
        let values: Vec<FieldValue> = vec![FieldValue::from("ok"), FieldValue::Int(5)];
        let err = Field::new("test|contains", values).unwrap_err();
        assert!(matches!(err, ParserError::InvalidValueForStringModifier(_)));
    }
    #[test]
    fn test_parse_exists_modifier() {
        let values: Vec<FieldValue> = vec![FieldValue::from(true)];
        let field = Field::new("test|exists", values).unwrap();
        assert!(field.modifier.exists.unwrap());
        let values: Vec<FieldValue> = vec![FieldValue::from(false)];
        let field = Field::new("test|exists", values).unwrap();
        assert!(!field.modifier.exists.unwrap());
    }
    #[test]
    fn test_parse_exists_modifier_invalid_values() {
        let values_vec: Vec<Vec<FieldValue>> = vec![
            vec![FieldValue::from("not a boolean")],
            vec![FieldValue::from("something"), FieldValue::Float(5.0)],
            vec![FieldValue::from(true), FieldValue::from(true)],
        ];
        for values in values_vec {
            let err = Field::new("test|exists", values).unwrap_err();
            assert!(matches!(err, ParserError::InvalidValueForExists()));
        }
    }
}