//! SEARCH query executor -- 8-stage pipeline.
//!
//! Implements the pipeline that turns a `Search` AST into `SearchResults`:
//! 1a. Text retrieval (BM25 via Tantivy), 1b. ANN retrieval, 1c. Fusion (RRF),
//! 2. Metadata filter, 2.5. User-context filter, 3. Profile scoring,
//! 4. Diversity enforcement, 5. Result assembly.

use std::sync::{Arc, RwLock};

use roaring::RoaringBitmap;

use crate::entities::{
    CreatorItemsBitmap, HardNegIndex, InteractionLedger, PreferenceVectors, UserStateIndex,
};
use crate::ranking::registry::ProfileRegistry;
use crate::session::{SessionContext, SessionSnapshot};
use crate::signals::SignalLedger;
use crate::storage::StorageEngine;
use crate::storage::indexes::bitmap::BitmapIndex;
use crate::storage::indexes::range::RangeIndex;
use crate::storage::vector::registry::EmbeddingSlotRegistry;

// ── SearchExecutor ───────────────────────────────────────────────────────────

/// Executes SEARCH queries through the 8-stage pipeline.
///
/// Constructed per-query by `TidalDb::search()`. Holds borrowed references
/// to all infrastructure needed for BM25 retrieval, ANN retrieval, fusion,
/// filtering, signal scoring, and diversity enforcement.
///
/// Index references are `Option` because the database may not have all indexes
/// available. The executor degrades gracefully when indexes are absent.
pub struct SearchExecutor<'a> {
    ledger: &'a SignalLedger,
    profile_registry: &'a ProfileRegistry,
    /// Text index for BM25 retrieval (items). `None` when no text fields are declared.
    text_index: Option<&'a Arc<crate::text::TextIndex>>,
    /// Text index for BM25 retrieval (creators). `None` when no creator text fields are declared.
    creator_text_index: Option<&'a Arc<crate::text::TextIndex>>,
    /// Embedding slot registry for ANN retrieval. `None` when no embedding slots declared.
    embedding_registry: Option<&'a RwLock<EmbeddingSlotRegistry>>,
    category_index: Option<&'a BitmapIndex>,
    format_index: Option<&'a BitmapIndex>,
    creator_index: Option<&'a BitmapIndex>,
    tag_index: Option<&'a BitmapIndex>,
    duration_index: Option<&'a RangeIndex<u32>>,
    created_at_index: Option<&'a RangeIndex<u64>>,
    universe: Option<&'a RwLock<RoaringBitmap>>,
    user_state: Option<&'a UserStateIndex>,
    hard_negatives: Option<&'a HardNegIndex>,
    interaction_ledger: Option<&'a InteractionLedger>,
    creator_items: Option<&'a CreatorItemsBitmap>,
    #[allow(dead_code)]
    preference_vectors: Option<&'a PreferenceVectors>,
    items_storage: Option<&'a dyn StorageEngine>,
    /// Storage engine for creator entities, used to load metadata for creator
    /// search results and to evaluate metadata-based post-filters.
    creators_storage: Option<&'a dyn StorageEngine>,
    session_context: Option<SessionContext>,
    session_snapshot: Option<SessionSnapshot>,
    /// M6 cohort ledger for `WithinScope::CohortTrending` resolution.
    cohort_ledger: Option<&'a crate::cohort::CohortSignalLedger>,
    /// M6 cohort registry for cohort existence checks.
    cohort_registry: Option<&'a crate::cohort::CohortRegistry>,
    /// M6p4 collection index for `WithinScope::Collection` resolution.
    collection_index: Option<&'a crate::entities::CollectionIndex>,
    // M7p2 load degradation
    degradation_level: crate::load::DegradationLevel,
}

impl<'a> SearchExecutor<'a> {
    /// Create a new executor with the core infrastructure.
    #[must_use]
    #[allow(clippy::too_many_arguments)]
    pub const fn new(
        ledger: &'a SignalLedger,
        profile_registry: &'a ProfileRegistry,
        text_index: Option<&'a Arc<crate::text::TextIndex>>,
        embedding_registry: Option<&'a RwLock<EmbeddingSlotRegistry>>,
        category_index: Option<&'a BitmapIndex>,
        format_index: Option<&'a BitmapIndex>,
        creator_index: Option<&'a BitmapIndex>,
        tag_index: Option<&'a BitmapIndex>,
        duration_index: Option<&'a RangeIndex<u32>>,
        created_at_index: Option<&'a RangeIndex<u64>>,
        universe: Option<&'a RwLock<RoaringBitmap>>,
    ) -> Self {
        Self {
            ledger,
            profile_registry,
            text_index,
            creator_text_index: None,
            embedding_registry,
            category_index,
            format_index,
            creator_index,
            tag_index,
            duration_index,
            created_at_index,
            universe,
            user_state: None,
            hard_negatives: None,
            interaction_ledger: None,
            creator_items: None,
            preference_vectors: None,
            items_storage: None,
            creators_storage: None,
            session_context: None,
            session_snapshot: None,
            cohort_ledger: None,
            cohort_registry: None,
            collection_index: None,
            degradation_level: crate::load::DegradationLevel::Full,
        }
    }

    /// Set the degradation level for this search execution.
    ///
    /// Computed by `LoadDetector::enter()` at query entry and threaded through
    /// the executor pipeline. Defaults to `Full` (no degradation).
    #[must_use]
    pub const fn with_degradation_level(mut self, level: crate::load::DegradationLevel) -> Self {
        self.degradation_level = level;
        self
    }

    /// Attach M3 user-context for personalization and user-state filtering.
    #[must_use]
    pub const fn with_user_context(
        mut self,
        user_state: &'a UserStateIndex,
        hard_negatives: &'a HardNegIndex,
        interaction_ledger: &'a InteractionLedger,
        creator_items: &'a CreatorItemsBitmap,
    ) -> Self {
        self.user_state = Some(user_state);
        self.hard_negatives = Some(hard_negatives);
        self.interaction_ledger = Some(interaction_ledger);
        self.creator_items = Some(creator_items);
        self
    }

    /// Attach preference vectors.
    #[must_use]
    pub const fn with_preference_vectors(
        mut self,
        preference_vectors: &'a PreferenceVectors,
    ) -> Self {
        self.preference_vectors = Some(preference_vectors);
        self
    }

    /// Attach a creator text index for BM25 retrieval when `entity_kind = Creator`.
    #[must_use]
    pub const fn with_creator_text_index(mut self, idx: &'a Arc<crate::text::TextIndex>) -> Self {
        self.creator_text_index = Some(idx);
        self
    }

    /// Attach items storage for metadata lookup during session-boosted scoring.
    #[must_use]
    pub fn with_items_storage(mut self, storage: &'a dyn StorageEngine) -> Self {
        self.items_storage = Some(storage);
        self
    }

    /// Attach creators storage for metadata lookup in creator search results
    /// and metadata-based post-filtering.
    #[must_use]
    pub fn with_creators_storage(mut self, storage: &'a dyn StorageEngine) -> Self {
        self.creators_storage = Some(storage);
        self
    }

    /// Attach M4 session context for FOR SESSION ranking boost.
    #[must_use]
    pub fn with_session(mut self, context: SessionContext, snapshot: SessionSnapshot) -> Self {
        self.session_context = Some(context);
        self.session_snapshot = Some(snapshot);
        self
    }

    /// Attach M6 cohort context for `WithinScope::CohortTrending` resolution.
    #[must_use]
    pub const fn with_cohort_context(
        mut self,
        cohort_ledger: &'a crate::cohort::CohortSignalLedger,
        cohort_registry: &'a crate::cohort::CohortRegistry,
    ) -> Self {
        self.cohort_ledger = Some(cohort_ledger);
        self.cohort_registry = Some(cohort_registry);
        self
    }

    /// Attach M6p4 collection index for `WithinScope::Collection` resolution.
    #[must_use]
    pub const fn with_collection_index(
        mut self,
        collection_index: &'a crate::entities::CollectionIndex,
    ) -> Self {
        self.collection_index = Some(collection_index);
        self
    }
}

mod pipeline;
