//! Matchup generation for balance testing.
//!
//! Provides utilities for generating all possible deck matchups at various
//! granularities (all decks, faction pairs, filtered subsets).

use crate::decks::{DeckDefinition, DeckRegistry, Faction};

use super::matchup::UnifiedMatchup;

/// Builder for generating matchups at various granularities.
///
/// # Example
///
/// ```ignore
/// let builder = MatchupBuilder::new(&deck_registry);
///
/// // Generate all 66 unique matchups (12 decks, no mirrors)
/// let all_matchups = builder.build_all_matchups();
///
/// // Generate only Argentum vs Symbiote matchups
/// let faction_matchups = builder.build_faction_matchups(Faction::Argentum, Faction::Symbiote);
///
/// // Filter to specific matchups
/// let filtered = MatchupBuilder::filter_matchups(all_matchups, "broodmother");
/// ```
pub struct MatchupBuilder<'a> {
    registry: &'a DeckRegistry,
}

impl<'a> MatchupBuilder<'a> {
    /// Create a new matchup builder for the given deck registry.
    pub fn new(registry: &'a DeckRegistry) -> Self {
        Self { registry }
    }

    /// Get all competitive decks for balance testing.
    fn competitive_decks(&self) -> Vec<&DeckDefinition> {
        self.registry.decks().collect()
    }

    /// Build all deck-vs-deck matchups (excluding same-deck mirrors).
    ///
    /// With 12 competitive decks, this produces 12×11/2 = 66 unique matchups.
    ///
    /// Matchups are ordered consistently:
    /// - By faction pair (Argentum-Symbiote, Argentum-Obsidion, Symbiote-Obsidion)
    /// - Then by deck within faction (alphabetically by deck ID)
    pub fn build_all_matchups(&self) -> Vec<UnifiedMatchup> {
        let all_decks: Vec<&DeckDefinition> = self.competitive_decks();
        let mut matchups = Vec::new();

        for (i, deck1) in all_decks.iter().enumerate() {
            for deck2 in all_decks.iter().skip(i + 1) {
                // Skip same-deck mirrors
                if deck1.id == deck2.id {
                    continue;
                }
                matchups.push(UnifiedMatchup::new((*deck1).clone(), (*deck2).clone()));
            }
        }

        matchups
    }

    /// Build matchups for a specific faction pair.
    ///
    /// If the factions are the same, generates intra-faction matchups
    /// (excluding mirrors).
    pub fn build_faction_matchups(&self, f1: Faction, f2: Faction) -> Vec<UnifiedMatchup> {
        let decks1: Vec<_> = self.registry.decks_for_faction(f1)
            .into_iter().collect();
        let decks2: Vec<_> = self.registry.decks_for_faction(f2)
            .into_iter().collect();

        let mut matchups = Vec::new();

        if f1 == f2 {
            // Intra-faction: avoid duplicates and mirrors
            for (i, deck1) in decks1.iter().enumerate() {
                for deck2 in decks1.iter().skip(i + 1) {
                    if deck1.id != deck2.id {
                        matchups.push(UnifiedMatchup::new((*deck1).clone(), (*deck2).clone()));
                    }
                }
            }
        } else {
            // Inter-faction: all combinations
            for deck1 in &decks1 {
                for deck2 in &decks2 {
                    matchups.push(UnifiedMatchup::new((*deck1).clone(), (*deck2).clone()));
                }
            }
        }

        matchups
    }

    /// Build all inter-faction matchups (excludes same-faction matchups).
    ///
    /// This is useful for focusing on cross-faction balance without
    /// intra-faction matchups.
    pub fn build_inter_faction_matchups(&self) -> Vec<UnifiedMatchup> {
        let factions = Faction::all_factions();
        let mut matchups = Vec::new();

        for (i, &f1) in factions.iter().enumerate() {
            for &f2 in factions.iter().skip(i + 1) {
                matchups.extend(self.build_faction_matchups(f1, f2));
            }
        }

        matchups
    }

    /// Build all intra-faction matchups (same-faction only, excluding mirrors).
    ///
    /// With 4 decks per faction, produces 4×3/2 = 6 matchups per faction,
    /// for a total of 18 intra-faction matchups.
    pub fn build_intra_faction_matchups(&self) -> Vec<UnifiedMatchup> {
        let mut matchups = Vec::new();

        for &faction in Faction::all_factions() {
            matchups.extend(self.build_faction_matchups(faction, faction));
        }

        matchups
    }

    /// Build all matchups where a specific deck is player 1.
    ///
    /// With 12 decks, this produces 11 matchups (the deck vs all others).
    /// Used for parallelizing benchmark jobs across decks.
    pub fn build_matchups_for_deck1(&self, deck_id: &str) -> Vec<UnifiedMatchup> {
        let all_decks: Vec<&DeckDefinition> = self.competitive_decks();
        let mut matchups = Vec::new();

        // Find the deck1
        let Some(deck1) = all_decks.iter().find(|d| d.id == deck_id) else {
            return matchups;
        };

        // Create matchups against all other decks
        for deck2 in &all_decks {
            if deck2.id != deck1.id {
                matchups.push(UnifiedMatchup::new((*deck1).clone(), (*deck2).clone()));
            }
        }

        matchups
    }

    /// Filter matchups by pattern.
    ///
    /// Supports matching by:
    /// - Deck ID (e.g., "broodmother" matches "broodmother_pack")
    /// - Faction pair (e.g., "argentum-symbiote" or "symbiote-argentum")
    /// - Single faction (e.g., "argentum" matches all matchups involving Argentum)
    ///
    /// Pattern matching is case-insensitive.
    pub fn filter_matchups(matchups: Vec<UnifiedMatchup>, pattern: &str) -> Vec<UnifiedMatchup> {
        let pattern_lower = pattern.to_lowercase();

        matchups
            .into_iter()
            .filter(|m| {
                let id_lower = m.id.to_lowercase();
                let f1 = m.faction1_or_neutral().as_tag();
                let f2 = m.faction2_or_neutral().as_tag();

                // Match by deck ID (partial match)
                if id_lower.contains(&pattern_lower) {
                    return true;
                }

                // Match by faction pair (either order)
                let faction_pair = format!("{}-{}", f1, f2);
                let faction_pair_rev = format!("{}-{}", f2, f1);
                if faction_pair.contains(&pattern_lower)
                    || faction_pair_rev.contains(&pattern_lower)
                {
                    return true;
                }

                // Match by single faction
                if f1 == pattern_lower || f2 == pattern_lower {
                    return true;
                }

                false
            })
            .collect()
    }

    /// Get a summary of matchup counts.
    pub fn matchup_summary(&self) -> MatchupSummary {
        let all = self.build_all_matchups();
        let inter = self.build_inter_faction_matchups();
        let intra = self.build_intra_faction_matchups();

        MatchupSummary {
            total_decks: self.competitive_decks().len(),
            total_matchups: all.len(),
            inter_faction_matchups: inter.len(),
            intra_faction_matchups: intra.len(),
        }
    }
}

/// Summary of matchup counts for reporting.
#[derive(Debug, Clone)]
pub struct MatchupSummary {
    /// Total number of decks in the registry.
    pub total_decks: usize,
    /// Total unique matchups (excluding mirrors).
    pub total_matchups: usize,
    /// Matchups between different factions.
    pub inter_faction_matchups: usize,
    /// Matchups within the same faction.
    pub intra_faction_matchups: usize,
}

impl std::fmt::Display for MatchupSummary {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(
            f,
            "{} decks, {} matchups ({} inter-faction, {} intra-faction)",
            self.total_decks,
            self.total_matchups,
            self.inter_faction_matchups,
            self.intra_faction_matchups
        )
    }
}