#[cfg(test)]
use std::collections::BTreeSet;

#[cfg(test)]
use serde_json::json;
use serde_json::{Map, Value};

#[cfg(test)]
use super::published_tool_definitions;

const UNSUPPORTED_TOOL_SCHEMA_KEYWORDS: [&str; 3] = ["oneOf", "allOf", "anyOf"];

pub(super) fn sanitize_tool_input_schema(schema: &Value) -> Value {
    match schema {
        Value::Object(map) => {
            let mut cleaned = Map::new();
            let mut removed_keywords = Vec::<String>::new();
            for (key, value) in map {
                if UNSUPPORTED_TOOL_SCHEMA_KEYWORDS.contains(&key.as_str()) {
                    removed_keywords.push(key.clone());
                    continue;
                }
                cleaned.insert(key.clone(), sanitize_tool_input_schema(value));
            }
            if !removed_keywords.is_empty() {
                let joined = removed_keywords.join(", ");
                let note = format!(
                    "Compatibility note: runtime validation is stricter than shown here because {joined} is omitted for clients that reject those JSON Schema keywords."
                );
                append_schema_description_note(&mut cleaned, &note);
            }
            Value::Object(cleaned)
        }
        Value::Array(items) => Value::Array(items.iter().map(sanitize_tool_input_schema).collect()),
        _ => schema.clone(),
    }
}

fn append_schema_description_note(schema: &mut Map<String, Value>, note: &str) {
    let merged = match schema.get("description").and_then(Value::as_str) {
        Some(existing) if existing.contains(note) => existing.to_string(),
        Some(existing) if !existing.trim().is_empty() => format!("{existing} {note}"),
        _ => note.to_string(),
    };
    schema.insert("description".to_string(), Value::String(merged));
}

#[cfg(test)]
pub(super) fn collect_unsupported_schema_keywords(schema: &Value, found: &mut BTreeSet<String>) {
    match schema {
        Value::Object(map) => {
            for (key, value) in map {
                if UNSUPPORTED_TOOL_SCHEMA_KEYWORDS.contains(&key.as_str()) {
                    found.insert(key.clone());
                }
                collect_unsupported_schema_keywords(value, found);
            }
        }
        Value::Array(items) => {
            for item in items {
                collect_unsupported_schema_keywords(item, found);
            }
        }
        _ => {}
    }
}

#[cfg(test)]
pub(super) fn tool_schema_compatibility_summary() -> Value {
    let defs = published_tool_definitions();
    let mut incompatible_tools = Vec::<Value>::new();
    for tool in &defs {
        let mut keywords = BTreeSet::new();
        collect_unsupported_schema_keywords(&tool.input_schema, &mut keywords);
        if !keywords.is_empty() {
            incompatible_tools.push(json!({
                "name": tool.name,
                "keywords": keywords.into_iter().collect::<Vec<_>>()
            }));
        }
    }

    json!({
        "transport_profile": "flat_json_schema_without_combinators",
        "blocked_keywords": UNSUPPORTED_TOOL_SCHEMA_KEYWORDS,
        "tool_count": defs.len(),
        "compatible": incompatible_tools.is_empty(),
        "incompatible_tools": incompatible_tools
    })
}
