use crate::db::Database;
use crate::types::*;
use chrono::Utc;
use tokio::sync::broadcast;
use uuid::Uuid;

/// 工具上下文
pub struct ToolContext {
    pub db: &'static Database,
    pub project_id: Uuid,
    pub tx: broadcast::Sender<String>,
}

/// 工具定义
pub struct Tool {
    pub name: &'static str,
    pub description: &'static str,
    pub parameters: serde_json::Value,
    pub execute: fn(&ToolContext, serde_json::Value) -> Result<ToolResult, ToolError>,
}

/// 工具错误
#[derive(Debug)]
pub struct ToolError(pub String);

impl std::fmt::Display for ToolError {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(f, "{}", self.0)
    }
}

/// 获取所有工具定义
pub fn registry() -> Vec<Tool> {
    vec![
        create_node_tool(),
        create_edge_tool(),
        update_node_tool(),
        delete_node_tool(),
        list_nodes_tool(),
        get_node_detail_tool(),
        list_edges_tool(),
        create_credential_tool(),
        get_analysis_tool(),
        get_timeline_tool(),
        get_health_checks_tool(),
        search_nodes_tool(),
        search_global_tool(),
    ]
}

/// 获取工具定义列表（供 LLM 使用）
pub fn tool_definitions() -> Vec<ToolDefinition> {
    registry()
        .iter()
        .map(|t| ToolDefinition {
            name: t.name.to_string(),
            description: t.description.to_string(),
            parameters: t.parameters.clone(),
        })
        .collect()
}

/// 执行工具调用
pub fn execute_tool(
    ctx: &ToolContext,
    name: &str,
    args: serde_json::Value,
) -> Result<ToolResult, ToolError> {
    let tools = registry();
    let tool = tools
        .iter()
        .find(|t| t.name == name)
        .ok_or_else(|| ToolError(format!("Unknown tool: {}", name)))?;
    (tool.execute)(ctx, args)
}

// ============================================================================
// 工具实现
// ============================================================================

fn create_node_tool() -> Tool {
    Tool {
        name: "create_node",
        description: "创建一个新的图节点。用于记录发现的服务、漏洞、入口点等。",
        parameters: serde_json::json!({
            "type": "object",
            "properties": {
                "label": {
                    "type": "string",
                    "description": "节点标签，如 'Nginx 1.18' 或 'SQL Injection'"
                },
                "node_type": {
                    "type": "string",
                    "enum": ["host", "service", "vulnerability", "privilege_escalation", "credential", "finding", "note", "entry_point", "attack_step", "impact", "objective"],
                    "description": "节点类型"
                },
                "content": {
                    "type": "string",
                    "description": "Markdown 格式的详细内容"
                },
                "verdict": {
                    "type": "string",
                    "enum": ["pending", "success", "failed", "in_progress"],
                    "description": "判定状态"
                },
                "evidence": {
                    "type": "string",
                    "enum": ["confirmed", "unverified", "hypothesis"],
                    "description": "证据状态"
                }
            },
            "required": ["label", "node_type"]
        }),
        execute: |ctx, args| {
            let label = args["label"]
                .as_str()
                .ok_or_else(|| ToolError("Missing label".to_string()))?;
            let node_type_str = args["node_type"]
                .as_str()
                .ok_or_else(|| ToolError("Missing node_type".to_string()))?;
            let node_type: NodeType =
                serde_json::from_value(serde_json::Value::String(node_type_str.to_string()))
                    .map_err(|_| ToolError("Invalid node_type".to_string()))?;

            let mut properties = serde_json::Map::new();
            if let Some(content) = args["content"].as_str() {
                properties.insert(
                    "content".to_string(),
                    serde_json::Value::String(content.to_string()),
                );
            }
            if let Some(verdict) = args["verdict"].as_str() {
                properties.insert(
                    "verdict".to_string(),
                    serde_json::Value::String(verdict.to_string()),
                );
            }
            if let Some(evidence) = args["evidence"].as_str() {
                properties.insert(
                    "evidence".to_string(),
                    serde_json::Value::String(evidence.to_string()),
                );
            }

            let node = Node {
                id: Uuid::new_v4(),
                project_id: ctx.project_id,
                label: label.to_string(),
                node_type,
                position: None,
                properties: serde_json::Value::Object(properties),
                created_at: Utc::now(),
            };

            ctx.db
                .insert_node(&node)
                .map_err(|e| ToolError(e.to_string()))?;

            let _ = ctx.tx.send(format!("graph:{}", ctx.project_id));

            Ok(ToolResult {
                output: serde_json::json!({
                    "id": node.id,
                    "label": node.label,
                    "node_type": node.node_type.to_string(),
                    "created": true
                }),
                side_effects: vec![SideEffect::NodeCreated(NodeResponse::from(node))],
            })
        },
    }
}

fn create_edge_tool() -> Tool {
    Tool {
        name: "create_edge",
        description: "在两个节点之间创建一条有向边。用于表示关系，如'发现'、'利用'、'连接'。",
        parameters: serde_json::json!({
            "type": "object",
            "properties": {
                "source_node_id": {
                    "type": "string",
                    "description": "源节点 ID（UUID 或前缀）"
                },
                "target_node_id": {
                    "type": "string",
                    "description": "目标节点 ID（UUID 或前缀）"
                },
                "label": {
                    "type": "string",
                    "description": "边标签，如 '发现'、'利用'、'连接'"
                }
            },
            "required": ["source_node_id", "target_node_id"]
        }),
        execute: |ctx, args| {
            let source_str = args["source_node_id"]
                .as_str()
                .ok_or_else(|| ToolError("Missing source_node_id".to_string()))?;
            let target_str = args["target_node_id"]
                .as_str()
                .ok_or_else(|| ToolError("Missing target_node_id".to_string()))?;
            let label = args["label"].as_str().unwrap_or("").to_string();

            let source_id = resolve_node_id(ctx.db, ctx.project_id, source_str)?;
            let target_id = resolve_node_id(ctx.db, ctx.project_id, target_str)?;

            let edge = Edge {
                id: Uuid::new_v4(),
                project_id: ctx.project_id,
                source_node_id: source_id,
                target_node_id: target_id,
                label: Some(label),
                created_at: Utc::now(),
            };

            ctx.db
                .insert_edge(&edge)
                .map_err(|e| ToolError(e.to_string()))?;

            let _ = ctx.tx.send(format!("graph:{}", ctx.project_id));

            Ok(ToolResult {
                output: serde_json::json!({
                    "id": edge.id,
                    "source_node_id": edge.source_node_id,
                    "target_node_id": edge.target_node_id,
                    "label": edge.label,
                    "created": true
                }),
                side_effects: vec![SideEffect::EdgeCreated(EdgeResponse::from(edge))],
            })
        },
    }
}

fn update_node_tool() -> Tool {
    Tool {
        name: "update_node",
        description: "更新节点的属性、判定、证据或内容。",
        parameters: serde_json::json!({
            "type": "object",
            "properties": {
                "node_id": {
                    "type": "string",
                    "description": "节点 ID（UUID 或前缀）"
                },
                "label": {
                    "type": "string",
                    "description": "新标签"
                },
                "content": {
                    "type": "string",
                    "description": "新内容（替换）"
                },
                "verdict": {
                    "type": "string",
                    "enum": ["pending", "success", "failed", "in_progress"],
                    "description": "新判定状态"
                },
                "evidence": {
                    "type": "string",
                    "enum": ["confirmed", "unverified", "hypothesis"],
                    "description": "新证据状态"
                }
            },
            "required": ["node_id"]
        }),
        execute: |ctx, args| {
            let node_id_str = args["node_id"]
                .as_str()
                .ok_or_else(|| ToolError("Missing node_id".to_string()))?;
            let node_id = resolve_node_id(ctx.db, ctx.project_id, node_id_str)?;

            let mut req = UpdateNodeRequest {
                label: None,
                node_type: None,
                position: None,
                properties: None,
                content: None,
                append_content: None,
                set_verdict: None,
                set_evidence: None,
            };

            if let Some(label) = args["label"].as_str() {
                req.label = Some(label.to_string());
            }
            if let Some(content) = args["content"].as_str() {
                req.content = Some(content.to_string());
            }
            if let Some(verdict) = args["verdict"].as_str() {
                req.set_verdict = Some(verdict.to_string());
            }
            if let Some(evidence) = args["evidence"].as_str() {
                req.set_evidence = Some(evidence.to_string());
            }

            let node = ctx
                .db
                .update_node(ctx.project_id, node_id, &req)
                .map_err(|e| ToolError(e.to_string()))?
                .ok_or_else(|| ToolError("Node not found".to_string()))?;

            let _ = ctx.tx.send(format!("graph:{}", ctx.project_id));

            Ok(ToolResult {
                output: serde_json::json!({
                    "id": node.id,
                    "label": node.label,
                    "updated": true
                }),
                side_effects: vec![],
            })
        },
    }
}

fn delete_node_tool() -> Tool {
    Tool {
        name: "delete_node",
        description: "删除一个节点。如果节点有边连接，边也会被删除。",
        parameters: serde_json::json!({
            "type": "object",
            "properties": {
                "node_id": {
                    "type": "string",
                    "description": "节点 ID（UUID 或前缀）"
                }
            },
            "required": ["node_id"]
        }),
        execute: |ctx, args| {
            let node_id_str = args["node_id"]
                .as_str()
                .ok_or_else(|| ToolError("Missing node_id".to_string()))?;
            let node_id = resolve_node_id(ctx.db, ctx.project_id, node_id_str)?;

            ctx.db
                .delete_node(ctx.project_id, node_id)
                .map_err(|e| ToolError(e.to_string()))?;

            let _ = ctx.tx.send(format!("graph:{}", ctx.project_id));

            Ok(ToolResult {
                output: serde_json::json!({
                    "id": node_id,
                    "deleted": true
                }),
                side_effects: vec![],
            })
        },
    }
}

fn list_nodes_tool() -> Tool {
    Tool {
        name: "list_nodes",
        description: "列出项目节点，可选择按类型、判定、证据筛选。",
        parameters: serde_json::json!({
            "type": "object",
            "properties": {
                "node_type": {
                    "type": "string",
                    "description": "按节点类型筛选"
                },
                "verdict": {
                    "type": "string",
                    "description": "按判定状态筛选"
                },
                "evidence": {
                    "type": "string",
                    "description": "按证据状态筛选"
                }
            }
        }),
        execute: |ctx, args| {
            let node_type = args["node_type"].as_str().map(|s| s.to_string());
            let verdict = args["verdict"].as_str().map(|s| s.to_string());
            let evidence = args["evidence"].as_str().map(|s| s.to_string());

            let nodes = ctx
                .db
                .list_nodes(ctx.project_id)
                .map_err(|e| ToolError(e.to_string()))?;

            // 手动过滤
            let filtered: Vec<&Node> = nodes
                .iter()
                .filter(|n| {
                    if let Some(ref nt) = node_type {
                        if n.node_type.to_string() != *nt {
                            return false;
                        }
                    }
                    if let Some(ref v) = verdict {
                        let node_verdict = n
                            .properties
                            .get("verdict")
                            .and_then(|v| v.as_str())
                            .unwrap_or("");
                        if node_verdict != *v {
                            return false;
                        }
                    }
                    if let Some(ref e) = evidence {
                        let node_evidence = n
                            .properties
                            .get("evidence")
                            .and_then(|v| v.as_str())
                            .unwrap_or("");
                        if node_evidence != *e {
                            return false;
                        }
                    }
                    true
                })
                .collect();

            let summaries: Vec<NodeSummary> =
                filtered.iter().map(|n| NodeSummary::from(*n)).collect();

            Ok(ToolResult {
                output: serde_json::json!({
                    "count": summaries.len(),
                    "nodes": summaries
                }),
                side_effects: vec![],
            })
        },
    }
}

fn get_node_detail_tool() -> Tool {
    Tool {
        name: "get_node_detail",
        description: "获取单个节点的完整详情，包括内容。",
        parameters: serde_json::json!({
            "type": "object",
            "properties": {
                "node_id": {
                    "type": "string",
                    "description": "节点 ID（UUID 或前缀）"
                }
            },
            "required": ["node_id"]
        }),
        execute: |ctx, args| {
            let node_id_str = args["node_id"]
                .as_str()
                .ok_or_else(|| ToolError("Missing node_id".to_string()))?;
            let node_id = resolve_node_id(ctx.db, ctx.project_id, node_id_str)?;

            let node = ctx
                .db
                .get_node(ctx.project_id, node_id)
                .map_err(|e| ToolError(e.to_string()))?
                .ok_or_else(|| ToolError("Node not found".to_string()))?;

            Ok(ToolResult {
                output: serde_json::json!({
                    "id": node.id,
                    "label": node.label,
                    "node_type": node.node_type.to_string(),
                    "properties": node.properties,
                    "created_at": node.created_at
                }),
                side_effects: vec![],
            })
        },
    }
}

fn list_edges_tool() -> Tool {
    Tool {
        name: "list_edges",
        description: "列出项目的所有边。",
        parameters: serde_json::json!({
            "type": "object",
            "properties": {}
        }),
        execute: |ctx, _args| {
            let edges = ctx
                .db
                .list_edges(ctx.project_id)
                .map_err(|e| ToolError(e.to_string()))?;

            let responses: Vec<EdgeResponse> = edges.into_iter().map(EdgeResponse::from).collect();

            Ok(ToolResult {
                output: serde_json::json!({
                    "count": responses.len(),
                    "edges": responses
                }),
                side_effects: vec![],
            })
        },
    }
}

fn create_credential_tool() -> Tool {
    Tool {
        name: "create_credential",
        description: "创建一个凭据记录。",
        parameters: serde_json::json!({
            "type": "object",
            "properties": {
                "label": {
                    "type": "string",
                    "description": "凭据标签"
                },
                "credential_type": {
                    "type": "string",
                    "enum": ["password", "ssh_key", "aksk", "token"],
                    "description": "凭据类型"
                },
                "username": {
                    "type": "string",
                    "description": "用户名"
                },
                "secret": {
                    "type": "string",
                    "description": "密码或密钥"
                },
                "source_node_id": {
                    "type": "string",
                    "description": "来源节点 ID"
                },
                "status": {
                    "type": "string",
                    "enum": ["unverified", "verified", "expired"],
                    "description": "状态"
                }
            },
            "required": ["label", "credential_type", "secret"]
        }),
        execute: |ctx, args| {
            let label = args["label"]
                .as_str()
                .ok_or_else(|| ToolError("Missing label".to_string()))?;
            let cred_type_str = args["credential_type"]
                .as_str()
                .ok_or_else(|| ToolError("Missing credential_type".to_string()))?;
            let secret = args["secret"]
                .as_str()
                .ok_or_else(|| ToolError("Missing secret".to_string()))?;

            let credential_type: CredentialType =
                serde_json::from_value(serde_json::Value::String(cred_type_str.to_string()))
                    .map_err(|_| ToolError("Invalid credential_type".to_string()))?;

            let username = args["username"].as_str().map(|s| s.to_string());
            let status_str = args["status"].as_str().unwrap_or("unverified");
            let status: CredentialStatus =
                serde_json::from_value(serde_json::Value::String(status_str.to_string()))
                    .map_err(|_| ToolError("Invalid status".to_string()))?;

            let source_node_id = if let Some(id_str) = args["source_node_id"].as_str() {
                Some(resolve_node_id(ctx.db, ctx.project_id, id_str)?)
            } else {
                None
            };

            let req = CreateCredentialRequest {
                label: label.to_string(),
                credential_type,
                username,
                secret: secret.to_string(),
                source_node_id,
                status,
                notes: None,
            };

            let cred = ctx
                .db
                .create_credential(ctx.project_id, &req)
                .map_err(|e| ToolError(e.to_string()))?;

            Ok(ToolResult {
                output: serde_json::json!({
                    "id": cred.id,
                    "label": cred.label,
                    "credential_type": cred.credential_type.to_string(),
                    "created": true
                }),
                side_effects: vec![SideEffect::CredentialCreated(CredentialResponse::from(
                    cred,
                ))],
            })
        },
    }
}

fn get_analysis_tool() -> Tool {
    Tool {
        name: "get_analysis",
        description: "运行 BFS 分析，获取攻击路径和健康检查。",
        parameters: serde_json::json!({
            "type": "object",
            "properties": {}
        }),
        execute: |ctx, _args| {
            let analysis = ctx
                .db
                .analyze(ctx.project_id)
                .map_err(|e| ToolError(e.to_string()))?;

            Ok(ToolResult {
                output: serde_json::json!({
                    "attack_paths": analysis.attack_paths,
                    "health_checks": analysis.health_checks,
                    "unexploited_vulns": analysis.unexploited_vulns,
                    "dead_ends": analysis.dead_ends,
                    "isolated_nodes": analysis.isolated_nodes
                }),
                side_effects: vec![],
            })
        },
    }
}

fn get_timeline_tool() -> Tool {
    Tool {
        name: "get_timeline",
        description: "按时间顺序排列的节点+边列表。",
        parameters: serde_json::json!({
            "type": "object",
            "properties": {}
        }),
        execute: |ctx, _args| {
            let timeline = ctx
                .db
                .project_timeline(ctx.project_id)
                .map_err(|e| ToolError(e.to_string()))?;

            Ok(ToolResult {
                output: serde_json::json!({
                    "count": timeline.len(),
                    "timeline": timeline
                }),
                side_effects: vec![],
            })
        },
    }
}

fn get_health_checks_tool() -> Tool {
    Tool {
        name: "get_health_checks",
        description: "4个确定性检查：孤立入口点、悬挂利用、断链、不可达节点。",
        parameters: serde_json::json!({
            "type": "object",
            "properties": {}
        }),
        execute: |ctx, _args| {
            let analysis = ctx
                .db
                .analyze(ctx.project_id)
                .map_err(|e| ToolError(e.to_string()))?;

            Ok(ToolResult {
                output: serde_json::json!({
                    "health_checks": analysis.health_checks
                }),
                side_effects: vec![],
            })
        },
    }
}

fn search_nodes_tool() -> Tool {
    Tool {
        name: "search_nodes",
        description: "项目内搜索，类似 LIKE。",
        parameters: serde_json::json!({
            "type": "object",
            "properties": {
                "query": {
                    "type": "string",
                    "description": "搜索关键词"
                }
            },
            "required": ["query"]
        }),
        execute: |ctx, args| {
            let query = args["query"]
                .as_str()
                .ok_or_else(|| ToolError("Missing query".to_string()))?;

            let nodes = ctx
                .db
                .search_nodes(ctx.project_id, query)
                .map_err(|e| ToolError(e.to_string()))?;

            let summaries: Vec<NodeSummary> = nodes.iter().map(|n| NodeSummary::from(n)).collect();

            Ok(ToolResult {
                output: serde_json::json!({
                    "count": summaries.len(),
                    "nodes": summaries
                }),
                side_effects: vec![],
            })
        },
    }
}

fn search_global_tool() -> Tool {
    Tool {
        name: "search_global",
        description: "跨项目搜索。",
        parameters: serde_json::json!({
            "type": "object",
            "properties": {
                "query": {
                    "type": "string",
                    "description": "搜索关键词"
                }
            },
            "required": ["query"]
        }),
        execute: |ctx, args| {
            let query = args["query"]
                .as_str()
                .ok_or_else(|| ToolError("Missing query".to_string()))?;

            let results = ctx
                .db
                .global_search(query)
                .map_err(|e| ToolError(e.to_string()))?;

            Ok(ToolResult {
                output: serde_json::json!({
                    "count": results.len(),
                    "results": results
                }),
                side_effects: vec![],
            })
        },
    }
}

// ============================================================================
// 辅助函数
// ============================================================================

fn resolve_node_id(db: &Database, project_id: Uuid, raw: &str) -> Result<Uuid, ToolError> {
    // 尝试完整 UUID
    if let Ok(id) = Uuid::parse_str(raw) {
        return Ok(id);
    }
    // 前缀匹配
    if raw.len() < 6 {
        return Err(ToolError(
            "Node ID must be at least 6 characters".to_string(),
        ));
    }
    let nodes = db
        .list_nodes(project_id)
        .map_err(|e| ToolError(e.to_string()))?;
    let matches: Vec<&Node> = nodes
        .iter()
        .filter(|n| n.id.to_string().starts_with(raw))
        .collect();
    match matches.len() {
        0 => Err(ToolError(format!("No node found with prefix: {}", raw))),
        1 => Ok(matches[0].id),
        _ => Err(ToolError(format!("Multiple nodes match prefix: {}", raw))),
    }
}
