use std::sync::Arc;

use anyhow::{Context, Result};
use tracing::debug;

use crate::db::LanceDb;
use crate::search::embedder;
use crate::types::{SearchRequest, SearchResult};

/// Tier 1 search: fast vector-similarity lookup against LanceDb.
/// This is the cheapest tier (no LLM calls) and returns results
/// in sub-100ms for a warmed index.
pub struct Tier1Search {
    vector_db: Arc<LanceDb>,
}

impl Tier1Search {
    pub fn new(vector_db: Arc<LanceDb>) -> Self {
        Self { vector_db }
    }

    /// Perform a vector similarity search using the shared text embedder.
    pub async fn search(&self, request: &SearchRequest) -> Result<Vec<SearchResult>> {
        let embedding = embedder::embed_text(&request.query);
        let limit = request.max_results;

        debug!(
            query = %request.query,
            embedding_dim = embedding.len(),
            limit,
            "Tier1: executing vector search"
        );

        let raw_results = self
            .vector_db
            .search(&embedding, limit)
            .await
            .context("Tier1 vector search failed")?;

        let results: Vec<SearchResult> = raw_results
            .into_iter()
            .filter(|r| {
                // Apply file-type filter if specified.
                if request.file_types.is_empty() {
                    return true;
                }
                request
                    .file_types
                    .iter()
                    .any(|ft| r.file_path.ends_with(ft))
            })
            .filter(|r| {
                // Apply exclude-path filter.
                !request
                    .exclude_paths
                    .iter()
                    .any(|ex| r.file_path.contains(ex))
            })
            .map(|r| SearchResult {
                file_path: r.file_path,
                content: r.content,
                relevance_score: r.score,
                line_number: None,
            })
            .collect();

        debug!(
            query = %request.query,
            count = results.len(),
            "Tier1: search complete"
        );

        Ok(results)
    }
}

#[cfg(test)]
mod tests {
    use crate::search::embedder;

    #[test]
    fn test_query_embedding_deterministic() {
        let e1 = embedder::embed_text("hello world");
        let e2 = embedder::embed_text("hello world");
        assert_eq!(e1, e2);
    }

    #[test]
    fn test_query_embedding_normalized() {
        let e = embedder::embed_text("test query");
        let norm: f32 = e.iter().map(|x| x * x).sum::<f32>().sqrt();
        assert!((norm - 1.0).abs() < 1e-5);
    }

    #[test]
    fn test_query_embedding_dimension() {
        let e = embedder::embed_text("anything");
        assert_eq!(e.len(), 768);
    }
}
