//! QueryBuilder for fluent query construction.

use stemedb_core::types::{EpochId, LifecycleStage};

use super::Query;

/// Builder for constructing queries with a fluent API.
#[derive(Debug, Clone, Default)]
pub struct QueryBuilder {
    query: Query,
}

impl QueryBuilder {
    /// Create a new query builder.
    pub fn new() -> Self {
        Self::default()
    }

    /// Filter by subject.
    pub fn subject(mut self, subject: impl Into<String>) -> Self {
        self.query.subject = Some(subject.into());
        self
    }

    /// Filter by predicate.
    pub fn predicate(mut self, predicate: impl Into<String>) -> Self {
        self.query.predicate = Some(predicate.into());
        self
    }

    /// Filter by lifecycle stage.
    pub fn lifecycle(mut self, lifecycle: LifecycleStage) -> Self {
        self.query.lifecycle = Some(lifecycle);
        self
    }

    /// Filter by epoch.
    pub fn epoch(mut self, epoch: EpochId) -> Self {
        self.query.epoch = Some(epoch);
        self
    }

    /// Set maximum number of results.
    pub fn limit(mut self, limit: usize) -> Self {
        self.query.limit = Some(limit);
        self
    }

    /// Set maximum acceptable staleness for materialized views.
    ///
    /// When set, the fast path (MV lookup) will be skipped if the materialized
    /// view was created more than `seconds` ago. This causes the query to
    /// fall through to the slow path, which re-computes the result from all
    /// candidate assertions.
    ///
    /// # Example
    /// ```rust
    /// use stemedb_query::Query;
    ///
    /// // Only accept MVs materialized within the last 5 minutes
    /// let query = Query::builder()
    ///     .subject("Tesla")
    ///     .predicate("revenue")
    ///     .max_stale(300)
    ///     .build();
    /// ```
    pub fn max_stale(mut self, seconds: u64) -> Self {
        self.query.max_stale = Some(seconds);
        self
    }

    /// Filter by visual similarity to a reference perceptual hash.
    ///
    /// Returns only assertions whose `visual_hash` has a hamming distance
    /// less than or equal to `threshold` from the target hash. Assertions
    /// without a `visual_hash` are excluded.
    ///
    /// # Arguments
    ///
    /// * `hash` - Hex-encoded 8-byte pHash (16 hex characters)
    /// * `threshold` - Maximum hamming distance (0-64, default: 8)
    ///
    /// # Example
    ///
    /// ```rust
    /// use stemedb_query::Query;
    ///
    /// // Find visually similar screenshots (within 5 bits of difference)
    /// let query = Query::builder()
    ///     .subject("Tesla_10K_2024")
    ///     .visual_near("a3f2b1c4d5e6f708", 5)
    ///     .build();
    /// ```
    pub fn visual_near(mut self, hash: impl Into<String>, threshold: u32) -> Self {
        self.query.visual_near = Some(hash.into());
        self.query.visual_threshold = Some(threshold);
        self
    }

    /// Query state as of a specific timestamp (time-travel).
    ///
    /// Returns only assertions created at or before the given timestamp.
    /// When set, the fast path (MV lookup) is bypassed since materialized
    /// views reflect current state, not historical state.
    ///
    /// # Example
    /// ```rust
    /// use stemedb_query::Query;
    ///
    /// // What was the consensus on January 1, 2024?
    /// let query = Query::builder()
    ///     .subject("Tesla")
    ///     .predicate("revenue")
    ///     .as_of(1704067200) // 2024-01-01 00:00:00 UTC
    ///     .build();
    /// ```
    pub fn as_of(mut self, timestamp: u64) -> Self {
        self.query.as_of = Some(timestamp);
        self
    }

    /// Set the decay half-life in seconds for confidence decay.
    ///
    /// Older assertions will have their effective confidence reduced based on age.
    /// Formula: `effective_confidence = confidence * 2^(-(age / halflife))`
    ///
    /// # Example
    /// ```rust
    /// use stemedb_query::Query;
    ///
    /// // 6-month decay half-life for medical knowledge
    /// let query = Query::builder()
    ///     .subject("Semaglutide")
    ///     .predicate("muscle_effect")
    ///     .decay_halflife(15768000) // 6 months in seconds
    ///     .build();
    /// ```
    pub fn decay_halflife(mut self, seconds: u64) -> Self {
        self.query.decay_halflife = Some(seconds);
        self
    }

    /// Enable source-class-aware decay.
    ///
    /// When enabled, each assertion's decay half-life is determined by its
    /// `source_class` tier instead of using a uniform half-life:
    /// - Regulatory (Tier 0): No decay
    /// - Clinical (Tier 1): 2-year half-life
    /// - Observational (Tier 2): 1-year half-life
    /// - Expert (Tier 3): 6-month half-life
    /// - Community (Tier 4): 3-month half-life
    /// - Anecdotal (Tier 5): 1-month half-life
    ///
    /// Requires `decay_halflife` to be set as a fallback for assertions
    /// without source_class.
    ///
    /// # Example
    /// ```rust
    /// use stemedb_query::Query;
    ///
    /// // Use tier-based decay with 1-year fallback
    /// let query = Query::builder()
    ///     .subject("Semaglutide")
    ///     .predicate("muscle_effect")
    ///     .decay_halflife(31536000) // 1-year fallback
    ///     .source_class_decay(true)
    ///     .build();
    /// ```
    pub fn source_class_decay(mut self, enabled: bool) -> Self {
        self.query.source_class_decay = enabled;
        self
    }

    /// Query by semantic vector similarity (k-nearest neighbors).
    ///
    /// Uses the vector index to find the `k` nearest assertions by embedding
    /// distance. Additional filters (subject, predicate, lifecycle) are applied
    /// after the vector search.
    ///
    /// # Arguments
    ///
    /// * `vector` - The query embedding vector
    /// * `k` - Number of nearest neighbors to return
    ///
    /// # Example
    /// ```rust
    /// use stemedb_query::Query;
    ///
    /// // Find 10 semantically similar assertions
    /// let embedding = vec![0.1, 0.2, 0.3]; // Your embedding here
    /// let query = Query::builder()
    ///     .vector_near(embedding, 10)
    ///     .build();
    /// ```
    pub fn vector_near(mut self, vector: Vec<f32>, k: usize) -> Self {
        self.query.vector_near = Some(vector);
        self.query.k = Some(k);
        self
    }

    /// Set minimum conflict score threshold (0.0 to 1.0).
    ///
    /// Only returns results where the conflict_score >= this value.
    /// Used to filter for controversial claims where assertions disagree.
    ///
    /// # Arguments
    ///
    /// * `score` - Minimum conflict score (0.0-1.0)
    ///
    /// # Example
    /// ```rust
    /// use stemedb_query::Query;
    ///
    /// // Only show controversial claims
    /// let query = Query::builder()
    ///     .subject("Semaglutide")
    ///     .predicate("muscle_effect")
    ///     .min_conflict_score(0.7)
    ///     .build();
    /// ```
    pub fn min_conflict_score(mut self, score: f32) -> Self {
        self.query.min_conflict_score = Some(score);
        self
    }

    /// Set maximum conflict score threshold (0.0 to 1.0).
    ///
    /// Only returns results where the conflict_score <= this value.
    /// Used to filter for claims with strong agreement.
    ///
    /// # Arguments
    ///
    /// * `score` - Maximum conflict score (0.0-1.0)
    ///
    /// # Example
    /// ```rust
    /// use stemedb_query::Query;
    ///
    /// // Only show claims with strong consensus
    /// let query = Query::builder()
    ///     .subject("Semaglutide")
    ///     .predicate("muscle_effect")
    ///     .max_conflict_score(0.2)
    ///     .build();
    /// ```
    pub fn max_conflict_score(mut self, score: f32) -> Self {
        self.query.max_conflict_score = Some(score);
        self
    }

    /// Enable alias resolution for subject queries.
    ///
    /// When enabled, the QueryEngine will resolve the subject to all aliased
    /// paths (via `AliasStore.resolve_all()`) and fetch assertions for all
    /// of them, deduplicating by hash.
    ///
    /// This enables cross-scheme concept resolution. For example, querying
    /// `code://rust/myapp/tls/cert_verification` would also return assertions
    /// from `rfc://5246/tls/cert_verification` if they are aliased.
    ///
    /// Requires an `AliasStore` to be configured on the `QueryEngine`.
    ///
    /// # Arguments
    ///
    /// * `enabled` - `true` to expand subject aliases, `false` for exact match
    ///
    /// # Example
    /// ```rust
    /// use stemedb_query::Query;
    ///
    /// // Find assertions from both code and authoritative sources
    /// let query = Query::builder()
    ///     .subject("code://rust/myapp/tls/cert_verification")
    ///     .resolve_aliases(true)
    ///     .build();
    /// ```
    pub fn resolve_aliases(mut self, enabled: bool) -> Self {
        self.query.resolve_aliases = enabled;
        self
    }

    /// Build the query.
    pub fn build(self) -> Query {
        self.query
    }
}
