//! Dashboard tools — services list, active workflows, audit log query, 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;

// ── dashboard_services ────────────────────────────────────────────────────────

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

#[async_trait]
impl Tool for DashboardServices {
    fn name(&self) -> &'static str { "dashboard_services" }
    fn description(&self) -> &'static str {
        "List platform services and their current status (GET /api/v1/services/list)"
    }
    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!("{}/api/v1/services/list", 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!(
                "services/list returned {}",
                resp.status()
            )));
        }

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

// ── dashboard_workflows ───────────────────────────────────────────────────────

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

#[async_trait]
impl Tool for DashboardWorkflows {
    fn name(&self) -> &'static str { "dashboard_workflows" }
    fn description(&self) -> &'static str {
        "List active (Running + Pending) orchestrator workflows"
    }
    fn schema(&self) -> Value {
        json!({
            "type": "object",
            "properties": {
                "include_completed": {
                    "type": "boolean",
                    "default": false,
                    "description": "Include completed/failed jobs in the result"
                }
            }
        })
    }
    fn category(&self) -> ToolCategory { ToolCategory::Read }

    async fn invoke(&self, params: Value, _ctx: &Context) -> Result<Value, ToolError> {
        let include_all = params["include_completed"].as_bool().unwrap_or(false);
        let tasks = self.client.list_tasks().await?;

        let items: Vec<Value> = tasks
            .into_iter()
            .filter(|t| {
                include_all || matches!(t.status.to_string().as_str(), "Running" | "Pending")
            })
            .map(|t| serde_json::to_value(t).unwrap_or(Value::Null))
            .collect();

        Ok(json!({ "items": items, "total": items.len() }))
    }
}

// ── dashboard_audit ───────────────────────────────────────────────────────────

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

#[async_trait]
impl Tool for DashboardAudit {
    fn name(&self) -> &'static str { "dashboard_audit" }
    fn description(&self) -> &'static str {
        "Query orchestrator audit logs (POST /api/v1/audit/query)"
    }
    fn schema(&self) -> Value {
        json!({
            "type": "object",
            "properties": {
                "limit":      { "type": "integer", "default": 50 },
                "user_id":    { "type": "string" },
                "action":     { "type": "string" },
                "since":      { "type": "string", "description": "ISO-8601 timestamp lower bound" }
            }
        })
    }
    fn category(&self) -> ToolCategory { ToolCategory::Read }

    async fn invoke(&self, params: Value, ctx: &Context) -> Result<Value, ToolError> {
        let mut query = json!({
            "limit": params["limit"].as_i64().unwrap_or(50),
        });

        if let Some(uid) = params["user_id"].as_str() {
            query["user_id"] = json!(uid);
        }
        if let Some(action) = params["action"].as_str() {
            query["action"] = json!(action);
        }
        if let Some(since) = params["since"].as_str() {
            query["since"] = json!(since);
        }

        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!("{}/api/v1/audit/query", ctx.env.orchestrator_url);
        let resp = client
            .post(&url)
            .json(&query)
            .send()
            .await
            .map_err(|e| ToolError::invocation(format!("orchestrator unreachable: {e}")))?;

        if !resp.status().is_success() {
            return Err(ToolError::invocation(format!(
                "audit/query returned {}",
                resp.status()
            )));
        }

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

// ── dashboard_statistics ──────────────────────────────────────────────────────

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

#[async_trait]
impl Tool for DashboardStatistics {
    fn name(&self) -> &'static str { "dashboard_statistics" }
    fn description(&self) -> &'static str {
        "Aggregate dashboard statistics: job counts + system metrics snapshot"
    }
    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 (tasks_result, metrics_result) =
            tokio::join!(self.client.list_tasks(), self.client.system_metrics());

        let tasks = tasks_result.unwrap_or_default();
        let metrics = metrics_result.ok();

        let counts = json!({
            "total":     tasks.len(),
            "pending":   tasks.iter().filter(|t| t.status.to_string() == "Pending").count(),
            "running":   tasks.iter().filter(|t| t.status.to_string() == "Running").count(),
            "completed": tasks.iter().filter(|t| t.status.to_string() == "Completed").count(),
            "failed":    tasks.iter().filter(|t| t.status.to_string() == "Failed").count(),
            "cancelled": tasks.iter().filter(|t| t.status.to_string() == "Cancelled").count(),
        });

        Ok(json!({ "jobs": counts, "metrics": metrics }))
    }
}
