//! 脚本节点 — 执行 Shell 脚本或自定义命令

use async_trait::async_trait;
use df_workflow::node::{Node, NodeContext, NodeOutput, NodeResult, NodeSchema};

/// 脚本节点
pub struct ScriptNode;

#[async_trait]
impl Node for ScriptNode {
    async fn execute(&self, ctx: NodeContext) -> NodeResult {
        tracing::info!("ScriptNode 执行: {}", ctx.node_id);

        // 从 config 解析 command（必填）
        let command = ctx
            .config
            .get("command")
            .and_then(|v| v.as_str())
            .ok_or_else(|| anyhow::anyhow!("ScriptNode 缺少必填参数: command"))?;

        // 解析可选参数
        let timeout_secs = ctx
            .config
            .get("timeout_secs")
            .and_then(|v| v.as_u64());

        let working_dir = ctx
            .config
            .get("working_dir")
            .and_then(|v| v.as_str())
            .map(|s| s.to_string());

        // 构建请求并执行
        let request = df_execute::shell::ShellRequest {
            command: command.to_string(),
            working_dir,
            env: std::collections::HashMap::new(),
            timeout_secs,
            shell_type: Default::default(),
        };

        tracing::info!("ScriptNode 执行命令: {}", command);
        let result = df_execute::shell::execute(request).await?;

        // 非零退出码视为执行失败
        let exit_code = result.exit_code.unwrap_or(-1);
        if exit_code != 0 {
            anyhow::bail!(
                "脚本执行失败 (exit_code={}): {}",
                exit_code,
                result.stderr.trim()
            );
        }

        tracing::info!(
            "ScriptNode 完成: 耗时 {}ms, exit_code={}",
            result.duration_ms,
            exit_code
        );

        Ok(NodeOutput::from_value(serde_json::json!({
            "stdout": result.stdout,
            "stderr": result.stderr,
            "exit_code": exit_code,
            "duration_ms": result.duration_ms,
        })))
    }

    fn schema(&self) -> NodeSchema {
        NodeSchema {
            params: serde_json::json!({
                "type": "object",
                "properties": {
                    "command": { "type": "string" },
                    "timeout_secs": { "type": "integer" },
                    "working_dir": { "type": "string" }
                },
                "required": ["command"]
            }),
            output: serde_json::json!({
                "type": "object",
                "properties": {
                    "stdout": { "type": "string" },
                    "stderr": { "type": "string" },
                    "exit_code": { "type": "integer" },
                    "duration_ms": { "type": "integer" }
                }
            }),
        }
    }

    fn node_type(&self) -> &str {
        "script"
    }
}
