//! Taskserv domain tool — deploy via orchestrator.

use crate::protocol::{ToolCategory, ToolError};
use crate::tool::{Context, Tool};
use async_trait::async_trait;
use serde_json::{Value, json};

// ── taskserv_deploy ───────────────────────────────────────────────────────────

pub struct TaskservDeploy;

impl TaskservDeploy {
    pub fn new() -> Self { Self }
}

impl Default for TaskservDeploy {
    fn default() -> Self { Self::new() }
}

#[async_trait]
impl Tool for TaskservDeploy {
    fn name(&self) -> &'static str { "taskserv_deploy" }
    fn description(&self) -> &'static str {
        "Deploy a taskserv via the orchestrator (create, delete, generate, or check-updates)"
    }
    fn schema(&self) -> Value {
        json!({
            "type": "object",
            "required": ["infra", "settings", "taskserv", "operation"],
            "properties": {
                "infra":     { "type": "string", "description": "Infrastructure name" },
                "settings":  { "type": "string", "description": "Settings NCL path" },
                "taskserv":  { "type": "string", "description": "Taskserv name" },
                "operation": {
                    "type": "string",
                    "enum": ["create", "delete", "generate", "check-updates"],
                    "description": "Operation to perform"
                },
                "check_mode": { "type": "boolean", "default": false },
                "wait":       { "type": "boolean", "default": true }
            }
        })
    }
    fn category(&self) -> ToolCategory { ToolCategory::Mutation }

    async fn invoke(&self, params: Value, ctx: &Context) -> Result<Value, ToolError> {
        let infra = params["infra"]
            .as_str()
            .ok_or_else(|| ToolError::invalid_param("infra", "required string"))?;
        let settings = params["settings"]
            .as_str()
            .ok_or_else(|| ToolError::invalid_param("settings", "required string"))?;
        let taskserv = params["taskserv"]
            .as_str()
            .ok_or_else(|| ToolError::invalid_param("taskserv", "required string"))?;
        let operation = params["operation"]
            .as_str()
            .ok_or_else(|| ToolError::invalid_param("operation", "required string"))?;
        let check_mode = params["check_mode"].as_bool().unwrap_or(false);
        let wait = params["wait"].as_bool().unwrap_or(true);

        // Enqueue via orchestrator POST /workflows/taskserv/create
        let body = json!({
            "infra": infra,
            "settings": settings,
            "taskserv": taskserv,
            "operation": operation,
            "check_mode": check_mode,
            "wait": wait,
        });

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

        let status = resp.status();
        if !status.is_success() {
            return Err(ToolError::invocation(format!(
                "orchestrator returned {status} for taskserv deploy"
            )));
        }

        let result: Value = resp
            .json()
            .await
            .map_err(|e| ToolError::invocation(format!("parse response: {e}")))?;

        Ok(json!({
            "taskserv": taskserv,
            "operation": operation,
            "infra": infra,
            "task_id": result["data"],
            "status": "queued",
        }))
    }
}
