//! Tier-aware verdict types for authority-scoped conflict resolution.

use std::collections::BTreeMap;

use serde::{Deserialize, Serialize};

use crate::types::{TierBreakdown, Verdict};

/// Tier-aware verdict that shows what each tier says about a conflict.
///
/// This enables tier-specific conflict resolution where higher-tier authority
/// (lower tier number) wins. For example, Tier 1 (RFC) overrides Tier 3 (Expert).
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum TierAwareVerdict {
    /// Single tier conflict (all conflicts from same tier).
    ///
    /// This is the common case when all conflicting sources share the same authority tier.
    SingleTier {
        /// Tier number (0-5, with 2.5 for TeamPolicy).
        tier: u8,
        /// Human-readable tier name (e.g., "Tier 1 (Clinical/RFC)").
        tier_name: String,
        /// The verdict at this tier.
        verdict: Verdict,
        /// Number of sources at this tier.
        sources: usize,
        /// Maximum confidence among sources at this tier.
        max_confidence: f32,
    },

    /// Multi-tier conflict (conflicts from multiple tiers).
    ///
    /// When conflicts span multiple authority tiers, the primary tier (highest authority,
    /// lowest tier number) determines the effective verdict.
    MultiTier {
        /// Primary tier (lowest tier number = highest authority).
        primary_tier: u8,
        /// The verdict from the primary tier (this is the effective verdict).
        primary_verdict: Verdict,
        /// Per-tier verdicts: (tier, verdict, source_count, max_confidence).
        tier_verdicts: Vec<(u8, Verdict, usize, f32)>,
        /// Overall conflict score.
        conflict_score: f32,
    },

    /// Higher tier agrees with code (no conflict at high tier, conflict at low tier).
    ///
    /// This occurs when a higher-authority tier agrees with the code observation,
    /// but a lower-authority tier conflicts. The recommendation is to trust the
    /// higher tier and ignore the lower tier conflict.
    HigherTierAgreement {
        /// The tier that agrees with the code.
        agreeing_tier: u8,
        /// The tier that conflicts with the code.
        conflicting_tier: u8,
        /// Human-readable recommendation.
        recommendation: String,
    },
}

impl TierAwareVerdict {
    /// Get the effective verdict (what should be shown to user).
    ///
    /// For `SingleTier` and `MultiTier`, this is the verdict.
    /// For `HigherTierAgreement`, this is `Verdict::Pass` (no action needed).
    pub fn effective_verdict(&self) -> Verdict {
        match self {
            TierAwareVerdict::SingleTier { verdict, .. } => *verdict,
            TierAwareVerdict::MultiTier { primary_verdict, .. } => *primary_verdict,
            TierAwareVerdict::HigherTierAgreement { .. } => Verdict::Pass,
        }
    }

    /// Get the primary tier (highest authority tier involved).
    ///
    /// Returns the tier number (0-5) of the most authoritative source involved.
    pub fn primary_tier(&self) -> u8 {
        match self {
            TierAwareVerdict::SingleTier { tier, .. } => *tier,
            TierAwareVerdict::MultiTier { primary_tier, .. } => *primary_tier,
            TierAwareVerdict::HigherTierAgreement { agreeing_tier, .. } => *agreeing_tier,
        }
    }

    /// Format for display (used in CLI output).
    ///
    /// Returns a human-readable string describing the tier-aware verdict.
    pub fn display(&self) -> String {
        match self {
            TierAwareVerdict::SingleTier {
                tier_name, verdict, sources, max_confidence, ..
            } => {
                format!(
                    "{} {} - {} source{}, max confidence {:.2}",
                    verdict.symbol(),
                    tier_name,
                    sources,
                    if *sources == 1 { "" } else { "s" },
                    max_confidence
                )
            }
            TierAwareVerdict::MultiTier {
                primary_tier,
                primary_verdict,
                tier_verdicts,
                conflict_score,
            } => {
                let tier_name = format_tier_name(*primary_tier);
                let mut display = format!(
                    "{} {} (primary tier, score {:.2})",
                    primary_verdict.symbol(),
                    tier_name,
                    conflict_score
                );

                // Add tier breakdown summary
                if tier_verdicts.len() > 1 {
                    display.push_str(&format!(" - {} tiers involved", tier_verdicts.len()));
                }

                display
            }
            TierAwareVerdict::HigherTierAgreement { recommendation, .. } => {
                format!("✓ PASS - {}", recommendation)
            }
        }
    }

    /// Create a SingleTier verdict from tier breakdown.
    pub fn from_single_tier(breakdown: &TierBreakdown, verdict: Verdict) -> Self {
        Self::SingleTier {
            tier: breakdown.tier,
            tier_name: format_tier_name(breakdown.tier),
            verdict,
            sources: breakdown.assertion_count,
            max_confidence: breakdown.max_confidence,
        }
    }

    /// Create a MultiTier verdict from tier breakdown map.
    pub fn from_multi_tier(
        tier_breakdown: &BTreeMap<u8, TierBreakdown>,
        primary_tier: u8,
        primary_verdict: Verdict,
        conflict_score: f32,
    ) -> Self {
        let tier_verdicts: Vec<_> = tier_breakdown
            .iter()
            .map(|(tier, bd)| {
                // Compute what this tier alone would say
                // For now, we'll use the primary verdict for all tiers
                // In the future, this could be tier-specific based on thresholds
                let tier_verdict = if *tier == primary_tier {
                    primary_verdict
                } else {
                    // Lower-authority tiers might have different verdicts
                    // but for now, we'll keep it simple
                    primary_verdict
                };
                (*tier, tier_verdict, bd.assertion_count, bd.max_confidence)
            })
            .collect();

        Self::MultiTier { primary_tier, primary_verdict, tier_verdicts, conflict_score }
    }
}

impl Verdict {
    /// Get the symbol for this verdict.
    pub fn symbol(&self) -> &'static str {
        match self {
            Verdict::Block => "❌ BLOCK",
            Verdict::Flag => "⚠️  FLAG",
            Verdict::Pass => "✓  PASS",
            Verdict::Ack => "✓  ACK",
            Verdict::Drift => "🔄 DRIFT",
        }
    }
}

/// Format a tier number as a human-readable name.
///
/// Examples:
/// - Tier 0 → "Tier 0 (Regulatory)"
/// - Tier 1 → "Tier 1 (Clinical/RFC)"
/// - Tier 2 → "Tier 2 (Observational)"
/// - Tier 2.5 → "Tier 2.5 (TeamPolicy)"
/// - Tier 3 → "Tier 3 (Expert)"
/// - Tier 4 → "Tier 4 (Community)"
/// - Tier 5 → "Tier 5 (Anecdotal)"
pub fn format_tier_name(tier: u8) -> String {
    match tier {
        0 => "Tier 0 (Regulatory)".to_string(),
        1 => "Tier 1 (Clinical/RFC)".to_string(),
        2 => "Tier 2 (Observational/TeamPolicy)".to_string(),
        3 => "Tier 3 (Expert)".to_string(),
        4 => "Tier 4 (Community)".to_string(),
        5 => "Tier 5 (Anecdotal)".to_string(),
        _ => format!("Tier {tier}"),
    }
}

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

    #[test]
    fn test_single_tier_verdict() {
        let breakdown = TierBreakdown {
            tier: 1,
            source_class: SourceClass::Clinical,
            assertion_count: 3,
            max_confidence: 0.95,
        };

        let verdict = TierAwareVerdict::from_single_tier(&breakdown, Verdict::Block);

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

        let display = verdict.display();
        assert!(display.contains("BLOCK"));
        assert!(display.contains("Tier 1"));
        assert!(display.contains("3 sources"));
    }

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

        let verdict = TierAwareVerdict::from_multi_tier(&tier_breakdown, 1, Verdict::Block, 0.92);

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

        let display = verdict.display();
        assert!(display.contains("BLOCK"));
        assert!(display.contains("Tier 1"));
        assert!(display.contains("2 tiers"));
    }

    #[test]
    fn test_higher_tier_agreement() {
        let verdict = TierAwareVerdict::HigherTierAgreement {
            agreeing_tier: 1,
            conflicting_tier: 4,
            recommendation: "Tier 1 RFC agrees with your code, ignore Tier 4".to_string(),
        };

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

        let display = verdict.display();
        assert!(display.contains("PASS"));
        assert!(display.contains("Tier 1"));
    }

    #[test]
    fn test_primary_tier_is_lowest_number() {
        let tiers = vec![1u8, 3, 5];
        let primary = *tiers.iter().min().unwrap();
        assert_eq!(primary, 1);
    }

    #[test]
    fn test_format_tier_name() {
        assert_eq!(format_tier_name(0), "Tier 0 (Regulatory)");
        assert_eq!(format_tier_name(1), "Tier 1 (Clinical/RFC)");
        assert_eq!(format_tier_name(2), "Tier 2 (Observational/TeamPolicy)");
        assert_eq!(format_tier_name(3), "Tier 3 (Expert)");
        assert_eq!(format_tier_name(4), "Tier 4 (Community)");
        assert_eq!(format_tier_name(5), "Tier 5 (Anecdotal)");
    }

    #[test]
    fn test_verdict_symbols() {
        assert_eq!(Verdict::Block.symbol(), "❌ BLOCK");
        assert_eq!(Verdict::Flag.symbol(), "⚠️  FLAG");
        assert_eq!(Verdict::Pass.symbol(), "✓  PASS");
        assert_eq!(Verdict::Ack.symbol(), "✓  ACK");
        assert_eq!(Verdict::Drift.symbol(), "🔄 DRIFT");
    }
}
