//! Vector-based skill and memory awareness using Qdrant embeddings.
//!
//! Leverages semantic similarity search to find relevant skills and conversation
//! memories based on vector embeddings. This is more powerful than keyword matching
//! because it captures semantic meaning, not just surface-level word overlap.
//!
//! - **VectorActiveAwareness**: Searches skills by semantic similarity when the user
//!   sends a message, finding skills relevant to the conversation topic.
//!
//! - **VectorPassiveAwareness**: Searches past conversation memories to provide relevant
//!   historical context when similar topics arise, based on tool-call patterns.

use crate::client::ChatRequestMessage;
use crate::embed::EmbedService;
use crate::perception::SkillContext;

/// Maximum relevant memories to inject.
const MAX_MEMORY_RESULTS: usize = 3;
/// Minimum similarity score (0.0–1.0) for memories.
const MIN_MEMORY_SCORE: f32 = 0.72;
/// Maximum skills to return from vector search.
const MAX_SKILL_RESULTS: usize = 3;
/// Minimum similarity score for skills.
const MIN_SKILL_SCORE: f32 = 0.70;

/// Vector-based active skill awareness — semantic search for relevant skills.
///
/// When the user sends a message, this awareness mode searches the Qdrant skill index
/// for skills whose content is semantically similar to the message, even if no keywords
/// match directly. This captures intent beyond explicit skill mentions.
#[derive(Debug, Clone)]
pub struct VectorActiveAwareness {
    pub max_skills: usize,
    pub min_score: f32,
}

impl Default for VectorActiveAwareness {
    fn default() -> Self {
        Self {
            max_skills: MAX_SKILL_RESULTS,
            min_score: MIN_SKILL_SCORE,
        }
    }
}

impl VectorActiveAwareness {
    /// Search for skills semantically relevant to the user's input.
    ///
    /// Uses Qdrant vector search within the given project to find skills whose
    /// embedded content is similar to `query`. Only returns results above `min_score`.
    pub async fn detect(
        &self,
        embed_service: &EmbedService,
        query: &str,
        project_uuid: &str,
    ) -> Vec<SkillContext> {
        let results = match embed_service
            .search_skills(query, project_uuid, self.max_skills)
            .await
        {
            Ok(results) => results,
            Err(_) => return Vec::new(),
        };

        results
            .into_iter()
            .filter(|r| r.score >= self.min_score)
            .map(|r| {
                let name = r
                    .payload
                    .extra
                    .as_ref()
                    .and_then(|v| v.get("name"))
                    .and_then(|v| v.as_str())
                    .unwrap_or("skill")
                    .to_string();
                SkillContext {
                    label: format!("[Vector] Skill: {}", name),
                    content: format!(
                        "[Relevant skill (score {:.2})]\n{}",
                        r.score, r.payload.text
                    ),
                }
            })
            .collect()
    }
}

/// Vector-based passive memory awareness — retrieve relevant past context.
///
/// When the agent encounters a topic (via tool-call or observation), this awareness
/// searches past conversation messages to find semantically similar prior discussions.
/// This gives the agent memory of how similar situations were handled before.
#[derive(Debug, Clone)]
pub struct VectorPassiveAwareness {
    pub max_memories: usize,
    pub min_score: f32,
}

impl Default for VectorPassiveAwareness {
    fn default() -> Self {
        Self {
            max_memories: MAX_MEMORY_RESULTS,
            min_score: MIN_MEMORY_SCORE,
        }
    }
}

impl VectorPassiveAwareness {
    /// Search for past conversation messages semantically similar to the current context.
    ///
    /// Uses Qdrant to find memories within the same room that share semantic similarity
    /// with the given query (usually the current input or a tool-call description).
    /// High-scoring results suggest prior discussions on this topic.
    pub async fn detect(
        &self,
        embed_service: &EmbedService,
        query: &str,
        project_name: &str,
        room_id: &str,
    ) -> Vec<MemoryContext> {
        let results = match embed_service
            .search_memories(query, project_name, room_id, self.max_memories)
            .await
        {
            Ok(results) => results,
            Err(_) => return Vec::new(),
        };

        results
            .into_iter()
            .filter(|r| r.score >= self.min_score)
            .map(|r| MemoryContext {
                score: r.score,
                content: r.payload.text,
            })
            .collect()
    }
}

/// A retrieved memory entry from vector search.
#[derive(Debug, Clone)]
pub struct MemoryContext {
    /// Similarity score (0.0–1.0).
    pub score: f32,
    /// The text of the past conversation message.
    pub content: String,
}

impl MemoryContext {
    /// Format as a system message for injection into the agent context.
    pub fn to_system_message(self) -> ChatRequestMessage {
        ChatRequestMessage::system(format!(
            "[Relevant memory (score {:.2})]\n{}",
            self.score, self.content
        ))
    }
}
