//! Monitoring tools — system metrics, aggregate health, statistics.

use crate::protocol::{ToolCategory, ToolError};
use crate::sources::OrchestratorClient;
use crate::tool::{Context, Tool};
use async_trait::async_trait;
use serde_json::{Value, json};
use std::sync::Arc;

// ── monitoring_metrics ────────────────────────────────────────────────────────

pub struct MonitoringMetrics {
    client: Arc<OrchestratorClient>,
}
impl MonitoringMetrics {
    pub fn new(client: Arc<OrchestratorClient>) -> Self { Self { client } }
}

#[async_trait]
impl Tool for MonitoringMetrics {
    fn name(&self) -> &'static str { "monitoring_metrics" }
    fn description(&self) -> &'static str {
        "Retrieve system metrics from the orchestrator (GET /state/system/metrics)"
    }
    fn schema(&self) -> Value {
        json!({ "type": "object", "properties": {}, "additionalProperties": false })
    }
    fn category(&self) -> ToolCategory { ToolCategory::Read }

    async fn invoke(&self, _params: Value, _ctx: &Context) -> Result<Value, ToolError> {
        self.client.system_metrics().await
    }
}

// ── monitoring_health ─────────────────────────────────────────────────────────

pub struct MonitoringHealth {
    client: Arc<OrchestratorClient>,
}
impl MonitoringHealth {
    pub fn new(client: Arc<OrchestratorClient>) -> Self { Self { client } }
}

#[async_trait]
impl Tool for MonitoringHealth {
    fn name(&self) -> &'static str { "monitoring_health" }
    fn description(&self) -> &'static str {
        "Aggregate health check: orchestrator + job queue state"
    }
    fn schema(&self) -> Value {
        json!({ "type": "object", "properties": {}, "additionalProperties": false })
    }
    fn category(&self) -> ToolCategory { ToolCategory::Read }

    async fn invoke(&self, _params: Value, _ctx: &Context) -> Result<Value, ToolError> {
        let orch_ok = self.client.health().await.is_ok();
        let detail = if orch_ok {
            self.client.system_health().await.ok()
        } else {
            None
        };

        let tasks = if orch_ok {
            self.client.list_tasks().await.unwrap_or_default()
        } else {
            Vec::new()
        };

        let pending = tasks.iter().filter(|t| t.status.to_string() == "Pending").count();
        let running = tasks.iter().filter(|t| t.status.to_string() == "Running").count();
        let failed  = tasks.iter().filter(|t| t.status.to_string() == "Failed").count();

        Ok(json!({
            "healthy": orch_ok && failed == 0,
            "orchestrator": {
                "reachable": orch_ok,
                "detail": detail,
            },
            "jobs": {
                "pending": pending,
                "running": running,
                "failed":  failed,
            },
        }))
    }
}

// ── monitoring_statistics ─────────────────────────────────────────────────────

pub struct MonitoringStatistics;
impl MonitoringStatistics {
    pub fn new() -> Self { Self }
}
impl Default for MonitoringStatistics {
    fn default() -> Self { Self::new() }
}

#[async_trait]
impl Tool for MonitoringStatistics {
    fn name(&self) -> &'static str { "monitoring_statistics" }
    fn description(&self) -> &'static str {
        "Retrieve orchestrator statistics (GET /state/statistics)"
    }
    fn schema(&self) -> Value {
        json!({ "type": "object", "properties": {}, "additionalProperties": false })
    }
    fn category(&self) -> ToolCategory { ToolCategory::Read }

    async fn invoke(&self, _params: Value, ctx: &Context) -> Result<Value, ToolError> {
        let client = reqwest::Client::builder()
            .timeout(std::time::Duration::from_secs(10))
            .build()
            .map_err(|e| ToolError::invocation(format!("http client: {e}")))?;

        let url = format!("{}/state/statistics", ctx.env.orchestrator_url);
        let resp = client
            .get(&url)
            .send()
            .await
            .map_err(|e| ToolError::invocation(format!("orchestrator unreachable: {e}")))?;

        if !resp.status().is_success() {
            return Err(ToolError::invocation(format!(
                "orchestrator returned {} for statistics",
                resp.status()
            )));
        }

        resp.json::<Value>()
            .await
            .map_err(|e| ToolError::invocation(format!("parse response: {e}")))
    }
}
