//! Recency Lens: Most recent timestamp wins.
//!
//! This is the simplest lens, useful for scenarios where the latest
//! information is always preferred (news, real-time data).

use crate::traits::{compute_conflict_score, Lens, Resolution};
use stemedb_core::types::Assertion;
use tracing::instrument;

/// Recency Lens: Returns the assertion with the highest timestamp.
///
/// # Resolution Strategy
///
/// 1. Find assertion with maximum `timestamp`
/// 2. If tie: prefer assertion with lower `confidence` hash (deterministic tiebreaker)
///
/// # Confidence Calculation
///
/// - Single candidate: 1.0 (trivial resolution)
/// - Multiple candidates: Based on timestamp gap to next candidate
#[derive(Debug, Clone, Copy, Default)]
pub struct RecencyLens;

impl Lens for RecencyLens {
    #[instrument(skip(self, candidates), fields(candidates_count = candidates.len(), lens = "Recency"))]
    fn resolve(&self, candidates: &[Assertion]) -> Resolution {
        if candidates.is_empty() {
            return Resolution::empty();
        }

        if candidates.len() == 1 {
            return Resolution::with_winner(candidates[0].clone(), 1, 1.0, 0.0);
        }

        // Find the assertion with the highest timestamp
        let winner = candidates
            .iter()
            .max_by(|a, b| {
                // Primary: highest timestamp
                // Tiebreaker: compare source_hash for determinism
                a.timestamp.cmp(&b.timestamp).then_with(|| a.source_hash.cmp(&b.source_hash))
            })
            .cloned();

        match winner {
            Some(w) => {
                // Calculate confidence based on how much newer the winner is
                let max_ts = w.timestamp;
                let second_max = candidates
                    .iter()
                    .filter(|a| a.timestamp < max_ts)
                    .map(|a| a.timestamp)
                    .max()
                    .unwrap_or(0);

                // Confidence is higher when the gap is larger
                // This is a simple heuristic; real systems might use more sophisticated metrics
                let gap = max_ts.saturating_sub(second_max);
                let confidence = if gap > 86400 {
                    // More than a day: high confidence
                    0.95
                } else if gap > 3600 {
                    // More than an hour: good confidence
                    0.8
                } else if gap > 60 {
                    // More than a minute: moderate confidence
                    0.6
                } else {
                    // Very close: low confidence
                    0.5
                };

                let conflict = compute_conflict_score(candidates);
                Resolution::with_winner(w, candidates.len(), confidence, conflict)
            }
            None => Resolution::empty(),
        }
    }

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

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

    fn create_assertion(subject: &str, timestamp: u64) -> Assertion {
        AssertionBuilder::new().subject(subject).timestamp(timestamp).build()
    }

    #[test]
    fn test_empty_candidates() {
        let lens = RecencyLens;
        let resolution = lens.resolve(&[]);

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

    #[test]
    fn test_single_candidate() {
        let lens = RecencyLens;
        let assertion = create_assertion("Tesla", 1000);
        let resolution = lens.resolve(std::slice::from_ref(&assertion));

        assert!(resolution.winner.is_some());
        assert_eq!(resolution.winner.as_ref().map(|a| &a.subject), Some(&"Tesla".to_string()));
        assert_eq!(resolution.candidates_count, 1);
        assert!((resolution.resolution_confidence - 1.0).abs() < f32::EPSILON);
    }

    #[test]
    fn test_multiple_candidates_selects_newest() {
        let lens = RecencyLens;
        let old = create_assertion("Old", 1000);
        let new = create_assertion("New", 2000);
        let older = create_assertion("Older", 500);

        let resolution = lens.resolve(&[old, new.clone(), older]);

        assert!(resolution.winner.is_some());
        assert_eq!(resolution.winner.as_ref().map(|a| &a.subject), Some(&"New".to_string()));
        assert_eq!(resolution.candidates_count, 3);
    }

    #[test]
    fn test_deterministic_tiebreaker() {
        let lens = RecencyLens;

        let mut a1 = create_assertion("A", 1000);
        a1.source_hash = [1u8; 32];

        let mut a2 = create_assertion("B", 1000);
        a2.source_hash = [2u8; 32];

        // Same timestamp, should use source_hash as tiebreaker
        let resolution1 = lens.resolve(&[a1.clone(), a2.clone()]);
        let resolution2 = lens.resolve(&[a2.clone(), a1.clone()]);

        // Should be deterministic regardless of input order
        assert_eq!(
            resolution1.winner.as_ref().map(|a| &a.subject),
            resolution2.winner.as_ref().map(|a| &a.subject)
        );
    }

    #[test]
    fn test_lens_name() {
        let lens = RecencyLens;
        assert_eq!(lens.name(), "Recency");
    }
}
