use std::time::Instant;

use tracing::{info_span, warn_span, Span};

/// Span context for task execution tracing
pub struct TaskSpan {
    span: Span,
    start: Instant,
}

impl TaskSpan {
    /// Create a new task execution span
    pub fn new(task_id: &str, agent_id: &str, task_type: &str) -> Self {
        let span = info_span!(
            "task_execution",
            task_id = %task_id,
            agent_id = %agent_id,
            task_type = %task_type,
            duration_ms = tracing::field::Empty,
        );

        Self {
            span,
            start: Instant::now(),
        }
    }

    /// Get reference to span for instrumentation
    pub fn span(&self) -> &Span {
        &self.span
    }

    /// Record span completion with duration
    pub fn complete(self) {
        let duration_ms = self.start.elapsed().as_millis() as u64;
        self.span.record("duration_ms", duration_ms);
    }

    /// Record span completion with error
    pub fn error(self, error_msg: &str) {
        let duration_ms = self.start.elapsed().as_millis() as u64;
        self.span.record("duration_ms", duration_ms);
        tracing::error!(
            parent: &self.span,
            error = %error_msg,
            "Task execution failed"
        );
    }
}

/// Span context for agent operations
pub struct AgentSpan {
    span: Span,
}

impl AgentSpan {
    /// Create span for agent registration
    pub fn registration(agent_id: &str, role: &str) -> Self {
        let span = info_span!(
            "agent_registration",
            agent_id = %agent_id,
            role = %role,
        );

        Self { span }
    }

    /// Create span for agent status update
    pub fn status_update(agent_id: &str, load: f64, available: bool) -> Self {
        let span = info_span!(
            "agent_status_update",
            agent_id = %agent_id,
            load = load,
            available = available,
        );

        Self { span }
    }

    /// Create span for agent heartbeat
    pub fn heartbeat(agent_id: &str) -> Self {
        let span = info_span!(
            "agent_heartbeat",
            agent_id = %agent_id,
        );

        Self { span }
    }

    /// Get reference to span
    pub fn span(&self) -> &Span {
        &self.span
    }
}

/// Span context for routing operations
pub struct RoutingSpan {
    span: Span,
    start: Instant,
}

impl RoutingSpan {
    /// Create span for provider selection
    pub fn provider_selection(task_type: &str, candidates: usize) -> Self {
        let span = info_span!(
            "provider_selection",
            task_type = %task_type,
            candidate_count = candidates,
            selected_provider = tracing::field::Empty,
        );

        Self {
            span,
            start: Instant::now(),
        }
    }

    /// Create span for cost calculation
    pub fn cost_calculation(provider: &str) -> Self {
        let span = info_span!(
            "cost_calculation",
            provider = %provider,
            input_tokens = tracing::field::Empty,
            output_tokens = tracing::field::Empty,
            total_cost = tracing::field::Empty,
        );

        Self {
            span,
            start: Instant::now(),
        }
    }

    /// Record selected provider
    pub fn record_selection(&self, provider: &str) {
        self.span.record("selected_provider", provider);
    }

    /// Record cost details
    pub fn record_cost(&self, input_tokens: u64, output_tokens: u64, cost: f64) {
        self.span.record("input_tokens", input_tokens);
        self.span.record("output_tokens", output_tokens);
        self.span.record("total_cost", cost);
    }

    /// Complete routing operation
    pub fn complete(self) {
        let duration_ms = self.start.elapsed().as_millis() as u64;
        tracing::debug!(
            parent: &self.span,
            duration_ms = duration_ms,
            "Routing decision completed"
        );
    }

    /// Get reference to span
    pub fn span(&self) -> &Span {
        &self.span
    }
}

/// Span context for swarm operations
pub struct SwarmSpan {
    span: Span,
    start: Instant,
}

impl SwarmSpan {
    /// Create span for task assignment
    pub fn task_assignment(task_id: &str, assigned_to: &str) -> Self {
        let span = info_span!(
            "swarm_task_assignment",
            task_id = %task_id,
            assigned_to = %assigned_to,
            duration_ms = tracing::field::Empty,
        );

        Self {
            span,
            start: Instant::now(),
        }
    }

    /// Create span for coalition formation
    pub fn coalition_formation(coalition_id: &str, required_roles: usize) -> Self {
        let span = info_span!(
            "swarm_coalition_formation",
            coalition_id = %coalition_id,
            required_roles = required_roles,
            members_recruited = tracing::field::Empty,
        );

        Self {
            span,
            start: Instant::now(),
        }
    }

    /// Create span for consensus voting
    pub fn consensus_voting(proposal_id: &str, voter_count: usize) -> Self {
        let span = info_span!(
            "swarm_consensus",
            proposal_id = %proposal_id,
            voter_count = voter_count,
            consensus_reached = tracing::field::Empty,
        );

        Self {
            span,
            start: Instant::now(),
        }
    }

    /// Record members recruited for coalition
    pub fn record_members(&self, count: usize) {
        self.span.record("members_recruited", count);
    }

    /// Record consensus result
    pub fn record_consensus(&self, reached: bool) {
        self.span.record("consensus_reached", reached);
    }

    /// Complete swarm operation
    pub fn complete(self) {
        let duration_ms = self.start.elapsed().as_millis() as u64;
        self.span.record("duration_ms", duration_ms);
    }

    /// Get reference to span
    pub fn span(&self) -> &Span {
        &self.span
    }
}

/// Span context for analytics operations
pub struct AnalyticsSpan {
    span: Span,
}

impl AnalyticsSpan {
    /// Create span for event processing
    pub fn event_processing(event_type: &str) -> Self {
        let span = info_span!(
            "analytics_event_processing",
            event_type = %event_type,
            processed = false,
        );

        Self { span }
    }

    /// Create span for alert generation
    pub fn alert_generation(alert_type: &str, severity: &str) -> Self {
        let span = warn_span!(
            "analytics_alert",
            alert_type = %alert_type,
            severity = %severity,
        );

        Self { span }
    }

    /// Create span for aggregation
    pub fn aggregation(window_name: &str) -> Self {
        let span = info_span!(
            "analytics_aggregation",
            window = %window_name,
            aggregated_count = tracing::field::Empty,
        );

        Self { span }
    }

    /// Record aggregation count
    pub fn record_count(&self, count: usize) {
        self.span.record("aggregated_count", count);
    }

    /// Get reference to span
    pub fn span(&self) -> &Span {
        &self.span
    }
}

/// Span context for knowledge graph operations
pub struct KGSpan {
    span: Span,
    start: Instant,
}

impl KGSpan {
    /// Create span for execution recording
    pub fn record_execution(task_id: &str, agent_id: &str) -> Self {
        let span = info_span!(
            "kg_record_execution",
            task_id = %task_id,
            agent_id = %agent_id,
            duration_ms = tracing::field::Empty,
        );

        Self {
            span,
            start: Instant::now(),
        }
    }

    /// Create span for similarity query
    pub fn similarity_query(query_text: &str) -> Self {
        let span = info_span!(
            "kg_similarity_query",
            query_length = query_text.len(),
            matches_found = tracing::field::Empty,
        );

        Self {
            span,
            start: Instant::now(),
        }
    }

    /// Create span for reasoning operation
    pub fn reasoning(operation: &str) -> Self {
        let span = info_span!(
            "kg_reasoning",
            operation = %operation,
            insights_generated = tracing::field::Empty,
        );

        Self {
            span,
            start: Instant::now(),
        }
    }

    /// Record number of insights
    pub fn record_insights(&self, count: usize) {
        self.span.record("insights_generated", count);
    }

    /// Record number of matches
    pub fn record_matches(&self, count: usize) {
        self.span.record("matches_found", count);
    }

    /// Complete operation
    pub fn complete(self) {
        let duration_ms = self.start.elapsed().as_millis() as u64;
        self.span.record("duration_ms", duration_ms);
    }

    /// Get reference to span
    pub fn span(&self) -> &Span {
        &self.span
    }
}

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

    #[test]
    fn test_task_span_creation() {
        let span = TaskSpan::new("task-1", "agent-1", "coding");
        // Span created successfully
        let _ = span.span();
    }

    #[test]
    fn test_agent_span_registration() {
        let span = AgentSpan::registration("agent-1", "developer");
        // Span created successfully
        let _ = span.span();
    }

    #[test]
    fn test_routing_span_selection() {
        let span = RoutingSpan::provider_selection("code_generation", 3);
        span.record_selection("claude");
        // Span should have recorded the provider selection
    }

    #[test]
    fn test_swarm_span_coalition() {
        let span = SwarmSpan::coalition_formation("coal_123", 3);
        span.record_members(3);
        // Span should have recorded member count
    }

    #[test]
    fn test_kg_span_reasoning() {
        let span = KGSpan::reasoning("pattern_detection");
        span.record_insights(5);
        // Span should have recorded insights
    }
}
