use std::fmt;

use serde::{Deserialize, Serialize};
use vapora_shared::AgentDefinition;

/// Complete specification for a capability package.
///
/// Carries everything needed to instantiate a domain-optimized agent:
/// system prompt, tool list, LLM preferences, and task classification hints.
/// Registry-stored capabilities expose this via [`Capability::spec()`].
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct CapabilitySpec {
    /// Unique kebab-case identifier (e.g., `"code-reviewer"`).
    pub id: String,
    /// Human-readable name shown in UIs and logs.
    pub display_name: String,
    /// Brief description of what the capability does.
    pub description: String,
    /// Agent role name used by the coordinator for task routing (e.g.,
    /// `"code_reviewer"`).
    pub agent_role: String,
    /// Task-type strings the coordinator uses to select this agent.
    ///
    /// These strings are matched by [`vapora_agents::coordinator`]'s
    /// `extract_task_type` heuristic — they should overlap with keywords
    /// present in task titles/descriptions.
    pub task_types: Vec<String>,
    /// Domain-optimized system prompt injected before every task execution.
    pub system_prompt: String,
    /// MCP tool IDs activated for this capability.
    ///
    /// These are semantic names matching what `vapora-mcp-server` exposes.
    pub mcp_tools: Vec<String>,
    /// Preferred LLM provider name (e.g., `"claude"`, `"openai"`).
    pub preferred_provider: String,
    /// Preferred model within the provider (e.g., `"claude-opus-4-6"`).
    pub preferred_model: String,
    /// Maximum output tokens for this domain's tasks.
    pub max_tokens: u32,
    /// Sampling temperature — lower values produce more deterministic output.
    pub temperature: f32,
    /// Assignment priority in range 0–100.
    pub priority: u32,
    /// Whether multiple instances of this capability can run concurrently.
    pub parallelizable: bool,
}

impl CapabilitySpec {
    /// Override the system prompt (e.g., from a TOML config file).
    pub fn with_system_prompt(mut self, prompt: impl Into<String>) -> Self {
        self.system_prompt = prompt.into();
        self
    }

    /// Override the preferred model.
    pub fn with_model(mut self, model: impl Into<String>) -> Self {
        self.preferred_model = model.into();
        self
    }

    /// Override the preferred provider.
    pub fn with_provider(mut self, provider: impl Into<String>) -> Self {
        self.preferred_provider = provider.into();
        self
    }

    /// Override max tokens.
    pub fn with_max_tokens(mut self, max_tokens: u32) -> Self {
        self.max_tokens = max_tokens;
        self
    }

    /// Override temperature.
    pub fn with_temperature(mut self, temperature: f32) -> Self {
        self.temperature = temperature;
        self
    }

    /// Override priority.
    pub fn with_priority(mut self, priority: u32) -> Self {
        self.priority = priority;
        self
    }
}

/// Object-safe trait for a capability package.
///
/// Built-in capabilities are unit structs (e.g.,
/// [`crate::built_in::CodeReviewer`]). TOML-loaded custom capabilities use
/// [`CustomCapability`].
///
/// The default `to_agent_definition()` implementation converts the spec into
/// an [`AgentDefinition`] ready for
/// [`vapora_agents::coordinator::AgentCoordinator`].
pub trait Capability: Send + Sync + fmt::Debug {
    /// Return the full specification for this capability.
    fn spec(&self) -> CapabilitySpec;

    /// Convert to an [`AgentDefinition`] usable by the agent registry.
    ///
    /// The system prompt is embedded into the definition so it is available
    /// at the execution site via
    /// [`vapora_agents::registry::AgentMetadata::system_prompt`].
    fn to_agent_definition(&self) -> AgentDefinition {
        let spec = self.spec();
        AgentDefinition {
            role: spec.agent_role.clone(),
            description: format!("[{}] {}", spec.display_name, spec.description),
            llm_provider: spec.preferred_provider.clone(),
            llm_model: spec.preferred_model.clone(),
            parallelizable: spec.parallelizable,
            priority: spec.priority,
            capabilities: spec.task_types.clone(),
            system_prompt: Some(spec.system_prompt.clone()),
        }
    }
}

/// Wraps a [`CapabilitySpec`] loaded from TOML as a [`Capability`]
/// implementation.
#[derive(Debug)]
pub struct CustomCapability(pub CapabilitySpec);

impl Capability for CustomCapability {
    fn spec(&self) -> CapabilitySpec {
        self.0.clone()
    }
}
