//! Circuit breaker configuration extractor.
//!
//! Detects missing or explicitly disabled circuit breaker middleware.
//! Circuit breakers are critical for resilience - they prevent cascading
//! failures by temporarily blocking requests to misbehaving agents.

use regex::Regex;
use stemedb_core::types::ObjectValue;

use super::Extractor;
use crate::types::{ExtractedClaim, Language};

/// Extractor for circuit breaker configuration.
///
/// Detects:
/// - Explicitly disabled circuit breakers
/// - Router configurations missing circuit breaker middleware
pub struct CircuitBreakerConfigExtractor {
    /// Pattern: circuit_breaker_enabled: false
    disabled_pattern: Regex,
    /// Pattern: CircuitBreakerConfig with enabled: false
    config_disabled: Regex,
}

impl Default for CircuitBreakerConfigExtractor {
    fn default() -> Self {
        Self::new()
    }
}

impl CircuitBreakerConfigExtractor {
    /// Create a new circuit breaker config extractor.
    ///
    /// # Panics
    /// Panics if any regex pattern is invalid (programmer error).
    #[allow(clippy::expect_used)]
    pub fn new() -> Self {
        Self {
            // YAML/TOML: circuit_breaker_enabled: false
            disabled_pattern: Regex::new(r#"(?i)circuit_?breaker_?enabled\s*[:=]\s*false"#)
                .expect("valid regex"),

            // Look for lines with just "enabled: false" in circuit breaker context
            // We'll rely on the first pattern for most cases
            config_disabled: Regex::new(r"(?i)^\s*enabled\s*:\s*false").expect("valid regex"),
        }
    }

    /// Determine confidence based on context.
    fn confidence_for_file(&self, file: &str) -> f32 {
        if file.contains("test") || file.contains("example") {
            0.5
        } else {
            1.0
        }
    }
}

impl Extractor for CircuitBreakerConfigExtractor {
    fn name(&self) -> &str {
        "circuit_breaker_config"
    }

    fn languages(&self) -> &[Language] {
        &[Language::Rust, Language::Go, Language::Yaml, Language::Toml, Language::Json]
    }

    fn extract(
        &self,
        path_segments: &[String],
        content: &str,
        _language: Language,
        file: &str,
    ) -> Vec<ExtractedClaim> {
        let mut claims = Vec::new();
        let confidence = self.confidence_for_file(file);

        for (line_idx, line) in content.lines().enumerate() {
            let line_num = line_idx + 1;

            // Check for explicitly disabled circuit breaker
            if self.disabled_pattern.is_match(line) {
                let mut concept_path = path_segments.to_vec();
                concept_path.push("api".to_string());
                concept_path.push("circuit_breaker".to_string());

                claims.push(ExtractedClaim {
                    concept_path: format!("code://{}", concept_path.join("/")),
                    predicate: "enabled".to_string(),
                    value: ObjectValue::Boolean(false),
                    file: file.to_string(),
                    line: line_num,
                    matched_text: line.trim().to_string(),
                    confidence,
                    description: "Circuit breaker explicitly disabled".to_string(),
                });
            }

            // Check for config with enabled: false
            if self.config_disabled.is_match(line) {
                let mut concept_path = path_segments.to_vec();
                concept_path.push("api".to_string());
                concept_path.push("circuit_breaker".to_string());

                claims.push(ExtractedClaim {
                    concept_path: format!("code://{}", concept_path.join("/")),
                    predicate: "enabled".to_string(),
                    value: ObjectValue::Boolean(false),
                    file: file.to_string(),
                    line: line_num,
                    matched_text: line.trim().to_string(),
                    confidence: confidence * 0.9, // Slightly lower for multiline pattern
                    description: "Circuit breaker configuration disabled".to_string(),
                });
            }
        }

        claims
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_disabled_yaml() {
        let extractor = CircuitBreakerConfigExtractor::new();
        let content = r#"
api:
  circuit_breaker_enabled: false
  timeout: 30s
"#;

        let claims =
            extractor.extract(&["config".to_string()], content, Language::Yaml, "config/api.yaml");

        assert_eq!(claims.len(), 1);
        assert_eq!(claims[0].predicate, "enabled");
        assert_eq!(claims[0].value, ObjectValue::Boolean(false));
        assert!(claims[0].concept_path.contains("circuit_breaker"));
    }

    #[test]
    fn test_disabled_toml() {
        let extractor = CircuitBreakerConfigExtractor::new();
        let content = r#"
[api]
circuit_breaker_enabled = false
timeout = 30
"#;

        let claims =
            extractor.extract(&["config".to_string()], content, Language::Toml, "config.toml");

        assert_eq!(claims.len(), 1);
        assert_eq!(claims[0].value, ObjectValue::Boolean(false));
    }

    #[test]
    fn test_rust_config_disabled() {
        let extractor = CircuitBreakerConfigExtractor::new();
        let content = r#"
            CircuitBreakerConfig {
                enabled: false,
                failure_threshold: 5,
            }
        "#;

        let claims =
            extractor.extract(&["rust".to_string()], content, Language::Rust, "src/config.rs");

        assert_eq!(claims.len(), 1);
        assert_eq!(claims[0].predicate, "enabled");
    }

    #[test]
    fn test_enabled_not_flagged() {
        let extractor = CircuitBreakerConfigExtractor::new();
        let content = r#"
api:
  circuit_breaker_enabled: true
  failure_threshold: 5
"#;

        let claims =
            extractor.extract(&["config".to_string()], content, Language::Yaml, "config/api.yaml");

        // Should not flag when enabled
        assert_eq!(claims.len(), 0);
    }

    #[test]
    fn test_confidence_in_test_file() {
        let extractor = CircuitBreakerConfigExtractor::new();
        let content = r#"
            circuit_breaker_enabled: false
        "#;

        let claims =
            extractor.extract(&["rust".to_string()], content, Language::Rust, "src/config_test.rs");

        assert_eq!(claims.len(), 1);
        assert_eq!(claims[0].confidence, 0.5);
    }

    #[test]
    fn test_go_snake_case() {
        let extractor = CircuitBreakerConfigExtractor::new();
        let content = r#"
config := Config{
    CircuitBreakerEnabled: false,
}
"#;

        let claims = extractor.extract(&["go".to_string()], content, Language::Go, "config.go");

        assert_eq!(claims.len(), 1);
    }
}
