//! The runtime CardDatabase: an immutable, Arc-shared lookup structure.

use std::sync::Arc;
use crate::core::keywords::Keywords;
use crate::core::types::CardId;
use super::card_types::{CardDefinition, CardType};
use super::commander_types::CommanderDefinition;

// =============================================================================
// CardDatabase
// =============================================================================

/// The complete card database - immutable, shared across game instances
#[derive(Clone)]
pub struct CardDatabase {
    pub(super) cards: Arc<Vec<CardDefinition>>,
    /// Lookup table: card ID -> index in cards vec
    pub(super) id_to_index: Arc<Vec<Option<usize>>>,
    /// Commander definitions (loaded separately from data/commanders/)
    pub(super) commanders: Arc<Vec<CommanderDefinition>>,
    /// Lookup table: commander ID -> index in commanders vec
    pub(super) commander_to_index: Arc<Vec<Option<usize>>>,
}

impl CardDatabase {
    /// Create a new database from a list of cards (without commanders)
    pub fn new(cards: Vec<CardDefinition>) -> Self {
        Self::new_with_commanders(cards, Vec::new())
    }

    /// Create a new database from cards and commanders
    pub fn new_with_commanders(mut cards: Vec<CardDefinition>, commanders: Vec<CommanderDefinition>) -> Self {
        // Pre-compute keyword bits for all creature cards
        for card in &mut cards {
            if let CardType::Creature { keywords, keywords_bits, level_up, .. } = &mut card.card_type {
                let refs: Vec<&str> = keywords.iter().map(|s| s.as_str()).collect();
                *keywords_bits = Keywords::from_names(&refs);
                if let Some(lu) = level_up {
                    let gain_refs: Vec<&str> = lu.keywords_gained.iter().map(|s| s.as_str()).collect();
                    lu.keywords_gained_bits = Keywords::from_names(&gain_refs);
                }
            }
        }

        // Find max card ID to size the lookup table
        let max_card_id = cards.iter().map(|c| c.id).max().unwrap_or(0) as usize;

        // Build card lookup table
        let mut id_to_index = vec![None; max_card_id + 1];
        for (index, card) in cards.iter().enumerate() {
            id_to_index[card.id as usize] = Some(index);
        }

        // Find max commander ID to size the lookup table
        let max_commander_id = commanders.iter().map(|c| c.id).max().unwrap_or(0) as usize;

        // Build commander lookup table
        let mut commander_to_index = vec![None; max_commander_id + 1];
        for (index, commander) in commanders.iter().enumerate() {
            commander_to_index[commander.id as usize] = Some(index);
        }

        Self {
            cards: Arc::new(cards),
            id_to_index: Arc::new(id_to_index),
            commanders: Arc::new(commanders),
            commander_to_index: Arc::new(commander_to_index),
        }
    }

    /// Create an empty database (for testing)
    pub fn empty() -> Self {
        Self {
            cards: Arc::new(Vec::new()),
            id_to_index: Arc::new(Vec::new()),
            commanders: Arc::new(Vec::new()),
            commander_to_index: Arc::new(Vec::new()),
        }
    }

    /// Get a card by ID (O(1) lookup)
    pub fn get(&self, id: CardId) -> Option<&CardDefinition> {
        let idx = id.0 as usize;
        if idx < self.id_to_index.len() {
            self.id_to_index[idx].map(|i| &self.cards[i])
        } else {
            None
        }
    }

    /// Get a commander by ID (O(1) lookup)
    pub fn get_commander(&self, id: CardId) -> Option<&CommanderDefinition> {
        let idx = id.0 as usize;
        if idx < self.commander_to_index.len() {
            self.commander_to_index[idx].map(|i| &self.commanders[i])
        } else {
            None
        }
    }

    /// Get total number of cards
    pub fn len(&self) -> usize {
        self.cards.len()
    }

    /// Get total number of commanders
    pub fn commander_count(&self) -> usize {
        self.commanders.len()
    }

    /// Check if database is empty (no cards)
    pub fn is_empty(&self) -> bool {
        self.cards.is_empty()
    }

    /// Iterate over all cards
    pub fn iter(&self) -> impl Iterator<Item = &CardDefinition> {
        self.cards.iter()
    }

    /// Iterate over all commanders
    pub fn iter_commanders(&self) -> impl Iterator<Item = &CommanderDefinition> {
        self.commanders.iter()
    }

    /// Get all card IDs
    pub fn card_ids(&self) -> impl Iterator<Item = CardId> + '_ {
        self.cards.iter().map(|c| CardId(c.id))
    }

    /// Get all commander IDs
    pub fn commander_ids(&self) -> impl Iterator<Item = CardId> + '_ {
        self.commanders.iter().map(|c| CardId(c.id))
    }
}

impl std::fmt::Debug for CardDatabase {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        f.debug_struct("CardDatabase")
            .field("num_cards", &self.cards.len())
            .field("num_commanders", &self.commanders.len())
            .finish()
    }
}
