use crate::validator::Validator;
use crate::validator::context::ValidationContext;
use crate::validator::error::ValidationError;
use crate::validator::result::ValidationResult;
use crate::validator::rules::util::equals;

impl<'a> ValidationContext<'a> {
  pub(crate) fn validate_core(
    &self,
    result: &mut ValidationResult,
  ) -> Result<bool, ValidationError> {
    let current = self.instance;

    if let Some(ref type_) = self.schema.type_ {
      match type_ {
        crate::database::schema::SchemaTypeOrArray::Single(t) => {
          if !Validator::check_type(t, current) {
            result.errors.push(ValidationError {
              code: "INVALID_TYPE".to_string(),
              message: format!("Expected type '{}'", t),
              path: self.path.to_string(),
            });
          }
        }
        crate::database::schema::SchemaTypeOrArray::Multiple(types) => {
          let mut valid = false;
          for t in types {
            if Validator::check_type(t, current) {
              valid = true;
              break;
            }
          }
          if !valid {
            result.errors.push(ValidationError {
              code: "INVALID_TYPE".to_string(),
              message: format!("Expected one of types {:?}", types),
              path: self.path.to_string(),
            });
          }
        }
      }
    }

    if let Some(ref const_val) = self.schema.const_ {
      if !equals(current, const_val) {
        result.errors.push(ValidationError {
          code: "CONST_VIOLATED".to_string(),
          message: "Value does not match const".to_string(),
          path: self.path.to_string(),
        });
      } else if let Some(obj) = current.as_object() {
        result.evaluated_keys.extend(obj.keys().cloned());
      } else if let Some(arr) = current.as_array() {
        result.evaluated_indices.extend(0..arr.len());
      }
    }

    if let Some(ref enum_vals) = self.schema.enum_ {
      let mut found = false;
      for val in enum_vals {
        if equals(current, val) {
          found = true;
          break;
        }
      }
      if !found {
        result.errors.push(ValidationError {
          code: "ENUM_MISMATCH".to_string(),
          message: "Value is not in enum".to_string(),
          path: self.path.to_string(),
        });
      } else if let Some(obj) = current.as_object() {
        result.evaluated_keys.extend(obj.keys().cloned());
      } else if let Some(arr) = current.as_array() {
        result.evaluated_indices.extend(0..arr.len());
      }
    }

    Ok(true)
  }
}
