//! ANN search strategy types.
//!
//! Each [`AnnStrategy`] variant corresponds to a different trade-off between
//! recall, latency, and the cost of filter evaluation during approximate
//! nearest-neighbor search.

/// The ANN search strategy selected by the adaptive query planner.
///
/// Each variant corresponds to a different trade-off between recall, latency,
/// and the cost of filter evaluation.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum AnnStrategy {
    /// No filter applied. Standard HNSW search with default `ef_search`.
    Unfiltered,

    /// In-graph filtering with default `ef_search`. Used when selectivity
    /// is high enough (>= 20%) that the HNSW graph remains well-connected
    /// after filtering.
    InGraphFilter,

    /// In-graph filtering with widened `ef_search` to compensate for
    /// filter-induced recall loss. The `ef_search` value is the expanded
    /// beam width (e.g., 400 for 2x, 600 for 3x).
    WidenedFilter {
        /// The expanded beam width for this query.
        ef_search: usize,
    },

    /// Pre-filter to a small candidate set, then brute-force exact search.
    /// Used when selectivity is extremely low (< 1%) -- the filtered set
    /// is small enough that linear scan is faster than HNSW traversal
    /// through a sparse subgraph.
    PreFilterBruteForce,
}

// ---------------------------------------------------------------------------
// Tests
// ---------------------------------------------------------------------------

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

    #[test]
    fn strategy_debug_format() {
        // Verify Debug is derived and doesn't panic.
        let _ = format!("{:?}", AnnStrategy::Unfiltered);
        let _ = format!("{:?}", AnnStrategy::InGraphFilter);
        let _ = format!("{:?}", AnnStrategy::WidenedFilter { ef_search: 400 });
        let _ = format!("{:?}", AnnStrategy::PreFilterBruteForce);
    }

    #[test]
    fn strategy_clone_and_eq() {
        let a = AnnStrategy::WidenedFilter { ef_search: 600 };
        let b = a;
        assert_eq!(a, b);
    }

    #[test]
    fn strategy_copy_semantics() {
        let a = AnnStrategy::Unfiltered;
        let b = a;
        // Both are valid after copy -- no move.
        assert_eq!(a, b);
    }
}
