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

impl<'a> ValidationContext<'a> {
  pub(crate) fn validate_combinators(
    &self,
    result: &mut ValidationResult,
  ) -> Result<bool, ValidationError> {
    if let Some(ref all_of) = self.schema.all_of {
      for sub in all_of {
        let derived = self.derive_for_schema(sub, true);
        let res = derived.validate()?;
        result.merge(res);
      }
    }

    if let Some(ref one_of) = self.schema.one_of {
      let mut passed_candidates: Vec<(Option<String>, usize, ValidationResult)> = Vec::new();

      for sub in one_of {
        let derived = self.derive_for_schema(sub, true);
        let sub_res = derived.validate()?;
        if sub_res.is_valid() {
          let child_id = sub.id.clone();
          let depth = child_id
            .as_ref()
            .and_then(|id| self.db.depths.get(id).copied())
            .unwrap_or(0);
          passed_candidates.push((child_id, depth, sub_res));
        }
      }

      if passed_candidates.len() == 1 {
        result.merge(passed_candidates.pop().unwrap().2);
      } else if passed_candidates.is_empty() {
        result.errors.push(ValidationError {
          code: "NO_ONEOF_MATCH".to_string(),
          message: "Matches none of oneOf schemas".to_string(),
          path: self.path.to_string(),
        });
      } else {
        // Apply depth heuristic tie-breaker
        let mut best_depth: Option<usize> = None;
        let mut ambiguous = false;
        let mut best_res = None;

        for (_, depth, res) in passed_candidates.into_iter() {
          if let Some(current_best) = best_depth {
            if depth > current_best {
              best_depth = Some(depth);
              best_res = Some(res);
              ambiguous = false;
            } else if depth == current_best {
              ambiguous = true;
            }
          } else {
            best_depth = Some(depth);
            best_res = Some(res);
          }
        }

        if !ambiguous {
          if let Some(res) = best_res {
            result.merge(res);
            return Ok(true);
          }
        }

        result.errors.push(ValidationError {
          code: "AMBIGUOUS_ONEOF_MATCH".to_string(),
          message: "Matches multiple oneOf schemas without a clear depth winner".to_string(),
          path: self.path.to_string(),
        });
      }
    }

    if let Some(ref not_schema) = self.schema.not {
      let derived = self.derive_for_schema(not_schema, true);
      let sub_res = derived.validate()?;
      if sub_res.is_valid() {
        result.errors.push(ValidationError {
          code: "NOT_VIOLATED".to_string(),
          message: "Matched 'not' schema".to_string(),
          path: self.path.to_string(),
        });
      }
    }

    Ok(true)
  }
}
