//! EigenTrust Authority Lens: Resolves based on global + domain trust.
//!
//! This lens integrates with both EigenTrust (global trust) and DomainTrust
//! (domain-specific expertise) to weight assertions by the combined reputation
//! and expertise of the signing agent.
//!
//! # Design Philosophy
//!
//! Follows the "Deep Module" principle:
//! - Simple interface: `resolve_async(&[Assertion])` returns winner
//! - Complex implementation: Queries TrustGraphStore for EigenTrust, DomainTrustStore for expertise
//! - Sybil-resistant: Only seed-connected agents have meaningful global trust
//! - Domain-aware: Expertise in the relevant domain boosts effective weight
//!
//! # Resolution Formula
//!
//! ```text
//! weight = confidence × eigentrust_score × domain_factor
//! ```
//!
//! Where:
//! - confidence: The assertion's self-declared confidence (0.0 - 1.0)
//! - eigentrust_score: Global trust from power iteration (0.0 - 1.0)
//! - domain_factor: 0.5 + (domain_score × 0.5), ranges from 0.5 to 1.0

use crate::traits::{compute_conflict_score, Resolution};
use crate::vote_aware_consensus::AsyncLens;
use async_trait::async_trait;
use std::sync::Arc;
use stemedb_core::types::Assertion;
use stemedb_storage::domain_trust_store::DomainTrustStore;
use stemedb_storage::trust_graph_store::TrustGraphStore;
use tracing::{debug, instrument};

/// EigenTrust Authority Lens: Returns the assertion with the highest
/// global + domain trust-weighted score.
///
/// # Resolution Strategy
///
/// 1. For each candidate assertion, extract the primary signer's agent_id
/// 2. Lookup the agent's EigenTrust score (global trust)
/// 3. Lookup the agent's domain trust for this assertion's predicate
/// 4. Calculate: `weight = confidence × eigentrust × domain_factor`
/// 5. Return the assertion with highest weighted score
/// 6. Tiebreaker: If scores are equal, prefer most recent timestamp
/// 7. Agents with no EigenTrust score get 0.0 (Sybil protection)
/// 8. Agents with no domain trust get default 0.5 (neutral)
///
/// # Sybil Resistance
///
/// The key insight is that isolated agents (not connected to seed trust)
/// have near-zero EigenTrust scores, effectively filtering out Sybil attacks.
///
/// # Example
///
/// ```ignore
/// use stemedb_lens::EigenTrustAuthorityLens;
/// use stemedb_storage::{HybridStore, GenericTrustGraphStore, GenericDomainTrustStore};
/// use std::sync::Arc;
///
/// let store = Arc::new(HybridStore::open("./data")?);
/// let trust_graph = Arc::new(GenericTrustGraphStore::new(store.clone()));
/// let domain_trust = Arc::new(GenericDomainTrustStore::new(store));
/// let lens = EigenTrustAuthorityLens::new(trust_graph, domain_trust);
///
/// let resolution = lens.resolve_async(&candidates).await;
/// ```
pub struct EigenTrustAuthorityLens<T, D> {
    trust_graph_store: Arc<T>,
    domain_trust_store: Arc<D>,
}

impl<T: TrustGraphStore, D: DomainTrustStore> EigenTrustAuthorityLens<T, D> {
    /// Create a new EigenTrustAuthorityLens with the given stores.
    ///
    /// Both stores are wrapped in Arc for shared ownership, allowing
    /// the lens to be used in multiple contexts.
    pub fn new(trust_graph_store: Arc<T>, domain_trust_store: Arc<D>) -> Self {
        Self { trust_graph_store, domain_trust_store }
    }

    /// Extract the primary agent ID from an assertion.
    ///
    /// Uses the first signature's agent_id. Returns None if no signatures exist.
    fn get_primary_agent(assertion: &Assertion) -> Option<[u8; 32]> {
        assertion.signatures.first().map(|sig| sig.agent_id)
    }
}

/// Internal struct to track assertion ranking data.
#[derive(Debug)]
struct RankedAssertion<'a> {
    assertion: &'a Assertion,
    eigentrust_score: f32,
    domain_factor: f32,
    weighted_score: f32,
}

#[async_trait]
impl<T: TrustGraphStore + 'static, D: DomainTrustStore + 'static> AsyncLens
    for EigenTrustAuthorityLens<T, D>
{
    #[instrument(skip(self, candidates), fields(candidates_count = candidates.len()))]
    async fn resolve_async(&self, candidates: &[Assertion]) -> Resolution {
        if candidates.is_empty() {
            return Resolution::empty();
        }

        // For single candidate, still calculate weighted score
        if candidates.len() == 1 {
            let assertion = &candidates[0];
            let (eigentrust_score, domain_factor, weighted_score) =
                self.calculate_weight(assertion).await;

            debug!(
                subject = %assertion.subject,
                eigentrust_score,
                domain_factor,
                weighted_score,
                "Single candidate resolution"
            );

            return Resolution::with_winner(assertion.clone(), 1, weighted_score, 0.0);
        }

        // Collect trust-weighted scores for all candidates
        let mut ranked: Vec<RankedAssertion> = Vec::with_capacity(candidates.len());

        for assertion in candidates {
            let (eigentrust_score, domain_factor, weighted_score) =
                self.calculate_weight(assertion).await;

            debug!(
                subject = %assertion.subject,
                eigentrust_score,
                domain_factor,
                weighted_score,
                "Calculated weighted score"
            );

            ranked.push(RankedAssertion {
                assertion,
                eigentrust_score,
                domain_factor,
                weighted_score,
            });
        }

        // Sort by weighted score (descending), then by timestamp (descending) for ties
        ranked.sort_by(|a, b| {
            b.weighted_score
                .partial_cmp(&a.weighted_score)
                .unwrap_or(std::cmp::Ordering::Equal)
                .then_with(|| b.assertion.timestamp.cmp(&a.assertion.timestamp))
        });

        // Select the winner (highest ranked)
        if let Some(winner) = ranked.first() {
            let conflict = compute_conflict_score(candidates);

            debug!(
                winner_subject = %winner.assertion.subject,
                eigentrust = winner.eigentrust_score,
                domain_factor = winner.domain_factor,
                weighted_score = winner.weighted_score,
                conflict,
                "Resolved via EigenTrust + domain authority"
            );

            Resolution::with_winner(
                winner.assertion.clone(),
                candidates.len(),
                winner.weighted_score,
                conflict,
            )
        } else {
            // Should never happen since we checked for empty candidates above
            Resolution::empty()
        }
    }

    fn name(&self) -> &'static str {
        "EigenTrustAuthority"
    }
}

impl<T: TrustGraphStore + 'static, D: DomainTrustStore + 'static> EigenTrustAuthorityLens<T, D> {
    /// Calculate the weighted score for an assertion.
    ///
    /// Returns (eigentrust_score, domain_factor, weighted_score).
    async fn calculate_weight(&self, assertion: &Assertion) -> (f32, f32, f32) {
        // Extract primary agent
        let agent_id = match Self::get_primary_agent(assertion) {
            Some(id) => id,
            None => {
                debug!(
                    subject = %assertion.subject,
                    "Assertion has no signatures, treating as untrusted"
                );
                return (0.0, 1.0, 0.0);
            }
        };

        // Get EigenTrust score (global trust)
        let eigentrust_score = match self.trust_graph_store.get_eigentrust_score(&agent_id).await {
            Ok(score) => score,
            Err(e) => {
                debug!(
                    agent_id = %hex::encode(agent_id),
                    error = %e,
                    "Failed to get EigenTrust score, using 0.0"
                );
                0.0 // No EigenTrust score = untrusted (Sybil protection)
            }
        };

        // Get domain factor (domain-specific expertise)
        let domain_factor = match self
            .domain_trust_store
            .get_effective_trust(&agent_id, &assertion.predicate, 1.0)
            .await
        {
            Ok(effective) => effective, // get_effective_trust returns eigentrust × factor, so with 1.0 it returns just the factor
            Err(e) => {
                debug!(
                    agent_id = %hex::encode(agent_id),
                    predicate = %assertion.predicate,
                    error = %e,
                    "Failed to get domain trust, using default factor 0.75"
                );
                0.75 // Default domain factor (0.5 score → 0.75 factor)
            }
        };

        // Calculate weighted score
        // weight = confidence × eigentrust × domain_factor
        let weighted_score = assertion.confidence * eigentrust_score * domain_factor;

        (eigentrust_score, domain_factor, weighted_score)
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use stemedb_core::testing::AssertionBuilder;
    use stemedb_storage::domain_trust_store::{DomainTrust, GenericDomainTrustStore};
    use stemedb_storage::trust_graph_store::{
        EigenTrustConfig, GenericTrustGraphStore, TrustEdge, TrustGraphStore,
    };
    use stemedb_storage::HybridStore;

    fn agent(id: u8) -> [u8; 32] {
        let mut arr = [0u8; 32];
        arr[0] = id;
        arr
    }

    fn create_assertion(
        subject: &str,
        predicate: &str,
        confidence: f32,
        agent_id: [u8; 32],
        timestamp: u64,
    ) -> Assertion {
        AssertionBuilder::new()
            .subject(subject)
            .predicate(predicate)
            .confidence(confidence)
            .agent_id(agent_id)
            .timestamp(timestamp)
            .build()
    }

    #[tokio::test]
    async fn test_empty_candidates() {
        let store = Arc::new(HybridStore::open_temp().expect("Failed to create store"));
        let trust_graph = Arc::new(GenericTrustGraphStore::new(store.clone()));
        let domain_trust = Arc::new(GenericDomainTrustStore::new(store));
        let lens = EigenTrustAuthorityLens::new(trust_graph, domain_trust);

        let resolution = lens.resolve_async(&[]).await;

        assert!(resolution.winner.is_none());
        assert_eq!(resolution.candidates_count, 0);
    }

    #[tokio::test]
    async fn test_single_candidate_no_eigentrust() {
        let store = Arc::new(HybridStore::open_temp().expect("Failed to create store"));
        let trust_graph = Arc::new(GenericTrustGraphStore::new(store.clone()));
        let domain_trust = Arc::new(GenericDomainTrustStore::new(store));
        let lens = EigenTrustAuthorityLens::new(trust_graph, domain_trust);

        // Agent with no EigenTrust score
        let assertion = create_assertion("Subject", "predicate", 0.8, agent(1), 1000);
        let resolution = lens.resolve_async(&[assertion]).await;

        assert!(resolution.winner.is_some());
        // No EigenTrust = 0.0, so weighted score = 0.8 * 0.0 * factor = 0.0
        assert!((resolution.resolution_confidence - 0.0).abs() < 0.01);
    }

    #[tokio::test]
    async fn test_eigentrust_integrated() {
        let store = Arc::new(HybridStore::open_temp().expect("Failed to create store"));
        let trust_graph = Arc::new(GenericTrustGraphStore::new(store.clone()));
        let domain_trust = Arc::new(GenericDomainTrustStore::new(store));
        let lens =
            EigenTrustAuthorityLens::new(Arc::clone(&trust_graph), Arc::clone(&domain_trust));

        // Set up trust graph: seed → agent1
        trust_graph.set_seed_trust(&agent(0), 1.0).await.expect("set seed");
        trust_graph
            .add_trust_edge(&TrustEdge::new(agent(0), agent(1), 1.0, 1000, None))
            .await
            .expect("add edge");

        // Compute EigenTrust
        trust_graph.compute_eigentrust(&EigenTrustConfig::default()).await.expect("compute");

        // Create assertion from agent 1
        let assertion = create_assertion("Subject", "predicate", 0.8, agent(1), 1000);
        let resolution = lens.resolve_async(&[assertion]).await;

        assert!(resolution.winner.is_some());
        // Agent 1 should have non-zero EigenTrust score
        assert!(resolution.resolution_confidence > 0.0);
    }

    #[tokio::test]
    async fn test_sybil_agent_gets_low_score() {
        let store = Arc::new(HybridStore::open_temp().expect("Failed to create store"));
        let trust_graph = Arc::new(GenericTrustGraphStore::new(store.clone()));
        let domain_trust = Arc::new(GenericDomainTrustStore::new(store));
        let lens =
            EigenTrustAuthorityLens::new(Arc::clone(&trust_graph), Arc::clone(&domain_trust));

        // Set up: seed has trust, Sybil ring is isolated
        trust_graph.set_seed_trust(&agent(0), 1.0).await.expect("set seed");
        trust_graph
            .add_trust_edge(&TrustEdge::new(agent(0), agent(1), 1.0, 1000, None))
            .await
            .expect("add edge");

        // Sybil ring: 10 → 11 → 12 → 10 (not connected to seed)
        trust_graph
            .add_trust_edge(&TrustEdge::new(agent(10), agent(11), 1.0, 1000, None))
            .await
            .expect("add edge");
        trust_graph
            .add_trust_edge(&TrustEdge::new(agent(11), agent(12), 1.0, 1000, None))
            .await
            .expect("add edge");
        trust_graph
            .add_trust_edge(&TrustEdge::new(agent(12), agent(10), 1.0, 1000, None))
            .await
            .expect("add edge");

        // Compute EigenTrust
        trust_graph.compute_eigentrust(&EigenTrustConfig::default()).await.expect("compute");

        // Legitimate agent vs Sybil agent
        let legitimate = create_assertion("Subject", "predicate", 0.8, agent(1), 1000);
        let sybil = create_assertion("Subject", "predicate", 1.0, agent(10), 1100); // Higher confidence!

        let resolution = lens.resolve_async(&[legitimate.clone(), sybil]).await;

        // Legitimate agent should win despite Sybil having higher confidence
        assert!(resolution.winner.is_some());
        assert_eq!(resolution.winner.as_ref().unwrap().signatures[0].agent_id, agent(1));
    }

    #[tokio::test]
    async fn test_domain_expertise_matters() {
        let store = Arc::new(HybridStore::open_temp().expect("Failed to create store"));
        let trust_graph = Arc::new(GenericTrustGraphStore::new(store.clone()));
        let domain_trust = Arc::new(GenericDomainTrustStore::new(store));
        let lens =
            EigenTrustAuthorityLens::new(Arc::clone(&trust_graph), Arc::clone(&domain_trust));

        // Set up: both agents have same EigenTrust
        trust_graph.set_seed_trust(&agent(0), 1.0).await.expect("set seed");
        trust_graph
            .add_trust_edge(&TrustEdge::new(agent(0), agent(1), 1.0, 1000, None))
            .await
            .expect("add edge");
        trust_graph
            .add_trust_edge(&TrustEdge::new(agent(0), agent(2), 1.0, 1000, None))
            .await
            .expect("add edge");
        trust_graph.compute_eigentrust(&EigenTrustConfig::default()).await.expect("compute");

        // Agent 1: Expert in medicine (score 0.95)
        let mut dt1 = DomainTrust::new(agent(1), "medicine".to_string(), 1000);
        dt1.score = 0.95;
        domain_trust.put_domain_trust(&dt1).await.expect("put");

        // Agent 2: Novice in medicine (score 0.3)
        let mut dt2 = DomainTrust::new(agent(2), "medicine".to_string(), 1000);
        dt2.score = 0.3;
        domain_trust.put_domain_trust(&dt2).await.expect("put");

        // Same confidence, same predicate (medicine domain)
        let expert_assertion = create_assertion("Drug", "treats_condition", 0.8, agent(1), 1000);
        let novice_assertion = create_assertion("Drug", "treats_condition", 0.8, agent(2), 1100);

        let resolution = lens.resolve_async(&[expert_assertion.clone(), novice_assertion]).await;

        // Expert should win due to higher domain trust
        assert!(resolution.winner.is_some());
        assert_eq!(resolution.winner.as_ref().unwrap().signatures[0].agent_id, agent(1));
    }

    #[tokio::test]
    async fn test_no_signatures_treated_as_untrusted() {
        let store = Arc::new(HybridStore::open_temp().expect("Failed to create store"));
        let trust_graph = Arc::new(GenericTrustGraphStore::new(store.clone()));
        let domain_trust = Arc::new(GenericDomainTrustStore::new(store));
        let lens =
            EigenTrustAuthorityLens::new(Arc::clone(&trust_graph), Arc::clone(&domain_trust));

        // Set up trusted agent
        trust_graph.set_seed_trust(&agent(0), 1.0).await.expect("set seed");
        trust_graph
            .add_trust_edge(&TrustEdge::new(agent(0), agent(1), 1.0, 1000, None))
            .await
            .expect("add edge");
        trust_graph.compute_eigentrust(&EigenTrustConfig::default()).await.expect("compute");

        let signed = create_assertion("Subject", "predicate", 0.7, agent(1), 1000);

        let mut unsigned = create_assertion("Subject", "predicate", 1.0, agent(99), 1100);
        unsigned.signatures.clear();

        let resolution = lens.resolve_async(&[signed.clone(), unsigned]).await;

        // Signed assertion should win even with lower confidence
        assert!(resolution.winner.is_some());
        assert_eq!(resolution.winner.as_ref().unwrap().signatures.len(), 1);
    }

    #[tokio::test]
    async fn test_tie_breaking_by_timestamp() {
        let store = Arc::new(HybridStore::open_temp().expect("Failed to create store"));
        let trust_graph = Arc::new(GenericTrustGraphStore::new(store.clone()));
        let domain_trust = Arc::new(GenericDomainTrustStore::new(store));
        let lens =
            EigenTrustAuthorityLens::new(Arc::clone(&trust_graph), Arc::clone(&domain_trust));

        // Set up: same agent makes two assertions
        trust_graph.set_seed_trust(&agent(0), 1.0).await.expect("set seed");
        trust_graph
            .add_trust_edge(&TrustEdge::new(agent(0), agent(1), 1.0, 1000, None))
            .await
            .expect("add edge");
        trust_graph.compute_eigentrust(&EigenTrustConfig::default()).await.expect("compute");

        // Same agent, same confidence, different timestamps
        let older = create_assertion("Subject", "predicate", 0.8, agent(1), 1000);
        let newer = create_assertion("Subject", "predicate", 0.8, agent(1), 2000);

        let resolution = lens.resolve_async(&[older, newer.clone()]).await;

        // Newer should win on tiebreak
        assert!(resolution.winner.is_some());
        assert_eq!(resolution.winner.as_ref().unwrap().timestamp, 2000);
    }

    #[tokio::test]
    async fn test_lens_name() {
        let store = Arc::new(HybridStore::open_temp().expect("Failed to create store"));
        let trust_graph = Arc::new(GenericTrustGraphStore::new(store.clone()));
        let domain_trust = Arc::new(GenericDomainTrustStore::new(store));
        let lens = EigenTrustAuthorityLens::new(trust_graph, domain_trust);

        assert_eq!(lens.name(), "EigenTrustAuthority");
    }
}
