//! Card-specific type definitions.
//!
//! Covers effects, abilities, tokens, level-up, and the core `CardDefinition`.

use serde::{Deserialize, Serialize};
use crate::core::types::*;
use crate::core::keywords::Keywords;
use crate::core::effects::{Condition, Trigger, TargetingRule, CreatureFilter, TokenDefinition, TokenAbility, TokenEffect};

// =============================================================================
// Ability & Effect Types
// =============================================================================

/// Definition of a triggered ability on a creature
#[derive(Clone, Debug, Deserialize, Serialize)]
pub struct AbilityDefinition {
    pub trigger: Trigger,
    #[serde(default)]
    pub targeting: TargetingRule,
    /// Essence cost to activate (only used for Trigger::Activated abilities)
    #[serde(default)]
    pub essence_cost: u8,
    pub effects: Vec<EffectDefinition>,
    /// Effects that trigger conditionally based on the result of the primary effects
    #[serde(default)]
    pub conditional_effects: Vec<ConditionalEffectGroup>,
}

/// A group of effects that trigger if a condition is met
#[derive(Clone, Debug, Deserialize, Serialize)]
pub struct ConditionalEffectGroup {
    /// The condition that must be met for these effects to trigger
    pub condition: Condition,
    /// The effects to apply if the condition is met
    pub effects: Vec<EffectDefinition>,
}

// =============================================================================
// Token Types
// =============================================================================

/// Token definition for YAML (uses string keywords instead of bitmask)
#[derive(Clone, Debug, Deserialize, Serialize)]
pub struct TokenDef {
    /// Display name for the token
    pub name: String,
    /// Attack value
    pub attack: u8,
    /// Health value
    pub health: u8,
    /// Keywords as string list (converted to bitmask at runtime)
    #[serde(default)]
    pub keywords: Vec<String>,
    /// Activated abilities for this token (optional)
    #[serde(default)]
    pub abilities: Vec<TokenAbilityDef>,
}

/// Token ability definition for YAML parsing
#[derive(Clone, Debug, Deserialize, Serialize)]
pub struct TokenAbilityDef {
    /// Display name for the ability
    pub name: String,
    /// Essence cost to activate
    #[serde(default)]
    pub essence_cost: u8,
    /// What this ability can target
    #[serde(default)]
    pub targeting: TargetingRule,
    /// Effects to apply when activated
    pub effects: Vec<TokenEffectDef>,
}

/// Token effect definition for YAML parsing
#[derive(Clone, Debug, Deserialize, Serialize)]
#[serde(tag = "type", rename_all = "snake_case")]
pub enum TokenEffectDef {
    /// Destroy the source creature (self-sacrifice)
    DestroySelf,
    /// Deal damage to target
    Damage { amount: u8 },
    /// Apply a stat debuff to target (negative values reduce stats)
    Debuff { attack: i8, health: i8 },
    /// Heal the ability owner's commander
    HealSelf { amount: u8 },
}

impl TokenDef {
    /// Convert to TokenDefinition with keywords as bitmask
    pub fn to_token_definition(&self) -> TokenDefinition {
        let keyword_refs: Vec<&str> = self.keywords.iter().map(|s| s.as_str()).collect();
        let keywords = Keywords::from_names(&keyword_refs);
        TokenDefinition {
            name: self.name.clone(),
            attack: self.attack,
            health: self.health,
            keywords: keywords.0,
            abilities: self.abilities.iter().map(|a| a.to_token_ability()).collect(),
        }
    }
}

impl TokenAbilityDef {
    /// Convert to runtime TokenAbility
    pub fn to_token_ability(&self) -> TokenAbility {
        TokenAbility {
            name: self.name.clone(),
            essence_cost: self.essence_cost,
            targeting: self.targeting.clone(),
            effects: self.effects.iter().map(|e| e.to_token_effect()).collect(),
        }
    }
}

impl TokenEffectDef {
    /// Convert to runtime TokenEffect
    pub fn to_token_effect(&self) -> TokenEffect {
        match self {
            TokenEffectDef::DestroySelf => TokenEffect::DestroySelf,
            TokenEffectDef::Damage { amount } => TokenEffect::Damage { amount: *amount },
            TokenEffectDef::Debuff { attack, health } => TokenEffect::Debuff { attack: *attack, health: *health },
            TokenEffectDef::HealSelf { amount } => TokenEffect::HealSelf { amount: *amount },
        }
    }
}

// =============================================================================
// Effect Definitions
// =============================================================================

/// Definition of an effect (serializable from YAML)
#[derive(Clone, Debug, Deserialize, Serialize)]
#[serde(tag = "type", rename_all = "snake_case")]
pub enum EffectDefinition {
    Damage {
        amount: u8,
        #[serde(default, skip_serializing_if = "Option::is_none")]
        filter: Option<CreatureFilter>,
    },
    Heal {
        amount: u8,
        #[serde(default, skip_serializing_if = "Option::is_none")]
        filter: Option<CreatureFilter>,
    },
    Draw { count: u8 },
    BuffStats {
        attack: i8,
        health: i8,
        #[serde(default, skip_serializing_if = "Option::is_none")]
        filter: Option<CreatureFilter>,
    },
    /// Temporary stat buff that resets at end of the caster's turn.
    /// Used by Prepare abilities — the creature charges up for one big attack.
    BuffStatsTemporal {
        attack: i8,
        health: i8,
    },
    Destroy {
        #[serde(default, skip_serializing_if = "Option::is_none")]
        filter: Option<CreatureFilter>,
    },
    GrantKeyword {
        keyword: String,
        #[serde(default, skip_serializing_if = "Option::is_none")]
        filter: Option<CreatureFilter>,
    },
    RemoveKeyword {
        keyword: String,
        #[serde(default, skip_serializing_if = "Option::is_none")]
        filter: Option<CreatureFilter>,
    },
    Silence {
        #[serde(default, skip_serializing_if = "Option::is_none")]
        filter: Option<CreatureFilter>,
    },
    GainEssence { amount: u8 },
    RefreshCreature,
    Bounce {
        #[serde(default, skip_serializing_if = "Option::is_none")]
        filter: Option<CreatureFilter>,
    },
    /// Summon a token creature
    SummonToken {
        token: TokenDef,
    },
    /// Transform target creature into a token
    Transform {
        into: TokenDef,
    },
    /// Create a copy of target creature
    Copy,
}

// =============================================================================
// Level-Up Types
// =============================================================================

/// Trigger condition for a creature's level-up
#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
#[serde(rename_all = "PascalCase")]
pub enum LevelUpTrigger {
    /// Gain 1 progress each time this creature kills an enemy creature
    OnKill,
    /// Gain progress equal to damage dealt each time this creature deals damage
    OnDealDamage,
}

/// Level-up definition for a creature card
///
/// When a creature accumulates `threshold` progress, it permanently gains
/// `attack_bonus`/`health_bonus` stats and `keywords_gained_bits` keywords.
#[derive(Clone, Debug, Deserialize, Serialize)]
pub struct LevelUpDefinition {
    pub trigger: LevelUpTrigger,
    pub threshold: u8,
    pub attack_bonus: i8,
    pub health_bonus: i8,
    /// Keyword names as listed in YAML (e.g. ["Rush", "Guard"])
    #[serde(default)]
    pub keywords_gained: Vec<String>,
    /// Precomputed bitmask from `keywords_gained` (filled at load time, not serialized)
    #[serde(skip)]
    pub keywords_gained_bits: Keywords,
}

// =============================================================================
// Passive Effect Types (for Supports)
// =============================================================================

/// Definition of a passive effect (for supports)
#[derive(Clone, Debug, Deserialize, Serialize)]
pub struct PassiveEffectDefinition {
    pub modifier: PassiveModifier,
}

/// Types of passive modifiers for supports
#[derive(Clone, Debug, Deserialize, Serialize)]
#[serde(rename_all = "snake_case")]
pub enum PassiveModifier {
    AttackBonus(i8),
    HealthBonus(i8),
    GrantKeyword(String),
}

// =============================================================================
// Card Type Enum
// =============================================================================

/// Card type with type-specific data
#[derive(Clone, Debug, Deserialize, Serialize)]
#[serde(tag = "card_type", rename_all = "snake_case")]
pub enum CardType {
    Creature {
        attack: u8,
        health: u8,
        #[serde(default)]
        keywords: Vec<String>,
        /// Cached keyword bitmask (computed at load time, not serialized)
        #[serde(skip)]
        keywords_bits: Keywords,
        #[serde(default)]
        abilities: Vec<AbilityDefinition>,
        /// Optional level-up definition (progress counter + stat bonus on threshold)
        #[serde(default)]
        level_up: Option<LevelUpDefinition>,
    },
    Spell {
        #[serde(default)]
        targeting: TargetingRule,
        effects: Vec<EffectDefinition>,
        /// Effects that trigger conditionally based on the result of the primary effects
        #[serde(default)]
        conditional_effects: Vec<ConditionalEffectGroup>,
    },
    Support {
        durability: u8,
        #[serde(default)]
        passive_effects: Vec<PassiveEffectDefinition>,
        #[serde(default)]
        triggered_effects: Vec<AbilityDefinition>,
    },
    Ritual {
        /// Target selection rule. `NoTarget` rituals fire without a board target
        /// (e.g. Draw, GainEssence). `TargetEnemyCreatureOrFace` lets the caster
        /// pick an opponent creature slot or face at cast time.
        #[serde(default)]
        targeting: TargetingRule,
        effects: Vec<EffectDefinition>,
        #[serde(default)]
        conditional_effects: Vec<ConditionalEffectGroup>,
    },
}

// =============================================================================
// CardDefinition & CardSet
// =============================================================================

/// Complete definition of a card
#[derive(Clone, Debug, Deserialize, Serialize)]
pub struct CardDefinition {
    pub id: u16,
    pub name: String,
    pub cost: u8,
    /// Overload cost: pay this many life instead of Essence to play this card.
    /// `None` means the card cannot be Overloaded. Life must exceed this value
    /// (cannot Overload to 0 or negative).
    #[serde(default)]
    pub overload_cost: Option<u8>,
    #[serde(flatten)]
    pub card_type: CardType,
    #[serde(default)]
    pub rarity: Rarity,
    #[serde(default)]
    pub tags: Vec<String>,
    /// Flanking: creature deals +1 damage when attacking with an ally in an adjacent slot.
    /// Suppressed by Silence. Only meaningful for creature cards.
    #[serde(default)]
    pub flanking: bool,
}

impl CardDefinition {
    /// Get keywords for a creature card (returns cached bitmask).
    /// The bitmask is pre-computed at card load time for performance.
    #[inline]
    pub fn keywords(&self) -> Keywords {
        match &self.card_type {
            CardType::Creature { keywords_bits, .. } => *keywords_bits,
            _ => Keywords::none(),
        }
    }

    /// Get attack for a creature card
    pub fn attack(&self) -> Option<u8> {
        match &self.card_type {
            CardType::Creature { attack, .. } => Some(*attack),
            _ => None,
        }
    }

    /// Get health for a creature card
    pub fn health(&self) -> Option<u8> {
        match &self.card_type {
            CardType::Creature { health, .. } => Some(*health),
            _ => None,
        }
    }

    /// Get durability for a support card
    pub fn durability(&self) -> Option<u8> {
        match &self.card_type {
            CardType::Support { durability, .. } => Some(*durability),
            _ => None,
        }
    }

    /// Check if this creature card has the Flanking property.
    /// Returns false for non-creature cards.
    #[inline]
    pub fn has_flanking(&self) -> bool {
        self.flanking && matches!(self.card_type, CardType::Creature { .. })
    }

    /// Check if this is a creature card
    pub fn is_creature(&self) -> bool {
        matches!(self.card_type, CardType::Creature { .. })
    }

    /// Check if this is a spell card
    pub fn is_spell(&self) -> bool {
        matches!(self.card_type, CardType::Spell { .. })
    }

    /// Check if this is a support card
    pub fn is_support(&self) -> bool {
        matches!(self.card_type, CardType::Support { .. })
    }

    /// Check if this is a ritual card
    pub fn is_ritual(&self) -> bool {
        matches!(self.card_type, CardType::Ritual { .. })
    }

    /// Get effects for a ritual card
    pub fn ritual_effects(&self) -> Option<&[EffectDefinition]> {
        match &self.card_type {
            CardType::Ritual { effects, .. } => Some(effects),
            _ => None,
        }
    }

    /// Get the level-up definition for this card, if any
    pub fn level_up(&self) -> Option<&LevelUpDefinition> {
        if let CardType::Creature { level_up, .. } = &self.card_type {
            level_up.as_ref()
        } else {
            None
        }
    }

    /// Get targeting rule for a spell
    pub fn spell_targeting(&self) -> Option<&TargetingRule> {
        match &self.card_type {
            CardType::Spell { targeting, .. } => Some(targeting),
            _ => None,
        }
    }

    /// Get effects for a spell
    pub fn spell_effects(&self) -> Option<&[EffectDefinition]> {
        match &self.card_type {
            CardType::Spell { effects, .. } => Some(effects),
            _ => None,
        }
    }

    /// Get abilities for a creature
    pub fn creature_abilities(&self) -> Option<&[AbilityDefinition]> {
        match &self.card_type {
            CardType::Creature { abilities, .. } => Some(abilities),
            _ => None,
        }
    }

    /// Get passive effects for a support
    pub fn support_passives(&self) -> Option<&[PassiveEffectDefinition]> {
        match &self.card_type {
            CardType::Support { passive_effects, .. } => Some(passive_effects),
            _ => None,
        }
    }
}

/// Card set loaded from YAML (container for multiple cards)
#[derive(Clone, Debug, Deserialize, Serialize)]
pub struct CardSet {
    pub name: String,
    pub cards: Vec<CardDefinition>,
}
