use crate::AppState;
use crate::error::CodeLensError;
use crate::tool_defs::deprecated_workflow_alias;
use crate::tool_runtime::{ToolHandler, ToolResult};
use serde_json::{Value, json};

#[cfg(feature = "semantic")]
use crate::protocol::BackendKind;
#[cfg(feature = "semantic")]
use crate::tool_runtime::success_meta;

fn attach_workflow_metadata(workflow: &str, delegated_tool: &str, payload: Value) -> Value {
    let deprecation = deprecated_workflow_alias(workflow);
    match payload {
        Value::Object(mut map) => {
            map.insert("workflow".to_owned(), json!(workflow));
            map.insert("delegated_tool".to_owned(), json!(delegated_tool));
            if let Some((replacement_tool, removal_target)) = deprecation {
                map.insert("deprecated".to_owned(), json!(true));
                map.insert("replacement_tool".to_owned(), json!(replacement_tool));
                map.insert("removal_target".to_owned(), json!(removal_target));
            }
            Value::Object(map)
        }
        other => {
            let mut map = serde_json::Map::new();
            map.insert("workflow".to_owned(), json!(workflow));
            map.insert("delegated_tool".to_owned(), json!(delegated_tool));
            if let Some((replacement_tool, removal_target)) = deprecation {
                map.insert("deprecated".to_owned(), json!(true));
                map.insert("replacement_tool".to_owned(), json!(replacement_tool));
                map.insert("removal_target".to_owned(), json!(removal_target));
            }
            map.insert("result".to_owned(), other);
            Value::Object(map)
        }
    }
}

fn delegate_workflow(
    state: &AppState,
    workflow: &str,
    delegated_tool: &str,
    delegated_args: Value,
    handler: ToolHandler,
) -> ToolResult {
    let (payload, meta) = handler(state, &delegated_args)?;
    Ok((
        attach_workflow_metadata(workflow, delegated_tool, payload),
        meta,
    ))
}

pub fn explore_codebase(state: &AppState, arguments: &Value) -> ToolResult {
    if let Some(query) = arguments.get("query").and_then(|value| value.as_str()) {
        return delegate_workflow(
            state,
            "explore_codebase",
            "get_ranked_context",
            json!({
                "query": query,
                "path": arguments.get("path").and_then(|value| value.as_str()),
                "max_tokens": arguments.get("max_tokens").and_then(|value| value.as_u64()),
                "include_body": arguments.get("include_body").and_then(|value| value.as_bool()),
                "depth": arguments.get("depth").and_then(|value| value.as_u64()),
                "disable_semantic": arguments.get("disable_semantic").and_then(|value| value.as_bool()),
            }),
            crate::tools::symbols::get_ranked_context,
        );
    }

    delegate_workflow(
        state,
        "explore_codebase",
        "onboard_project",
        json!({
            "project_root": arguments.get("project_root").and_then(|value| value.as_str()),
        }),
        crate::tools::composite::onboard_project,
    )
}

pub fn trace_request_path(state: &AppState, arguments: &Value) -> ToolResult {
    let function_name = arguments
        .get("function_name")
        .or_else(|| arguments.get("symbol"))
        .or_else(|| arguments.get("entrypoint"))
        .and_then(|value| value.as_str())
        .ok_or_else(|| CodeLensError::MissingParam("function_name".to_owned()))?;

    delegate_workflow(
        state,
        "trace_request_path",
        "explain_code_flow",
        json!({
            "function_name": function_name,
            "max_depth": arguments.get("max_depth").and_then(|value| value.as_u64()),
            "max_results": arguments.get("max_results").and_then(|value| value.as_u64()),
        }),
        crate::tools::composite::explain_code_flow,
    )
}

pub fn review_architecture(state: &AppState, arguments: &Value) -> ToolResult {
    let include_diagram = arguments
        .get("include_diagram")
        .and_then(|value| value.as_bool())
        .unwrap_or(false);

    if let Some(path) = arguments.get("path").and_then(|value| value.as_str()) {
        if include_diagram {
            return delegate_workflow(
                state,
                "review_architecture",
                "mermaid_module_graph",
                json!({
                    "path": path,
                    "max_nodes": arguments.get("max_nodes").and_then(|value| value.as_u64()),
                }),
                crate::tools::reports::mermaid_module_graph,
            );
        }

        return delegate_workflow(
            state,
            "review_architecture",
            "module_boundary_report",
            json!({ "path": path }),
            crate::tools::reports::module_boundary_report,
        );
    }

    delegate_workflow(
        state,
        "review_architecture",
        "onboard_project",
        json!({
            "project_root": arguments.get("project_root").and_then(|value| value.as_str()),
        }),
        crate::tools::composite::onboard_project,
    )
}

pub fn plan_safe_refactor(state: &AppState, arguments: &Value) -> ToolResult {
    if let (Some(file_path), Some(symbol)) = (
        arguments.get("file_path").and_then(|value| value.as_str()),
        arguments.get("symbol").and_then(|value| value.as_str()),
    ) {
        return delegate_workflow(
            state,
            "plan_safe_refactor",
            "safe_rename_report",
            json!({
                "file_path": file_path,
                "symbol": symbol,
                "new_name": arguments.get("new_name").and_then(|value| value.as_str()),
            }),
            crate::tools::reports::safe_rename_report,
        );
    }

    delegate_workflow(
        state,
        "plan_safe_refactor",
        "refactor_safety_report",
        json!({
            "task": arguments.get("task").and_then(|value| value.as_str()),
            "symbol": arguments.get("symbol").and_then(|value| value.as_str()),
            "path": arguments.get("path").and_then(|value| value.as_str()),
            "file_path": arguments.get("file_path").and_then(|value| value.as_str()),
        }),
        crate::tools::reports::refactor_safety_report,
    )
}

pub fn review_changes(state: &AppState, arguments: &Value) -> ToolResult {
    if arguments
        .get("changed_files")
        .and_then(|v| v.as_array())
        .is_some()
    {
        return delegate_workflow(
            state,
            "review_changes",
            "diff_aware_references",
            arguments.clone(),
            crate::tools::reports::diff_aware_references,
        );
    }

    delegate_workflow(
        state,
        "review_changes",
        "impact_report",
        arguments.clone(),
        crate::tools::reports::impact_report,
    )
}

pub fn diagnose_issues(state: &AppState, arguments: &Value) -> ToolResult {
    if arguments
        .get("path")
        .or_else(|| arguments.get("file_path"))
        .and_then(|v| v.as_str())
        .is_some()
    {
        return delegate_workflow(
            state,
            "diagnose_issues",
            "get_file_diagnostics",
            json!({
                "file_path": arguments.get("file_path")
                    .or_else(|| arguments.get("path"))
                    .and_then(|v| v.as_str()),
            }),
            crate::tools::lsp::get_file_diagnostics,
        );
    }

    delegate_workflow(
        state,
        "diagnose_issues",
        "unresolved_reference_check",
        json!({
            "file_path": arguments.get("file_path").and_then(|v| v.as_str()),
            "symbol": arguments.get("symbol").and_then(|v| v.as_str()),
            "changed_files": arguments.get("changed_files"),
        }),
        crate::tools::reports::unresolved_reference_check,
    )
}

pub fn cleanup_duplicate_logic(state: &AppState, arguments: &Value) -> ToolResult {
    #[cfg(feature = "semantic")]
    {
        let threshold = arguments
            .get("threshold")
            .and_then(|value| value.as_f64())
            .unwrap_or(0.85);
        let max_pairs = arguments
            .get("max_pairs")
            .and_then(|value| value.as_u64())
            .unwrap_or(20) as usize;
        let guard = state.embedding_engine();
        if let Some(engine) = guard.as_ref() {
            let pairs = engine.find_duplicates(threshold, max_pairs)?;
            let payload = json!({
                "threshold": threshold,
                "duplicates": pairs,
                "count": pairs.len(),
            });
            return Ok((
                attach_workflow_metadata(
                    "cleanup_duplicate_logic",
                    "find_code_duplicates",
                    payload,
                ),
                success_meta(BackendKind::Semantic, 0.80),
            ));
        }
    }

    delegate_workflow(
        state,
        "cleanup_duplicate_logic",
        "dead_code_report",
        json!({
            "scope": arguments.get("scope").and_then(|value| value.as_str()),
            "max_results": arguments.get("max_results").and_then(|value| value.as_u64()),
        }),
        crate::tools::reports::dead_code_report,
    )
}
