//! Trait definitions for the similarity index.

use crate::error::Result;
use async_trait::async_trait;
use stemedb_core::types::Hash;

use super::model::{MinHashSignature, SimilarityCheckResult};

/// Trait for near-duplicate detection via MinHash + LSH.
///
/// The similarity index provides O(1) expected-time duplicate detection using:
/// 1. Bloom filter for fast "definitely not seen" checks
/// 2. MinHash for estimating Jaccard similarity
/// 3. LSH (Locality-Sensitive Hashing) for efficient candidate retrieval
#[async_trait]
pub trait SimilarityIndex: Send + Sync {
    /// Check if the given content (subject + predicate) is a near-duplicate.
    ///
    /// Returns information about similar entries found and whether they exceed
    /// the similarity threshold (default 0.9 Jaccard).
    ///
    /// # Algorithm
    /// 1. Hash the content and check the Bloom filter
    /// 2. If possibly present, compute MinHash signature
    /// 3. Hash signature into LSH buckets and retrieve candidates
    /// 4. Compare MinHash signatures with candidates
    /// 5. Return if any exceed the similarity threshold
    async fn check_similarity(
        &self,
        subject: &str,
        predicate: &str,
    ) -> Result<SimilarityCheckResult>;

    /// Add content to the similarity index.
    ///
    /// # Steps
    /// 1. Compute content hash (BLAKE3)
    /// 2. Compute MinHash signature
    /// 3. Add to Bloom filter
    /// 4. Store MinHash signature in KV store
    /// 5. Add to LSH buckets (all bands)
    async fn add(&self, subject: &str, predicate: &str, timestamp: u64) -> Result<Hash>;

    /// Check if a content hash exists in the Bloom filter.
    ///
    /// This is a fast probabilistic check:
    /// - Returns `false`: content is definitely NOT in the index
    /// - Returns `true`: content is PROBABLY in the index (may be false positive)
    fn contains_fast(&self, content_hash: &Hash) -> bool;

    /// Get the MinHash signature for a content hash, if it exists.
    async fn get_signature(&self, content_hash: &Hash) -> Result<Option<MinHashSignature>>;

    /// Get the number of entries in the index.
    async fn len(&self) -> Result<usize>;

    /// Check if the index is empty.
    async fn is_empty(&self) -> Result<bool> {
        Ok(self.len().await? == 0)
    }

    /// Rebuild the Bloom filter from persisted MinHash signatures.
    ///
    /// Called on startup to restore in-memory state from disk.
    async fn rebuild_bloom_filter(&self) -> Result<usize>;
}
