// Prometheus metrics for swarm coordination
// Phase 5.2: Monitor assignment latency, coalition formation, and consensus
// voting

use std::sync::Arc;

use prometheus::{HistogramVec, IntCounter, IntCounterVec, IntGauge, Registry};

/// Swarm metrics collection for Prometheus monitoring
pub struct SwarmMetrics {
    /// Histogram: Assignment latency in seconds
    pub assignment_latency: HistogramVec,
    /// Counter: Total task assignments
    pub assignments_total: IntCounterVec,
    /// Counter: Failed assignments
    pub assignment_failures: IntCounter,
    /// Gauge: Currently registered agents
    pub agents_registered: IntGauge,
    /// Gauge: Currently available agents
    pub agents_available: IntGauge,
    /// Gauge: Average load across agents
    pub avg_load_factor: prometheus::Gauge,
    /// Counter: Coalition formations
    pub coalitions_formed: IntCounter,
    /// Counter: Consensus votes conducted
    pub consensus_votes: IntCounterVec,
    /// Histogram: Consensus vote duration
    pub vote_latency: HistogramVec,
}

impl SwarmMetrics {
    /// Create new metrics collection (registers with default global registry)
    pub fn new() -> Result<Arc<Self>, prometheus::Error> {
        // Use the default global registry for Prometheus
        let registry = prometheus::default_registry();
        Self::with_registry(registry)
    }

    /// Create metrics with existing registry
    pub fn with_registry(registry: &Registry) -> Result<Arc<Self>, prometheus::Error> {
        let assignment_latency = HistogramVec::new(
            prometheus::HistogramOpts {
                common_opts: prometheus::Opts::new(
                    "vapora_swarm_assignment_latency_seconds",
                    "Assignment latency histogram in seconds",
                ),
                buckets: vec![0.001, 0.005, 0.01, 0.025, 0.05, 0.1, 0.25, 0.5, 1.0],
            },
            &["complexity"],
        )?;
        registry.register(Box::new(assignment_latency.clone()))?;

        let assignments_total = IntCounterVec::new(
            prometheus::Opts::new(
                "vapora_swarm_assignments_total",
                "Total number of task assignments",
            ),
            &["status"],
        )?;
        registry.register(Box::new(assignments_total.clone()))?;

        let assignment_failures = IntCounter::new(
            "vapora_swarm_assignment_failures_total",
            "Total number of failed assignments",
        )?;
        registry.register(Box::new(assignment_failures.clone()))?;

        let agents_registered = IntGauge::new(
            "vapora_swarm_agents_registered",
            "Total registered agents in swarm",
        )?;
        registry.register(Box::new(agents_registered.clone()))?;

        let agents_available = IntGauge::new(
            "vapora_swarm_agents_available",
            "Available agents ready for assignment",
        )?;
        registry.register(Box::new(agents_available.clone()))?;

        let avg_load_factor = prometheus::Gauge::new(
            "vapora_swarm_avg_load_factor",
            "Average load factor across all agents (0.0-1.0)",
        )?;
        registry.register(Box::new(avg_load_factor.clone()))?;

        let coalitions_formed = IntCounter::new(
            "vapora_swarm_coalitions_formed_total",
            "Total number of coalitions formed",
        )?;
        registry.register(Box::new(coalitions_formed.clone()))?;

        let consensus_votes = IntCounterVec::new(
            prometheus::Opts::new(
                "vapora_swarm_consensus_votes_total",
                "Total number of consensus votes conducted",
            ),
            &["outcome"],
        )?;
        registry.register(Box::new(consensus_votes.clone()))?;

        let vote_latency = HistogramVec::new(
            prometheus::HistogramOpts {
                common_opts: prometheus::Opts::new(
                    "vapora_swarm_vote_latency_seconds",
                    "Consensus vote latency histogram",
                ),
                buckets: vec![0.001, 0.01, 0.05, 0.1, 0.5, 1.0],
            },
            &["participant_count"],
        )?;
        registry.register(Box::new(vote_latency.clone()))?;

        Ok(Arc::new(Self {
            assignment_latency,
            assignments_total,
            assignment_failures,
            agents_registered,
            agents_available,
            avg_load_factor,
            coalitions_formed,
            consensus_votes,
            vote_latency,
        }))
    }

    /// Record successful assignment
    pub fn record_assignment_success(&self, latency_secs: f64, complexity: &str) {
        self.assignment_latency
            .with_label_values(&[complexity])
            .observe(latency_secs);
        self.assignments_total.with_label_values(&["success"]).inc();
    }

    /// Record failed assignment
    pub fn record_assignment_failure(&self) {
        self.assignment_failures.inc();
        self.assignments_total.with_label_values(&["failure"]).inc();
    }

    /// Update agent count metrics
    pub fn update_agent_metrics(&self, total: u32, available: u32, avg_load: f64) {
        self.agents_registered.set(total as i64);
        self.agents_available.set(available as i64);
        self.avg_load_factor.set(avg_load);
    }

    /// Record coalition formation
    pub fn record_coalition_formed(&self) {
        self.coalitions_formed.inc();
    }

    /// Record consensus vote outcome
    pub fn record_vote(&self, latency_secs: f64, participants: usize, passed: bool) {
        let outcome = if passed { "passed" } else { "failed" };
        self.consensus_votes.with_label_values(&[outcome]).inc();
        self.vote_latency
            .with_label_values(&[&participants.to_string()])
            .observe(latency_secs);
    }
}

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

    #[test]
    fn test_metrics_all_operations() {
        // Single test that exercises all metrics operations
        // This avoids registry conflicts when running multiple tests
        let metrics = SwarmMetrics::new();
        assert!(metrics.is_ok(), "SwarmMetrics should create successfully");

        let metrics = metrics.unwrap();

        // Test assignment recording
        metrics.record_assignment_success(0.042, "simple");
        metrics.record_assignment_success(0.087, "complex");
        metrics.record_assignment_failure();

        // Test agent metrics
        metrics.update_agent_metrics(10, 8, 0.45);

        // Test coalition formation
        metrics.record_coalition_formed();
        metrics.record_coalition_formed();

        // Test voting
        metrics.record_vote(0.025, 5, true);
        metrics.record_vote(0.015, 3, false);

        // Verify metrics were recorded by gathering them
        let metric_families = prometheus::gather();
        assert!(
            !metric_families.is_empty(),
            "Should have some metrics registered"
        );
    }
}
