//! Tool definitions advertised to the daemon via
//! `PluginAdapter::declare_tools`. Each entry mirrors a
//! `[[plugin.tools.outbound]]` block in `nexo-plugin.toml` — same
//! name + description + schema. The host validates incoming
//! `tool.invoke` args against the same schemas it loaded from the
//! manifest, but the adapter wants these in code form too so it
//! can answer `initialize` without re-parsing the manifest.

use nexo_microapp_sdk::plugin::ToolDef;
use serde_json::json;

/// All 5 outbound voice tools. Order is stable (`voice_inbound_transform`
/// first so transform-discovery debug output reads in pipeline
/// order).
pub fn voice_tool_defs() -> Vec<ToolDef> {
    vec![
        ToolDef {
            name: "voice_inbound_transform".to_string(),
            description: "Returns a system_addendum teaching the LLM voice-mode SSML markers when the per-conversation toggle is active. Hot path: fires on every inbound turn; SQLite store lookup + static string return, < 2ms typical.".to_string(),
            input_schema: json!({
                "type": "object",
                "additionalProperties": false,
                "required": ["context"],
                "properties": {
                    "context": {
                        "type": "object",
                        "required": ["conversation_key"],
                        "properties": {
                            "conversation_key": { "type": "string", "minLength": 1 },
                            "language": { "type": "string" },
                            "inbound_kind": { "type": "string", "enum": ["text", "audio", "image", "other"] }
                        }
                    }
                }
            }),
        },
        ToolDef {
            name: "voice_language_style_inbound_transform".to_string(),
            description: "Returns dialect/vocabulary guidance addendum for the conversation's locale (voseo es-AR, vosotros es-ES, British en-GB, Spanglish es-US, ...). Independent of the voice-mode toggle — fires on every inbound turn to lock the LLM into the configured register.".to_string(),
            input_schema: json!({
                "type": "object",
                "additionalProperties": false,
                "required": ["context"],
                "properties": {
                    "context": {
                        "type": "object",
                        "properties": {
                            "conversation_key": { "type": "string" },
                            "language": { "type": "string" }
                        }
                    }
                }
            }),
        },
        ToolDef {
            name: "voice_reply_transform".to_string(),
            description: "Transforms an outbound Text reply into a VoiceNote (OGG/Opus PTT) when the conversation toggle is ON. Falls back to clean text (markers stripped) if every provider fails.".to_string(),
            input_schema: json!({
                "type": "object",
                "additionalProperties": false,
                "required": ["context", "reply"],
                "properties": {
                    "context": {
                        "type": "object",
                        "required": ["conversation_key"],
                        "properties": {
                            "conversation_key": { "type": "string", "minLength": 1 },
                            "language": { "type": "string" },
                            "agent_id": { "type": "string" }
                        }
                    },
                    "reply": {}
                }
            }),
        },
        ToolDef {
            name: "voice_list_voices".to_string(),
            description: "Returns the curated voice catalog filtered by optional locale_prefix / provider / cost_class. Static index from voices.toml — no provider live call.".to_string(),
            input_schema: json!({
                "type": "object",
                "additionalProperties": false,
                "properties": {
                    "locale_prefix": { "type": "string" },
                    "provider":      { "type": "string" },
                    "cost_class":    { "type": "string", "enum": ["free", "cheap", "premium"] }
                }
            }),
        },
        ToolDef {
            name: "voice_set".to_string(),
            description: "Turn voice mode ON for a conversation and persist the chosen voice (+ optional speed/pitch overrides). Gated by model_overrides_policy.allow_toggle when called by the LLM.".to_string(),
            input_schema: json!({
                "type": "object",
                "additionalProperties": false,
                "required": ["context", "voice_id"],
                "properties": {
                    "context": {
                        "type": "object",
                        "required": ["conversation_key"],
                        "properties": {
                            "conversation_key": { "type": "string", "minLength": 1 },
                            "source": { "type": "string", "enum": ["ui", "llm", "admin"] }
                        }
                    },
                    "voice_id":  { "type": "string", "minLength": 1 },
                    "speed":     { "type": "number", "minimum": 0.5, "maximum": 2.0 },
                    "pitch_pct": { "type": "integer", "minimum": -50, "maximum": 50 }
                }
            }),
        },
        ToolDef {
            name: "voice_unset".to_string(),
            description: "Turn voice mode OFF for a conversation. Gated by model_overrides_policy.allow_toggle when called by the LLM.".to_string(),
            input_schema: json!({
                "type": "object",
                "additionalProperties": false,
                "required": ["context"],
                "properties": {
                    "context": {
                        "type": "object",
                        "required": ["conversation_key"],
                        "properties": {
                            "conversation_key": { "type": "string", "minLength": 1 },
                            "source": { "type": "string", "enum": ["ui", "llm", "admin"] }
                        }
                    }
                }
            }),
        },
    ]
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn six_defs_in_pipeline_order() {
        let defs = voice_tool_defs();
        assert_eq!(defs.len(), 6);
        assert_eq!(defs[0].name, "voice_inbound_transform");
        assert_eq!(defs[1].name, "voice_language_style_inbound_transform");
        assert_eq!(defs[2].name, "voice_reply_transform");
        assert_eq!(defs[3].name, "voice_list_voices");
        assert_eq!(defs[4].name, "voice_set");
        assert_eq!(defs[5].name, "voice_unset");
    }

    #[test]
    fn every_def_has_object_schema() {
        for def in voice_tool_defs() {
            assert_eq!(
                def.input_schema.get("type").and_then(|v| v.as_str()),
                Some("object"),
                "def `{}` schema must be object",
                def.name
            );
        }
    }

    #[test]
    fn names_all_start_with_voice_prefix() {
        for def in voice_tool_defs() {
            assert!(
                def.name.starts_with("voice_"),
                "tool `{}` must start with `voice_`",
                def.name
            );
        }
    }
}
