use crate::core::FunctionMetrics;
use serde::{Deserialize, Serialize};

/// Preset threshold configurations
#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
pub enum ThresholdPreset {
    /// Strict thresholds for high code quality standards
    Strict,
    /// Balanced thresholds for typical projects
    Balanced,
    /// Lenient thresholds for legacy or complex domains
    Lenient,
}

/// Complexity threshold configuration for determining when to flag functions
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ComplexityThresholds {
    /// Minimum total complexity to flag (cyclomatic + cognitive)
    #[serde(default = "default_minimum_total_complexity")]
    pub minimum_total_complexity: u32,

    /// Minimum cyclomatic complexity to flag
    #[serde(default = "default_minimum_cyclomatic_complexity")]
    pub minimum_cyclomatic_complexity: u32,

    /// Minimum cognitive complexity to flag
    #[serde(default = "default_minimum_cognitive_complexity")]
    pub minimum_cognitive_complexity: u32,

    /// Minimum number of match arms to flag
    #[serde(default = "default_minimum_match_arms")]
    pub minimum_match_arms: usize,

    /// Minimum if-else chain length to flag
    #[serde(default = "default_minimum_if_else_chain")]
    pub minimum_if_else_chain: usize,

    /// Minimum function length (lines) to flag
    #[serde(default = "default_minimum_function_length")]
    pub minimum_function_length: usize,

    // Role-based multipliers
    /// Multiplier for entry point functions (main, handlers)
    #[serde(default = "default_entry_point_multiplier")]
    pub entry_point_multiplier: f64,

    /// Multiplier for core logic functions
    #[serde(default = "default_core_logic_multiplier")]
    pub core_logic_multiplier: f64,

    /// Multiplier for utility functions (getters, setters)
    #[serde(default = "default_utility_multiplier")]
    pub utility_multiplier: f64,

    /// Multiplier for test functions
    #[serde(default = "default_test_function_multiplier")]
    pub test_function_multiplier: f64,
}

impl Default for ComplexityThresholds {
    fn default() -> Self {
        Self {
            minimum_total_complexity: default_minimum_total_complexity(),
            minimum_cyclomatic_complexity: default_minimum_cyclomatic_complexity(),
            minimum_cognitive_complexity: default_minimum_cognitive_complexity(),
            minimum_match_arms: default_minimum_match_arms(),
            minimum_if_else_chain: default_minimum_if_else_chain(),
            minimum_function_length: default_minimum_function_length(),
            entry_point_multiplier: default_entry_point_multiplier(),
            core_logic_multiplier: default_core_logic_multiplier(),
            utility_multiplier: default_utility_multiplier(),
            test_function_multiplier: default_test_function_multiplier(),
        }
    }
}

// Default values
fn default_minimum_total_complexity() -> u32 {
    8
}
fn default_minimum_cyclomatic_complexity() -> u32 {
    5
}
fn default_minimum_cognitive_complexity() -> u32 {
    10
}
fn default_minimum_match_arms() -> usize {
    4
}
fn default_minimum_if_else_chain() -> usize {
    3
}
fn default_minimum_function_length() -> usize {
    20
}
fn default_entry_point_multiplier() -> f64 {
    1.5
}
fn default_core_logic_multiplier() -> f64 {
    1.0
}
fn default_utility_multiplier() -> f64 {
    0.8
}
fn default_test_function_multiplier() -> f64 {
    2.0
}

/// Complexity level classification
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub enum ComplexityLevel {
    Trivial,
    Moderate,
    High,
    Excessive,
}

impl ComplexityThresholds {
    /// Create thresholds from a preset configuration
    pub fn from_preset(preset: ThresholdPreset) -> Self {
        match preset {
            ThresholdPreset::Strict => Self {
                minimum_total_complexity: 5,
                minimum_cyclomatic_complexity: 3,
                minimum_cognitive_complexity: 7,
                minimum_match_arms: 3,
                minimum_if_else_chain: 2,
                minimum_function_length: 15,
                entry_point_multiplier: 1.2,
                core_logic_multiplier: 1.0,
                utility_multiplier: 0.6,
                test_function_multiplier: 3.0, // More lenient for test functions
            },
            ThresholdPreset::Balanced => Self::default(),
            ThresholdPreset::Lenient => Self {
                minimum_total_complexity: 15,
                minimum_cyclomatic_complexity: 10,
                minimum_cognitive_complexity: 20,
                minimum_match_arms: 8,
                minimum_if_else_chain: 5,
                minimum_function_length: 50,
                entry_point_multiplier: 2.0,
                core_logic_multiplier: 1.0,
                utility_multiplier: 1.0,
                test_function_multiplier: 3.0,
            },
        }
    }

    /// Check if a function should be flagged based on thresholds.
    ///
    /// A function is flagged if ANY of these conditions are met:
    /// 1. Cyclomatic complexity alone exceeds its threshold
    /// 2. Cognitive complexity alone exceeds its threshold
    /// 3. Total complexity (cyclomatic + cognitive) exceeds its threshold AND length exceeds minimum
    /// 4. Function length is extremely long (5x the minimum threshold)
    ///
    /// This ensures single-dimension complexity issues are caught, not just
    /// functions that are complex in ALL dimensions simultaneously.
    pub fn should_flag_function(&self, metrics: &FunctionMetrics, role: FunctionRole) -> bool {
        let multiplier = self.get_role_multiplier(role);

        // Apply multiplier to thresholds (higher multiplier = more lenient)
        let adjusted_cyclomatic_threshold =
            (self.minimum_cyclomatic_complexity as f64 * multiplier) as u32;
        let adjusted_cognitive_threshold =
            (self.minimum_cognitive_complexity as f64 * multiplier) as u32;
        let adjusted_total_threshold = (self.minimum_total_complexity as f64 * multiplier) as u32;
        let adjusted_length_threshold = (self.minimum_function_length as f64 * multiplier) as usize;

        let total_complexity = metrics.cyclomatic + metrics.cognitive;

        // Flag if ANY significant threshold is exceeded
        let cyclomatic_exceeded = metrics.cyclomatic >= adjusted_cyclomatic_threshold;
        let cognitive_exceeded = metrics.cognitive >= adjusted_cognitive_threshold;
        let total_exceeded = total_complexity >= adjusted_total_threshold;
        let length_exceeded = metrics.length >= adjusted_length_threshold;
        let extremely_long = metrics.length >= adjusted_length_threshold * 5;

        // Flag if:
        // - Cyclomatic alone is high enough, OR
        // - Cognitive alone is high enough, OR
        // - Total complexity is high AND function is not trivially short, OR
        // - Function is extremely long (5x threshold)
        cyclomatic_exceeded
            || cognitive_exceeded
            || (total_exceeded && length_exceeded)
            || extremely_long
    }

    /// Get the complexity level for given metrics
    pub fn get_complexity_level(&self, metrics: &FunctionMetrics) -> ComplexityLevel {
        let total = metrics.cyclomatic + metrics.cognitive;
        match total {
            t if t < self.minimum_total_complexity => ComplexityLevel::Trivial,
            t if t < self.minimum_total_complexity * 2 => ComplexityLevel::Moderate,
            t if t < self.minimum_total_complexity * 3 => ComplexityLevel::High,
            _ => ComplexityLevel::Excessive,
        }
    }

    /// Get role-based multiplier
    pub fn get_role_multiplier(&self, role: FunctionRole) -> f64 {
        match role {
            FunctionRole::EntryPoint => self.entry_point_multiplier,
            FunctionRole::CoreLogic => self.core_logic_multiplier,
            FunctionRole::Utility => self.utility_multiplier,
            FunctionRole::Test => self.test_function_multiplier,
            FunctionRole::Unknown => self.core_logic_multiplier,
        }
    }

    /// Validate thresholds are reasonable
    pub fn validate(&self) -> Result<(), String> {
        if self.minimum_total_complexity == 0 {
            return Err("minimum_total_complexity must be greater than 0".to_string());
        }
        if self.minimum_cyclomatic_complexity == 0 {
            return Err("minimum_cyclomatic_complexity must be greater than 0".to_string());
        }
        if self.minimum_cognitive_complexity == 0 {
            return Err("minimum_cognitive_complexity must be greater than 0".to_string());
        }

        // Check multipliers are positive
        if self.entry_point_multiplier <= 0.0 {
            return Err("entry_point_multiplier must be positive".to_string());
        }
        if self.core_logic_multiplier <= 0.0 {
            return Err("core_logic_multiplier must be positive".to_string());
        }
        if self.utility_multiplier <= 0.0 {
            return Err("utility_multiplier must be positive".to_string());
        }
        if self.test_function_multiplier <= 0.0 {
            return Err("test_function_multiplier must be positive".to_string());
        }

        Ok(())
    }

    /// Get preset configurations
    pub fn preset(name: &str) -> Option<Self> {
        match name {
            "strict" => Some(Self {
                minimum_total_complexity: 5,
                minimum_cyclomatic_complexity: 3,
                minimum_cognitive_complexity: 7,
                minimum_match_arms: 3,
                minimum_if_else_chain: 2,
                minimum_function_length: 15,
                entry_point_multiplier: 1.2,
                core_logic_multiplier: 1.0,
                utility_multiplier: 0.6,
                test_function_multiplier: 3.0,
            }),
            "balanced" => Some(Self::default()),
            "lenient" => Some(Self {
                minimum_total_complexity: 15,
                minimum_cyclomatic_complexity: 10,
                minimum_cognitive_complexity: 20,
                minimum_match_arms: 8,
                minimum_if_else_chain: 5,
                minimum_function_length: 50,
                entry_point_multiplier: 2.0,
                core_logic_multiplier: 1.0,
                utility_multiplier: 1.0,
                test_function_multiplier: 3.0,
            }),
            _ => None,
        }
    }
}

/// Function role classification (duplicated here to avoid circular dependency)
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub enum FunctionRole {
    EntryPoint,
    CoreLogic,
    Utility,
    Test,
    Unknown,
}

impl FunctionRole {
    /// Determine role from function name
    pub fn from_name(name: &str) -> Self {
        if name == "main" || name.ends_with("_handler") || name.starts_with("handle_") {
            Self::EntryPoint
        } else if name.starts_with("test_") || name.ends_with("_test") {
            Self::Test
        } else if name.starts_with("get_")
            || name.starts_with("set_")
            || name.starts_with("is_")
            || name.starts_with("has_")
        {
            Self::Utility
        } else {
            Self::CoreLogic
        }
    }
}

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

    #[test]
    fn test_default_thresholds() {
        let thresholds = ComplexityThresholds::default();
        assert_eq!(thresholds.minimum_total_complexity, 8);
        assert_eq!(thresholds.minimum_cyclomatic_complexity, 5);
        assert_eq!(thresholds.minimum_cognitive_complexity, 10);
    }

    #[test]
    fn test_should_flag_function() {
        let thresholds = ComplexityThresholds::default();

        let mut simple_metrics =
            FunctionMetrics::new("simple".to_string(), std::path::PathBuf::from("test.rs"), 1);
        simple_metrics.cyclomatic = 2;
        simple_metrics.cognitive = 3;
        simple_metrics.length = 10;
        simple_metrics.is_pure = Some(true);
        simple_metrics.purity_confidence = Some(0.9);

        // Simple function should not be flagged
        assert!(!thresholds.should_flag_function(&simple_metrics, FunctionRole::CoreLogic));

        let mut complex_metrics = FunctionMetrics::new(
            "complex".to_string(),
            std::path::PathBuf::from("test.rs"),
            1,
        );
        complex_metrics.cyclomatic = 10;
        complex_metrics.cognitive = 15;
        complex_metrics.length = 50;
        complex_metrics.is_pure = Some(false);
        complex_metrics.purity_confidence = Some(0.9);

        // Complex function should be flagged
        assert!(thresholds.should_flag_function(&complex_metrics, FunctionRole::CoreLogic));
    }

    #[test]
    fn test_complexity_levels() {
        let thresholds = ComplexityThresholds::default();

        let mut trivial = FunctionMetrics::new(
            "trivial".to_string(),
            std::path::PathBuf::from("test.rs"),
            1,
        );
        trivial.cyclomatic = 2;
        trivial.cognitive = 3;
        assert_eq!(
            thresholds.get_complexity_level(&trivial),
            ComplexityLevel::Trivial
        );

        let mut moderate = FunctionMetrics::new(
            "moderate".to_string(),
            std::path::PathBuf::from("test.rs"),
            1,
        );
        moderate.cyclomatic = 5;
        moderate.cognitive = 8;
        assert_eq!(
            thresholds.get_complexity_level(&moderate),
            ComplexityLevel::Moderate
        );

        let mut high =
            FunctionMetrics::new("high".to_string(), std::path::PathBuf::from("test.rs"), 1);
        high.cyclomatic = 8;
        high.cognitive = 12;
        assert_eq!(
            thresholds.get_complexity_level(&high),
            ComplexityLevel::High
        );

        let mut excessive = FunctionMetrics::new(
            "excessive".to_string(),
            std::path::PathBuf::from("test.rs"),
            1,
        );
        excessive.cyclomatic = 20;
        excessive.cognitive = 30;
        assert_eq!(
            thresholds.get_complexity_level(&excessive),
            ComplexityLevel::Excessive
        );
    }

    #[test]
    fn test_presets() {
        let strict = ComplexityThresholds::preset("strict").unwrap();
        assert_eq!(strict.minimum_total_complexity, 5);

        let lenient = ComplexityThresholds::preset("lenient").unwrap();
        assert_eq!(lenient.minimum_total_complexity, 15);

        let balanced = ComplexityThresholds::preset("balanced").unwrap();
        assert_eq!(balanced.minimum_total_complexity, 8);
    }

    #[test]
    fn test_role_multipliers() {
        let thresholds = ComplexityThresholds::default();

        assert_eq!(
            thresholds.get_role_multiplier(FunctionRole::EntryPoint),
            1.5
        );
        assert_eq!(thresholds.get_role_multiplier(FunctionRole::CoreLogic), 1.0);
        assert_eq!(thresholds.get_role_multiplier(FunctionRole::Utility), 0.8);
        assert_eq!(thresholds.get_role_multiplier(FunctionRole::Test), 2.0);
    }

    /// Bug fix test: A function with extremely high cyclomatic complexity
    /// should be flagged even if cognitive complexity is low.
    /// The current bug requires ALL thresholds to be exceeded, which means
    /// single-dimension complexity issues are missed.
    #[test]
    fn test_high_cyclomatic_low_cognitive_should_flag() {
        let thresholds = ComplexityThresholds::default();
        // Default thresholds: cyclomatic=5, cognitive=10, total=8, length=20

        let mut high_cyclomatic = FunctionMetrics::new(
            "high_cyclomatic".to_string(),
            std::path::PathBuf::from("test.rs"),
            1,
        );
        // Very high cyclomatic (10x threshold), low cognitive (below threshold)
        high_cyclomatic.cyclomatic = 50; // Way over 5 threshold
        high_cyclomatic.cognitive = 5; // Under 10 threshold
        high_cyclomatic.length = 100; // Over 20 threshold
        // Total = 55, way over 8 threshold

        // This should be flagged - cyclomatic=50 is extremely complex
        assert!(
            thresholds.should_flag_function(&high_cyclomatic, FunctionRole::CoreLogic),
            "Function with cyclomatic=50 should be flagged even if cognitive is low"
        );
    }

    /// Bug fix test: A function with extremely high cognitive complexity
    /// should be flagged even if cyclomatic complexity is low.
    #[test]
    fn test_high_cognitive_low_cyclomatic_should_flag() {
        let thresholds = ComplexityThresholds::default();

        let mut high_cognitive = FunctionMetrics::new(
            "high_cognitive".to_string(),
            std::path::PathBuf::from("test.rs"),
            1,
        );
        // Low cyclomatic (below threshold), very high cognitive (5x threshold)
        high_cognitive.cyclomatic = 3; // Under 5 threshold
        high_cognitive.cognitive = 50; // Way over 10 threshold
        high_cognitive.length = 100;
        // Total = 53, way over 8 threshold

        // This should be flagged - cognitive=50 is extremely complex
        assert!(
            thresholds.should_flag_function(&high_cognitive, FunctionRole::CoreLogic),
            "Function with cognitive=50 should be flagged even if cyclomatic is low"
        );
    }

    /// Bug fix test: A very long function should be flagged even if
    /// complexity metrics are moderate.
    #[test]
    fn test_very_long_function_should_flag() {
        let thresholds = ComplexityThresholds::default();

        let mut very_long = FunctionMetrics::new(
            "very_long".to_string(),
            std::path::PathBuf::from("test.rs"),
            1,
        );
        // Moderate complexity but very long
        very_long.cyclomatic = 6; // Just over 5 threshold
        very_long.cognitive = 12; // Just over 10 threshold
        very_long.length = 500; // 25x the threshold!
        // Total = 18, over 8 threshold

        // This should be flagged - 500 lines is way too long
        assert!(
            thresholds.should_flag_function(&very_long, FunctionRole::CoreLogic),
            "Function with 500 lines should be flagged"
        );
    }
}
