//! Guardrail rule definitions and registry.
//!
//! Each rule implements the [`GuardrailRule`] trait and is registered
//! in the [`RuleRegistry`]. The registry evaluates all rules against
//! a query's explanation and policy configuration.

pub mod cost_control;
pub mod data_protection;
pub mod query_patterns;
pub mod tag_rules;

use crate::explainer::types::QueryExplanation;
use crate::schema::types::SchemaDefinition;

use super::policy::PolicyConfig;
use super::types::{PolicyCategory, PolicyViolation, PolicyWarning};

/// Result of evaluating a single rule.
pub struct RuleResult {
    /// Blocking violations found by this rule
    pub violations: Vec<PolicyViolation>,
    /// Non-blocking warnings found by this rule
    pub warnings: Vec<PolicyWarning>,
}

/// Trait for individual guardrail rules.
///
/// Each rule checks one specific aspect of a query against the policy
/// configuration. Rules receive the full query explanation and schema
/// for maximum flexibility.
pub trait GuardrailRule: Send + Sync {
    /// The name of this rule (used in violation/warning messages).
    fn name(&self) -> &str;

    /// The policy category this rule belongs to.
    fn category(&self) -> PolicyCategory;

    /// Evaluate this rule against a query.
    ///
    /// Returns violations and warnings. The rule should check its
    /// corresponding section of the [`PolicyConfig`] and skip evaluation
    /// if the section is absent or disabled.
    fn evaluate(
        &self,
        sql: &str,
        explanation: &QueryExplanation,
        schema: &SchemaDefinition,
        config: &PolicyConfig,
    ) -> RuleResult;
}

/// Registry of guardrail rules.
///
/// Holds a collection of rules and evaluates them all against a query.
pub struct RuleRegistry {
    rules: Vec<Box<dyn GuardrailRule>>,
}

impl RuleRegistry {
    /// Create a registry with all built-in rules registered.
    pub fn with_defaults() -> Self {
        let rules: Vec<Box<dyn GuardrailRule>> = vec![
            Box::new(cost_control::MaxJoinsRule),
            Box::new(cost_control::MaxTablesRule),
            Box::new(cost_control::BlockSelectStarRule),
            Box::new(cost_control::RequireLimitRule),
            Box::new(cost_control::BlockCrossJoinRule),
            Box::new(cost_control::MaxComplexityScoreRule),
            Box::new(data_protection::BlockedColumnsRule),
            Box::new(data_protection::BlockedTablesRule),
            Box::new(data_protection::RequireWhereClauseRule),
            Box::new(query_patterns::AllowedStatementsRule),
            Box::new(query_patterns::BlockStatementsRule),
            Box::new(tag_rules::TagRule),
        ];
        Self { rules }
    }

    /// Evaluate all rules against a query and collect results.
    ///
    /// Returns `(violations, warnings, rules_evaluated_count)`.
    pub fn evaluate_all(
        &self,
        sql: &str,
        explanation: &QueryExplanation,
        schema: &SchemaDefinition,
        config: &PolicyConfig,
    ) -> (Vec<PolicyViolation>, Vec<PolicyWarning>, usize) {
        let mut all_violations = Vec::new();
        let mut all_warnings = Vec::new();
        let count = self.rules.len();

        for rule in &self.rules {
            let result = rule.evaluate(sql, explanation, schema, config);
            all_violations.extend(result.violations);
            all_warnings.extend(result.warnings);
        }

        (all_violations, all_warnings, count)
    }
}
