use onig::{Regex, RegexOptions, Syntax};
use yaml_rust::Yaml;

use crate::UserAgentParserError;

#[derive(Debug)]
pub struct DeviceRegex {
    pub(crate) regex: Regex,
    pub(crate) device_replacement: Option<String>,
    pub(crate) brand_replacement: Option<String>,
    pub(crate) model_replacement: Option<String>,
}

impl DeviceRegex {
    pub fn from_yaml(yaml: &Yaml) -> Result<Vec<DeviceRegex>, UserAgentParserError> {
        let yamls = yaml.as_vec().ok_or(UserAgentParserError::IncorrectSource)?;

        let yamls_len = yamls.len();

        if yamls_len == 0 {
            Err(UserAgentParserError::IncorrectSource)
        } else {
            let mut device_regexes = Vec::with_capacity(yamls_len);

            let yaml_regex = Yaml::String("regex".to_string());
            let yaml_device_replacement = Yaml::String("device_replacement".to_string());
            let yaml_brand_replacement = Yaml::String("brand_replacement".to_string());
            let yaml_model_replacement = Yaml::String("model_replacement".to_string());
            let yaml_regex_flag = Yaml::String("regex_flag".to_string());

            for yaml in yamls {
                let yaml = yaml.as_hash().ok_or(UserAgentParserError::IncorrectSource)?;

                let device_replacement = match yaml.get(&yaml_device_replacement) {
                    Some(yaml) => {
                        yaml.as_str()
                            .map(|s| Some(s.to_string()))
                            .ok_or(UserAgentParserError::IncorrectSource)?
                    }
                    None => None,
                };

                let brand_replacement = match yaml.get(&yaml_brand_replacement) {
                    Some(yaml) => {
                        yaml.as_str()
                            .map(|s| Some(s.to_string()))
                            .ok_or(UserAgentParserError::IncorrectSource)?
                    }
                    None => None,
                };

                let model_replacement = match yaml.get(&yaml_model_replacement) {
                    Some(yaml) => {
                        yaml.as_str()
                            .map(|s| Some(s.to_string()))
                            .ok_or(UserAgentParserError::IncorrectSource)?
                    }
                    None => None,
                };

                let regex_options = if let Some(yaml) = yaml.get(&yaml_regex_flag) {
                    let regex_flag = yaml.as_str().ok_or(UserAgentParserError::IncorrectSource)?;

                    if regex_flag == "i" {
                        RegexOptions::REGEX_OPTION_IGNORECASE
                    } else {
                        RegexOptions::REGEX_OPTION_NONE
                    }
                } else {
                    RegexOptions::REGEX_OPTION_NONE
                };

                let regex = Regex::with_options(
                    yaml.get(&yaml_regex)
                        .ok_or(UserAgentParserError::IncorrectSource)?
                        .as_str()
                        .ok_or(UserAgentParserError::IncorrectSource)?,
                    regex_options,
                    Syntax::default(),
                )?;

                let device_regex = DeviceRegex {
                    regex,
                    device_replacement,
                    brand_replacement,
                    model_replacement,
                };

                device_regexes.push(device_regex);
            }

            Ok(device_regexes)
        }
    }
}
