//! Authority resolution logic for tier-aware conflict detection.

use std::collections::BTreeMap;

use stemedb_core::types::Assertion;

use crate::config::AphoriaConfig;
use crate::types::{ConflictingSource, TierBreakdown, Verdict};

use super::tier_verdict::TierAwareVerdict;

/// Compute tier-aware verdict from conflict information.
///
/// This function analyzes conflicts across multiple authority tiers and determines:
/// 1. Which tier has the highest authority (lowest tier number)
/// 2. What verdict each tier would produce
/// 3. Whether higher tiers agree with the code (making lower tier conflicts irrelevant)
///
/// # Arguments
/// * `tier_breakdown` - Per-tier summary of conflicting sources
/// * `overall_score` - Overall conflict score
/// * `config` - Configuration with thresholds
///
/// # Returns
/// A `TierAwareVerdict` that captures the tier-specific resolution logic.
pub fn compute_tier_aware_verdict(
    tier_breakdown: &BTreeMap<u8, TierBreakdown>,
    overall_score: f32,
    config: &AphoriaConfig,
) -> TierAwareVerdict {
    // Get the primary tier (lowest number = highest authority)
    let primary_tier = tier_breakdown.keys().min().copied().unwrap_or(3);

    // Compute the primary verdict based on overall score
    let primary_verdict = if overall_score >= config.thresholds.block {
        Verdict::Block
    } else if overall_score >= config.thresholds.flag {
        Verdict::Flag
    } else {
        Verdict::Pass
    };

    // If only one tier, return SingleTier verdict
    if tier_breakdown.len() == 1 {
        let breakdown = &tier_breakdown[&primary_tier];
        return TierAwareVerdict::from_single_tier(breakdown, primary_verdict);
    }

    // Multi-tier conflict - primary tier wins
    TierAwareVerdict::from_multi_tier(tier_breakdown, primary_tier, primary_verdict, overall_score)
}

/// Compute per-tier breakdown from conflicting sources.
///
/// Groups conflicts by tier number and computes summary statistics for each tier.
pub fn compute_tier_breakdown(conflicts: &[ConflictingSource]) -> BTreeMap<u8, TierBreakdown> {
    let mut by_tier: BTreeMap<u8, TierBreakdown> = BTreeMap::new();

    for source in conflicts {
        let tier = source.source_class.tier();
        let entry = by_tier.entry(tier).or_insert_with(|| TierBreakdown {
            tier,
            source_class: source.source_class,
            assertion_count: 0,
            max_confidence: 0.0,
        });

        entry.assertion_count += 1;
        if source.confidence > entry.max_confidence {
            entry.max_confidence = source.confidence;
        }
    }

    by_tier
}

/// Check if higher tiers agree with the code while lower tiers conflict.
///
/// This is used to detect cases where we can ignore lower-tier conflicts
/// because higher-tier authorities agree with the code.
///
/// Currently not implemented - reserved for Phase 3 (Gap Closure).
#[allow(dead_code)]
pub fn check_higher_tier_agreement(
    _tier_breakdown: &BTreeMap<u8, TierBreakdown>,
    _assertions: &[&Assertion],
) -> Option<TierAwareVerdict> {
    // TODO: Implement in Phase 3
    // This would check if higher tiers (0-2) agree with code
    // while lower tiers (3-5) conflict
    None
}

#[cfg(test)]
mod tests {
    use super::*;
    use stemedb_core::types::{ObjectValue, SourceClass};

    #[test]
    fn test_compute_tier_breakdown() {
        let conflicts = vec![
            ConflictingSource {
                path: "rfc://7519".to_string(),
                source_class: SourceClass::Clinical,
                value: ObjectValue::Boolean(true),
                confidence: 0.95,
                rfc_citation: Some("RFC 7519".to_string()),
                policy_source: None,
            },
            ConflictingSource {
                path: "team://guideline".to_string(),
                source_class: SourceClass::Expert,
                value: ObjectValue::Boolean(true),
                confidence: 0.70,
                rfc_citation: None,
                policy_source: None,
            },
            ConflictingSource {
                path: "team://guideline2".to_string(),
                source_class: SourceClass::Expert,
                value: ObjectValue::Boolean(true),
                confidence: 0.75,
                rfc_citation: None,
                policy_source: None,
            },
        ];

        let breakdown = compute_tier_breakdown(&conflicts);

        assert_eq!(breakdown.len(), 2);
        assert!(breakdown.contains_key(&1)); // Tier 1 (Clinical)
        assert!(breakdown.contains_key(&3)); // Tier 3 (Expert)

        let tier1 = &breakdown[&1];
        assert_eq!(tier1.assertion_count, 1);
        assert!((tier1.max_confidence - 0.95).abs() < f32::EPSILON);

        let tier3 = &breakdown[&3];
        assert_eq!(tier3.assertion_count, 2);
        assert!((tier3.max_confidence - 0.75).abs() < f32::EPSILON);
    }

    #[test]
    fn test_compute_tier_aware_verdict_single_tier() {
        let mut tier_breakdown = BTreeMap::new();
        tier_breakdown.insert(
            1,
            TierBreakdown {
                tier: 1,
                source_class: SourceClass::Clinical,
                assertion_count: 2,
                max_confidence: 0.95,
            },
        );

        let config = AphoriaConfig::default();
        let verdict = compute_tier_aware_verdict(&tier_breakdown, 0.92, &config);

        assert_eq!(verdict.effective_verdict(), Verdict::Block);
        assert_eq!(verdict.primary_tier(), 1);
    }

    #[test]
    fn test_compute_tier_aware_verdict_multi_tier() {
        let mut tier_breakdown = BTreeMap::new();
        tier_breakdown.insert(
            1,
            TierBreakdown {
                tier: 1,
                source_class: SourceClass::Clinical,
                assertion_count: 1,
                max_confidence: 0.95,
            },
        );
        tier_breakdown.insert(
            3,
            TierBreakdown {
                tier: 3,
                source_class: SourceClass::Expert,
                assertion_count: 2,
                max_confidence: 0.70,
            },
        );

        let config = AphoriaConfig::default();
        let verdict = compute_tier_aware_verdict(&tier_breakdown, 0.85, &config);

        assert_eq!(verdict.effective_verdict(), Verdict::Block);
        assert_eq!(verdict.primary_tier(), 1); // Tier 1 is primary (highest authority)
    }

    #[test]
    fn test_primary_tier_always_lowest() {
        let mut tier_breakdown = BTreeMap::new();
        tier_breakdown.insert(
            5,
            TierBreakdown {
                tier: 5,
                source_class: SourceClass::Anecdotal,
                assertion_count: 1,
                max_confidence: 0.3,
            },
        );
        tier_breakdown.insert(
            2,
            TierBreakdown {
                tier: 2,
                source_class: SourceClass::Observational,
                assertion_count: 1,
                max_confidence: 0.8,
            },
        );
        tier_breakdown.insert(
            3,
            TierBreakdown {
                tier: 3,
                source_class: SourceClass::Expert,
                assertion_count: 1,
                max_confidence: 0.6,
            },
        );

        let config = AphoriaConfig::default();
        let verdict = compute_tier_aware_verdict(&tier_breakdown, 0.7, &config);

        // Primary tier should be 2 (lowest number = highest authority)
        assert_eq!(verdict.primary_tier(), 2);
    }
}
