Documentation
use serde_json::Value;
use std::collections::HashMap;

use crate::infrastructure::http::request::form_request::errors::ValidationErrors;
use crate::infrastructure::http::request::form_request::validate_rules::{
    rules_registry, split_rule,
};

pub struct Validator {
    pub data: HashMap<String, Value>,
    rules: HashMap<String, Vec<String>>,
    pub custom_messages: HashMap<String, String>,
    pub custom_attributes: HashMap<String, String>,
    errors: ValidationErrors,
}

impl Validator {
    pub fn make(data: HashMap<String, Value>, rules: HashMap<&'static str, &'static str>) -> Self {
        let parsed_rules = rules
            .into_iter()
            .map(|(k, v)| {
                (
                    k.to_string(),
                    v.split('|').map(|s| s.trim().to_string()).collect(),
                )
            })
            .collect();

        Self {
            data,
            rules: parsed_rules,
            custom_messages: HashMap::new(),
            custom_attributes: HashMap::new(),
            errors: HashMap::new(),
        }
    }

    pub fn set_custom_messages(&mut self, messages: HashMap<&'static str, &'static str>) {
        self.custom_messages = messages
            .into_iter()
            .map(|(k, v)| (k.to_string(), v.to_string()))
            .collect();
    }

    pub fn set_custom_attributes(&mut self, attributes: HashMap<&'static str, &'static str>) {
        self.custom_attributes = attributes
            .into_iter()
            .map(|(k, v)| (k.to_string(), v.to_string()))
            .collect();
    }

    pub fn validate(&mut self) -> Result<(), ValidationErrors> {
        self.errors.clear();
        let registry = rules_registry();

        for (field, rules) in &self.rules {
            let value = self.data.get(field);
            let field_name = self.custom_attributes.get(field).unwrap_or(field);

            if value.is_none() && rules.contains(&"nullable".into()) {
                continue;
            }

            for rule in rules {
                let (rule_name, param) = split_rule(rule);

                if rule_name == "nullable" {
                    continue;
                }

                if let Some(handler) = registry.get(rule_name) {
                    let result = handler(field, field_name, value, self, param);

                    if let Err(msg) = result {
                        self.errors
                            .entry(field.clone())
                            .or_insert_with(Vec::new)
                            .push(msg);
                    }
                }
            }
        }

        if self.errors.is_empty() {
            Ok(())
        } else {
            Err(self.errors.clone())
        }
    }

    pub fn validated_data(&self) -> HashMap<String, Value> {
        self.data
            .iter()
            .filter(|(k, _)| self.rules.contains_key(*k))
            .map(|(k, v)| (k.clone(), v.clone()))
            .collect()
    }
}