use crate::validator::context::ValidationContext;
use crate::validator::error::ValidationError;
use crate::validator::result::ValidationResult;

impl<'a> ValidationContext<'a> {
  pub(crate) fn validate_conditionals(
    &self,
    result: &mut ValidationResult,
  ) -> Result<bool, ValidationError> {
    if let Some(ref if_schema) = self.schema.if_ {
      let derived_if = self.derive_for_schema(if_schema, true);
      let if_res = derived_if.validate()?;

      result.evaluated_keys.extend(if_res.evaluated_keys.clone());
      result
        .evaluated_indices
        .extend(if_res.evaluated_indices.clone());

      if if_res.is_valid() {
        if let Some(ref then_schema) = self.schema.then_ {
          let derived_then = self.derive_for_schema(then_schema, true);
          result.merge(derived_then.validate()?);
        }
      } else if let Some(ref else_schema) = self.schema.else_ {
        let derived_else = self.derive_for_schema(else_schema, true);
        result.merge(derived_else.validate()?);
      }
    }

    Ok(true)
  }

  pub(crate) fn validate_strictness(
    &self,
    result: &mut ValidationResult,
  ) -> Result<bool, ValidationError> {
    if self.extensible || self.reporter {
      return Ok(true);
    }

    if let Some(obj) = self.instance.as_object() {
      for key in obj.keys() {
        if !result.evaluated_keys.contains(key) && !self.overrides.contains(key) {
          result.errors.push(ValidationError {
            code: "STRICT_PROPERTY_VIOLATION".to_string(),
            message: format!("Unexpected property '{}'", key),
            path: format!("{}/{}", self.path, key),
          });
        }
      }
    }

    if let Some(arr) = self.instance.as_array() {
      for i in 0..arr.len() {
        if !result.evaluated_indices.contains(&i) {
          result.errors.push(ValidationError {
            code: "STRICT_ITEM_VIOLATION".to_string(),
            message: format!("Unexpected item at index {}", i),
            path: format!("{}/{}", self.path, i),
          });
        }
      }
    }

    Ok(true)
  }
}
