#![allow(unused)]
#![cfg_attr(coverage_nightly, coverage(off))]
//! Enforcement Layer: Error Budget System
//!
//! Phase 3 Implementation (Months 7-9)
//! Flexible enforcement using SRE-style error budgets

use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use std::time::{Duration, SystemTime};

/// Flexible enforcement using SRE-style error budgets
pub struct ErrorBudgetEnforcer {
    /// Team-specific quality budgets
    budgets: HashMap<TeamId, QualityBudget>,

    /// Historical tracking for trends
    history: TimeSeriesDB,

    /// Progressive enforcement rules
    rules: EnforcementRules,

    /// Configuration
    config: EnforcerConfig,
}

/// Team identifier
pub type TeamId = String;

/// Quality budget for a team
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct QualityBudget {
    /// Complexity points above baseline
    pub complexity_budget: i32,

    /// Temporary SATD allowance
    pub satd_budget: u32,

    /// Coverage floor during refactoring
    pub coverage_floor: f64,

    /// Budget regeneration rate (daily)
    pub regeneration_rate: f64,

    /// Time until strict enforcement
    pub grace_period: Duration,

    /// Current consumption
    pub current_consumption: BudgetConsumption,

    /// Budget started at
    pub started_at: SystemTime,
}

/// Current budget consumption
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct BudgetConsumption {
    /// Complexity points consumed
    pub complexity_used: i32,

    /// SATD issues added
    pub satd_used: u32,

    /// Coverage reduction
    pub coverage_reduction: f64,

    /// Last updated
    pub last_updated: Option<SystemTime>,
}

/// Enforcement decision
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum Decision {
    /// Change approved
    Approved,

    /// Warning issued but not blocked
    Warning(String),

    /// Requires manual approval
    RequiresApproval {
        approvers: Vec<String>,
        reason_required: bool,
    },

    /// Change blocked
    Blocked {
        suggestion: String,
        refactor_targets: Vec<RefactorTarget>,
    },
}

/// Refactoring target suggestion
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct RefactorTarget {
    pub file: String,
    pub complexity: u32,
    pub estimated_reduction: u32,
    pub priority: Priority,
}

/// Priority levels
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum Priority {
    Low,
    Medium,
    High,
    Critical,
}

/// Time series database for historical tracking
pub struct TimeSeriesDB {
    /// Stored metrics by team and timestamp
    data: HashMap<TeamId, Vec<TimeSeries>>,
}

/// Time series entry
#[derive(Debug, Clone)]
struct TimeSeries {
    timestamp: SystemTime,
    metrics: TeamMetrics,
}

/// Team-level metrics
#[derive(Debug, Clone)]
pub struct TeamMetrics {
    avg_complexity: f64,

    total_satd: u32,

    avg_coverage: f64,
    quality_score: f64,
}

/// Enforcement rules configuration
#[derive(Debug, Clone, Default)]
pub struct EnforcementRules {
    /// Approvers by team
    approvers: HashMap<TeamId, Vec<String>>,

    /// Escalation thresholds
    escalation: EscalationPolicy,

    /// Override permissions
    overrides: HashMap<String, OverridePermission>,
}

/// Escalation policy
#[derive(Debug, Clone)]
struct EscalationPolicy {
    warning_threshold: f64,
    approval_threshold: f64,
    block_threshold: f64,
}

impl Default for EscalationPolicy {
    fn default() -> Self {
        Self {
            warning_threshold: 0.5,
            approval_threshold: 0.8,
            block_threshold: 1.0,
        }
    }
}

/// Override permission levels
#[derive(Debug, Clone)]
enum OverridePermission {
    None,

    Limited,

    Full,
}

/// Enforcer configuration
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct EnforcerConfig {
    /// Enable budget enforcement
    pub enabled: bool,

    /// Default budget for new teams
    pub default_budget: QualityBudget,

    /// Budget regeneration interval
    pub regeneration_interval: Duration,

    /// History retention period
    pub history_retention: Duration,
}

impl Default for EnforcerConfig {
    fn default() -> Self {
        Self {
            enabled: true,
            default_budget: QualityBudget::default(),
            regeneration_interval: Duration::from_secs(24 * 60 * 60), // Daily
            history_retention: Duration::from_secs(30 * 24 * 60 * 60), // 30 days
        }
    }
}

impl Default for QualityBudget {
    fn default() -> Self {
        Self {
            complexity_budget: 100,
            satd_budget: 20,
            coverage_floor: 0.7,
            regeneration_rate: 5.0,
            grace_period: Duration::from_secs(7 * 24 * 60 * 60), // 1 week
            current_consumption: BudgetConsumption::default(),
            started_at: SystemTime::now(),
        }
    }
}

/// Diff analysis result
#[derive(Debug, Clone)]
pub struct DiffAnalysis {
    pub complexity_change: i32,
    pub satd_change: i32,
    pub coverage_change: f64,
    pub files_changed: Vec<String>,
}

impl ErrorBudgetEnforcer {
    /// Create a new error budget enforcer
    #[must_use]
    #[provable_contracts_macros::contract("pmat-core.yaml", equation = "check_compliance")]
    pub fn new(config: EnforcerConfig) -> Self {
        Self {
            budgets: HashMap::new(),
            history: TimeSeriesDB::new(),
            rules: EnforcementRules::default(),
            config,
        }
    }

    /// Register a team with a budget
    #[provable_contracts_macros::contract("pmat-core.yaml", equation = "check_compliance")]
    pub fn register_team(&mut self, team_id: TeamId, budget: Option<QualityBudget>) {
        let budget = budget.unwrap_or_else(|| self.config.default_budget.clone());
        self.budgets.insert(team_id, budget);
    }

    /// Check if a commit is allowed
    #[must_use]
    #[provable_contracts_macros::contract("pmat-core.yaml", equation = "check_compliance")]
    pub fn check_commit(&self, team: &TeamId, diff: &DiffAnalysis) -> Decision {
        if !self.config.enabled {
            return Decision::Approved;
        }

        let budget = match self.budgets.get(team) {
            Some(b) => b,
            None => return Decision::Approved, // No budget = no enforcement
        };

        let consumption = self.calculate_consumption(budget, diff);

        match consumption {
            c if c < self.rules.escalation.warning_threshold => Decision::Approved,
            c if c < self.rules.escalation.approval_threshold => Decision::Warning(format!(
                "Approaching budget limit ({:.0}% consumed). Consider refactoring.",
                c * 100.0
            )),
            c if c < self.rules.escalation.block_threshold => Decision::RequiresApproval {
                approvers: self.rules.approvers_for(team),
                reason_required: true,
            },
            _ => Decision::Blocked {
                suggestion: "Budget exceeded. Refactor existing code first.".to_string(),
                refactor_targets: self.suggest_refactor_targets(team),
            },
        }
    }

    /// Update budget consumption
    #[provable_contracts_macros::contract("pmat-core.yaml", equation = "check_compliance")]
    pub fn update_consumption(&mut self, team: &TeamId, diff: &DiffAnalysis) {
        if let Some(budget) = self.budgets.get_mut(team) {
            budget.current_consumption.complexity_used += diff.complexity_change;
            budget.current_consumption.satd_used =
                (budget.current_consumption.satd_used as i32 + diff.satd_change).max(0) as u32;
            budget.current_consumption.coverage_reduction += diff.coverage_change;
            budget.current_consumption.last_updated = Some(SystemTime::now());
        }
    }

    /// Regenerate budgets (called periodically)
    #[provable_contracts_macros::contract("pmat-core.yaml", equation = "check_compliance")]
    pub fn regenerate_budgets(&mut self) {
        let now = SystemTime::now();

        for budget in self.budgets.values_mut() {
            if let Some(last_updated) = budget.current_consumption.last_updated {
                if let Ok(elapsed) = now.duration_since(last_updated) {
                    if elapsed >= self.config.regeneration_interval {
                        // Regenerate budget
                        let days_elapsed = elapsed.as_secs() / (24 * 60 * 60);
                        let regeneration = (budget.regeneration_rate * days_elapsed as f64) as i32;

                        budget.current_consumption.complexity_used =
                            (budget.current_consumption.complexity_used - regeneration).max(0);
                        budget.current_consumption.satd_used =
                            budget.current_consumption.satd_used.saturating_sub(5);
                        budget.current_consumption.coverage_reduction =
                            (budget.current_consumption.coverage_reduction - 0.01).max(0.0);
                        budget.current_consumption.last_updated = Some(now);
                    }
                }
            }
        }
    }

    /// Get budget status for a team
    #[must_use]
    #[provable_contracts_macros::contract("pmat-core.yaml", equation = "check_compliance")]
    pub fn get_budget_status(&self, team: &TeamId) -> Option<BudgetStatus> {
        self.budgets.get(team).map(|budget| {
            let consumption = self.calculate_consumption_percentage(budget);
            BudgetStatus {
                team: team.clone(),
                consumption_percentage: consumption,
                complexity_remaining: budget.complexity_budget
                    - budget.current_consumption.complexity_used,
                satd_remaining: budget.satd_budget - budget.current_consumption.satd_used,
                days_until_regeneration: self.days_until_regeneration(budget),
            }
        })
    }

    /// Calculate consumption as a percentage
    fn calculate_consumption(&self, budget: &QualityBudget, diff: &DiffAnalysis) -> f64 {
        let complexity_ratio =
            f64::from(budget.current_consumption.complexity_used + diff.complexity_change)
                / f64::from(budget.complexity_budget);
        let satd_ratio = f64::from(budget.current_consumption.satd_used as i32 + diff.satd_change)
            / f64::from(budget.satd_budget);
        let coverage_impact = if diff.coverage_change < 0.0 {
            (-diff.coverage_change) / (1.0 - budget.coverage_floor)
        } else {
            0.0
        };

        // Weighted average
        (complexity_ratio * 0.5 + satd_ratio * 0.3 + coverage_impact * 0.2)
            .max(0.0)
            .min(1.0)
    }

    /// Calculate consumption percentage for current state
    fn calculate_consumption_percentage(&self, budget: &QualityBudget) -> f64 {
        let complexity_ratio = f64::from(budget.current_consumption.complexity_used)
            / f64::from(budget.complexity_budget);
        let satd_ratio =
            f64::from(budget.current_consumption.satd_used) / f64::from(budget.satd_budget);

        ((complexity_ratio * 0.6 + satd_ratio * 0.4) * 100.0)
            .max(0.0)
            .min(100.0)
    }

    /// Calculate days until next regeneration
    fn days_until_regeneration(&self, budget: &QualityBudget) -> u32 {
        if let Some(last_updated) = budget.current_consumption.last_updated {
            if let Ok(elapsed) = SystemTime::now().duration_since(last_updated) {
                let remaining = self.config.regeneration_interval.as_secs() - elapsed.as_secs();
                return (remaining / (24 * 60 * 60)) as u32;
            }
        }
        0
    }

    /// Suggest refactoring targets
    fn suggest_refactor_targets(&self, _team: &TeamId) -> Vec<RefactorTarget> {
        // Would integrate with complexity analysis
        vec![
            RefactorTarget {
                file: "src/complex_module.rs".to_string(),
                complexity: 35,
                estimated_reduction: 15,
                priority: Priority::High,
            },
            RefactorTarget {
                file: "src/legacy_code.rs".to_string(),
                complexity: 28,
                estimated_reduction: 10,
                priority: Priority::Medium,
            },
        ]
    }
}

/// Budget status report
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct BudgetStatus {
    pub team: TeamId,
    pub consumption_percentage: f64,
    pub complexity_remaining: i32,
    pub satd_remaining: u32,
    pub days_until_regeneration: u32,
}

impl TimeSeriesDB {
    fn new() -> Self {
        Self {
            data: HashMap::new(),
        }
    }

    #[provable_contracts_macros::contract("pmat-core.yaml", equation = "check_compliance")]
    /// Record.
    pub fn record(&mut self, team: TeamId, metrics: TeamMetrics) {
        let entry = TimeSeries {
            timestamp: SystemTime::now(),
            metrics,
        };

        self.data.entry(team).or_default().push(entry);
    }

    /// Record a measurement for a team
    #[provable_contracts_macros::contract("pmat-core.yaml", equation = "check_compliance")]
    pub fn record_measurement(&mut self, team: &TeamId, timestamp: SystemTime, value: f64) {
        let metrics = TeamMetrics {
            avg_complexity: value,
            total_satd: 0,
            avg_coverage: value,
            quality_score: value,
        };

        let entry = TimeSeries { timestamp, metrics };

        self.data.entry(team.clone()).or_default().push(entry);
    }

    /// Get recent measurements for a team
    #[must_use]
    #[provable_contracts_macros::contract("pmat-core.yaml", equation = "check_compliance")]
    pub fn get_recent_measurements(&self, team: &TeamId, duration: Duration) -> Vec<f64> {
        let now = SystemTime::now();
        let cutoff = now.checked_sub(duration).unwrap_or(SystemTime::UNIX_EPOCH);

        self.data
            .get(team)
            .map(|entries| {
                entries
                    .iter()
                    .filter(|e| e.timestamp >= cutoff)
                    .map(|e| e.metrics.quality_score)
                    .collect()
            })
            .unwrap_or_default()
    }
}

impl EnforcementRules {
    fn approvers_for(&self, team: &TeamId) -> Vec<String> {
        self.approvers
            .get(team)
            .cloned()
            .unwrap_or_else(|| vec!["tech-lead".to_string()])
    }
}

// Tests extracted to enforcement_tests.rs for file health (CB-040).
include!("enforcement_tests.rs");
