use std::sync::Arc;

use super::{ProviderError, RoutineRunner, ToolFactory, builder};
use crate::Slug;
use crate::agents::builder::AgentBuilder;
use crate::agents::prompts::PromptContext;
use crate::hooks::{ResolvedHook, resolve_command_hooks, resolve_skill_hooks};
use crate::manifest::{
    AbilityManifest, AgentManifest, CommandManifest, DomainManifest, Manifest, ModelManifest,
    ProjectManifest, SkillManifest,
};
use crate::memory::Memory;
use crate::tools::Tool;
use anyhow::Result;

#[doc(hidden)]
pub trait ProviderMemory: Send + Sync + 'static {
    type Runtime<'a>: Memory + ?Sized + 'a
    where
        Self: 'a;

    fn clone_runtime(memory: Option<&Arc<Self>>) -> Option<Arc<Self::Runtime<'static>>>;
}

impl<T> ProviderMemory for T
where
    T: Memory + Sized + 'static,
{
    type Runtime<'a>
        = T
    where
        Self: 'a;

    fn clone_runtime(memory: Option<&Arc<Self>>) -> Option<Arc<Self::Runtime<'static>>> {
        memory.cloned()
    }
}

impl ProviderMemory for dyn Memory + 'static {
    type Runtime<'a>
        = dyn Memory + 'static
    where
        Self: 'a;

    fn clone_runtime(memory: Option<&Arc<Self>>) -> Option<Arc<Self::Runtime<'static>>> {
        memory.cloned()
    }
}

impl ProviderMemory for builder::NoMemory {
    type Runtime<'a>
        = dyn Memory + 'static
    where
        Self: 'a;

    fn clone_runtime(_memory: Option<&Arc<Self>>) -> Option<Arc<Self::Runtime<'static>>> {
        None
    }
}

/// Runtime contract required by generic agent and routine execution.
///
/// `ProviderRuntime` lets runners stay generic over the concrete provider,
/// model factory, tool factory, and memory backend. The concrete
/// [`Provider`](crate::provider::Provider) implements this trait, and most
/// applications can use `Provider` directly. Implement this trait only when you
/// need a custom provider-like runtime for tests or embedding.
///
/// The naming convention is intentional:
///
/// - `find_*` methods borrow data already present in the manifest.
/// - `create_*` methods construct runtime dependencies.
/// - `build_*` methods return higher-level builders.
/// - [`new_agent`](Self::new_agent) starts a blank agent builder that may be
///   configured without an agent already present in the provider manifest.
#[async_trait::async_trait]
pub trait ProviderRuntime: Clone + Send + Sync + 'static {
    /// Model provider type created for an agent model manifest.
    type Model<'a>: nenjo_models::ModelProvider + Send + Sync + ?Sized + 'a
    where
        Self: 'a;

    /// Tool factory type used when constructing an agent's runtime tools.
    type ToolFactory<'a>: ToolFactory + ?Sized + 'a
    where
        Self: 'a;

    /// Memory backend type available to agent builders.
    type Memory<'a>: Memory + ?Sized + 'a
    where
        Self: 'a;

    /// Return an owned snapshot of the manifest used by this runtime.
    fn manifest_snapshot(&self) -> Arc<Manifest>;

    /// Return a runtime with the same services and a new manifest.
    fn with_manifest(&self, manifest: Manifest) -> Self;

    /// Borrow the runtime's tool factory.
    fn tool_factory(&self) -> &Self::ToolFactory<'_>;

    /// Find an agent manifest by slug.
    fn find_agent_manifest(&self, slug: &Slug) -> Option<&AgentManifest>;

    /// Find an ability manifest by exact assigned ability name.
    fn find_ability(&self, name: &str) -> Option<&AbilityManifest>;

    /// Find a domain manifest by slug, name, or command selector.
    fn find_domain(&self, selector: &str) -> Option<&DomainManifest>;

    /// Find a project manifest by slug.
    fn find_project(&self, slug: &Slug) -> Option<&ProjectManifest>;

    /// Resolve hooks that should become active for a command invocation.
    fn resolve_hooks_for_command(&self, command: &CommandManifest) -> Vec<ResolvedHook> {
        let manifest = self.manifest_snapshot();
        resolve_command_hooks(&manifest.hooks, command)
    }

    /// Resolve hooks that should become active for a skill activation.
    fn resolve_hooks_for_skill(&self, skill: &SkillManifest) -> Vec<ResolvedHook> {
        let manifest = self.manifest_snapshot();
        resolve_skill_hooks(&manifest.hooks, skill)
    }

    /// Create provider-level knowledge tools.
    fn create_knowledge_tools(&self) -> Vec<Arc<dyn Tool>>;

    /// Build prompt context for an agent manifest.
    fn build_prompt_context(&self, agent: &AgentManifest) -> PromptContext;

    /// Create the model provider requested by a model manifest.
    async fn create_model_provider(
        &self,
        model: &ModelManifest,
    ) -> Result<Arc<Self::Model<'static>>, ProviderError>;

    /// Start a blank agent builder backed by this provider runtime.
    fn new_agent(&self) -> AgentBuilder<Self>;

    /// Build an agent from the provider manifest by slug.
    async fn agent(&self, slug: &Slug) -> Result<AgentBuilder<Self>, ProviderError>;

    /// Build a routine runner from the provider manifest by routine slug.
    fn routine(&self, slug: &Slug) -> Result<RoutineRunner<Self>, ProviderError>
    where
        Self: Sized;
}
