//! Unit tests for the Volatile keyword.
//!
//! Volatile: When this creature dies, deal 2 damage to all enemy creatures.
//!
//! This module covers two independent death paths:
//! - Combat death: via `resolve_combat` → `process_creature_death`
//! - Spell/ability death: via `EffectQueue::process_all` → `process_deaths`
//!
//! The spell-kill path had a bug (Volatile did not fire) that was fixed in the
//! same commit that introduced these tests. Both paths must be covered to prevent
//! regression.

use cardgame::cards::{CardDatabase, CardDefinition, CardType};
use cardgame::combat::resolve_combat;
use cardgame::effects::{Effect, EffectSource, EffectTarget};
use cardgame::engine::EffectQueue;
use cardgame::keywords::Keywords;
use cardgame::state::{Creature, CreatureStatus, GameState};
use cardgame::types::{CardId, CreatureInstanceId, PlayerId, Rarity, Slot};

// ============================================================================
// Helpers
// ============================================================================

fn make_card(id: u16, attack: u8, health: u8) -> CardDefinition {
    CardDefinition {
        id,
        name: format!("Creature {id}"),
        cost: 2,
        overload_cost: None,
        card_type: CardType::Creature {
            attack,
            health,
            keywords: vec![],
            keywords_bits: Keywords::none(),
            abilities: vec![],
            level_up: None,
        },
        rarity: Rarity::Common,
        flanking: false,
        tags: vec![],
    }
}

fn test_db() -> CardDatabase {
    // Card 1: a vanilla 1/1 (fodder for attacks / spell kills)
    // Card 2: a 2/2 Volatile creature
    // Card 3: a 1/3 (survives 2 Volatile damage)
    // Card 4: a 1/2 (dies to exactly 2 Volatile damage)
    CardDatabase::new(vec![
        make_card(1, 1, 1),
        make_card(2, 2, 2),
        make_card(3, 1, 3),
        make_card(4, 1, 2),
    ])
}

fn make_creature(
    instance_id: u32,
    card_id: u16,
    owner: PlayerId,
    slot: u8,
    attack: i8,
    health: i8,
    keywords: Keywords,
) -> Creature {
    Creature {
        instance_id: CreatureInstanceId(instance_id),
        card_id: CardId(card_id),
        owner,
        slot: Slot(slot),
        attack,
        current_health: health,
        max_health: health,
        base_attack: attack as u8,
        base_health: health as u8,
        keywords,
        status: CreatureStatus::default(),
        turn_played: 1,
        frenzy_stacks: 0,
        temp_attack_bonus: 0,
        level: 0,
        progress: 0,
        token_data: None,
    }
}

fn test_state() -> GameState {
    let mut state = GameState::new();
    state.players[0].life = 30;
    state.players[1].life = 30;
    state.current_turn = 2;
    state
}

// ============================================================================
// Combat-death path (resolve_combat)
// ============================================================================

/// Volatile creature dies in combat → all enemy creatures take 2 damage.
#[test]
fn volatile_combat_death_damages_all_enemy_creatures() {
    let db = test_db();
    let mut state = test_state();
    let mut queue = EffectQueue::new();

    // P1: 3/3 attacker — kills P2's Volatile 2/2, takes 2 damage (survives at 1 health)
    state.players[0].creatures.push(make_creature(1, 1, PlayerId::PLAYER_ONE, 0, 3, 3, Keywords::none()));
    state.players[1].creatures.push(make_creature(
        2, 2, PlayerId::PLAYER_TWO, 0, 2, 2, Keywords::none().with_volatile(),
    ));
    // P1 bystander with 3 health — should absorb the 2 Volatile damage
    state.players[0].creatures.push(make_creature(3, 3, PlayerId::PLAYER_ONE, 1, 1, 3, Keywords::none()));

    // P1 Slot(0) (3/3) attacks P2 Slot(0) (Volatile 2/2): defender dies, Volatile fires
    resolve_combat(&mut state, &db, &mut queue, PlayerId::PLAYER_ONE, Slot(0), Slot(0), None);
    queue.process_all(&mut state, &db);

    // P1's 1/3 bystander should have taken 2 Volatile damage → 1 health remaining
    let bystander = state.players[0].get_creature(Slot(1)).expect("bystander should survive");
    assert_eq!(bystander.current_health, 1, "Volatile should deal 2 to enemy bystander (3-2=1)");
}

/// Volatile creature dies in combat, and the 2 Volatile damage kills another enemy.
#[test]
fn volatile_combat_death_can_kill_enemy_creature() {
    let db = test_db();
    let mut state = test_state();
    let mut queue = EffectQueue::new();

    // P1: 3/3 attacker kills P2's Volatile 2/2
    state.players[0].creatures.push(make_creature(1, 1, PlayerId::PLAYER_ONE, 0, 3, 3, Keywords::none()));
    state.players[1].creatures.push(make_creature(
        2, 2, PlayerId::PLAYER_TWO, 0, 2, 2, Keywords::none().with_volatile(),
    ));
    // P1 bystander with exactly 2 health — should die from Volatile
    state.players[0].creatures.push(make_creature(3, 4, PlayerId::PLAYER_ONE, 1, 1, 2, Keywords::none()));

    resolve_combat(&mut state, &db, &mut queue, PlayerId::PLAYER_ONE, Slot(0), Slot(0), None);
    queue.process_all(&mut state, &db);

    // Bystander should be dead (2 - 2 = 0)
    assert!(state.players[0].get_creature(Slot(1)).is_none(), "bystander should die from Volatile explosion");
}

/// Silenced Volatile creature dies in combat → Volatile does NOT fire.
#[test]
fn silenced_volatile_does_not_fire_on_combat_death() {
    let db = test_db();
    let mut state = test_state();
    let mut queue = EffectQueue::new();

    let mut volatile_creature = make_creature(
        2, 2, PlayerId::PLAYER_TWO, 0, 2, 2, Keywords::none().with_volatile(),
    );
    volatile_creature.status.set_silenced(true);

    // Use a 3/3 attacker to ensure the Volatile creature actually dies
    state.players[0].creatures.push(make_creature(1, 1, PlayerId::PLAYER_ONE, 0, 3, 3, Keywords::none()));
    state.players[1].creatures.push(volatile_creature);
    state.players[0].creatures.push(make_creature(3, 4, PlayerId::PLAYER_ONE, 1, 1, 2, Keywords::none()));

    resolve_combat(&mut state, &db, &mut queue, PlayerId::PLAYER_ONE, Slot(0), Slot(0), None);
    queue.process_all(&mut state, &db);

    // Bystander should be alive (silenced Volatile cannot fire)
    let bystander = state.players[0].get_creature(Slot(1)).expect("bystander should survive");
    assert_eq!(bystander.current_health, 2, "silenced Volatile should not deal damage");
}

/// Volatile does NOT damage the dying creature's own side (no friendly fire).
#[test]
fn volatile_no_friendly_fire_on_combat_death() {
    let db = test_db();
    let mut state = test_state();
    let mut queue = EffectQueue::new();

    // Use a 3/3 attacker so the Volatile defender actually dies
    state.players[0].creatures.push(make_creature(1, 1, PlayerId::PLAYER_ONE, 0, 3, 3, Keywords::none()));
    state.players[1].creatures.push(make_creature(
        2, 2, PlayerId::PLAYER_TWO, 0, 2, 2, Keywords::none().with_volatile(),
    ));
    // P2 ally bystander — should NOT take Volatile damage
    state.players[1].creatures.push(make_creature(3, 4, PlayerId::PLAYER_TWO, 1, 1, 2, Keywords::none()));

    resolve_combat(&mut state, &db, &mut queue, PlayerId::PLAYER_ONE, Slot(0), Slot(0), None);
    queue.process_all(&mut state, &db);

    // P2 ally bystander should be untouched
    let ally_bystander = state.players[1].get_creature(Slot(1)).expect("P2 bystander should survive");
    assert_eq!(ally_bystander.current_health, 2, "Volatile must not hit friendly creatures");
}

// ============================================================================
// Spell/ability-death path (EffectQueue) — REGRESSION TESTS
// These cover the bug that was fixed: Volatile was not firing on spell kills.
// ============================================================================

/// REGRESSION: Volatile creature killed by a spell/ability deals 2 to all enemies.
#[test]
fn volatile_spell_death_damages_all_enemy_creatures() {
    let db = test_db();
    let mut state = test_state();
    let mut queue = EffectQueue::new();

    // P1: Volatile 2/2 that will be killed by a spell
    state.players[0].creatures.push(make_creature(
        1, 2, PlayerId::PLAYER_ONE, 0, 2, 2, Keywords::none().with_volatile(),
    ));
    // P2: bystander with 3 health, should absorb 2 Volatile damage
    state.players[1].creatures.push(make_creature(2, 3, PlayerId::PLAYER_TWO, 0, 1, 3, Keywords::none()));

    // Kill the Volatile creature via the spell/ability death path
    queue.push(
        Effect::Damage {
            target: EffectTarget::Creature { owner: PlayerId::PLAYER_ONE, slot: Slot(0) },
            amount: 10,
            filter: None,
        },
        EffectSource::System,
    );
    queue.process_all(&mut state, &db);

    // P1's Volatile creature should be gone
    assert!(state.players[0].get_creature(Slot(0)).is_none(), "Volatile creature should be dead");
    // P2 bystander should have taken 2 Volatile damage
    let bystander = state.players[1].get_creature(Slot(0)).expect("P2 bystander should survive");
    assert_eq!(bystander.current_health, 1, "Volatile should deal 2 to enemy bystander (3-2=1)");
}

/// REGRESSION: Volatile spell-kill can kill a second creature (chain reaction).
#[test]
fn volatile_spell_death_can_kill_enemy_creature() {
    let db = test_db();
    let mut state = test_state();
    let mut queue = EffectQueue::new();

    state.players[0].creatures.push(make_creature(
        1, 2, PlayerId::PLAYER_ONE, 0, 2, 2, Keywords::none().with_volatile(),
    ));
    // P2 bystander with exactly 2 health — should die
    state.players[1].creatures.push(make_creature(2, 4, PlayerId::PLAYER_TWO, 0, 1, 2, Keywords::none()));

    queue.push(
        Effect::Damage {
            target: EffectTarget::Creature { owner: PlayerId::PLAYER_ONE, slot: Slot(0) },
            amount: 10,
            filter: None,
        },
        EffectSource::System,
    );
    queue.process_all(&mut state, &db);

    assert!(state.players[0].get_creature(Slot(0)).is_none(), "Volatile creature dead");
    assert!(state.players[1].get_creature(Slot(0)).is_none(), "P2 bystander killed by Volatile");
}

/// Volatile chain reaction: first Volatile kills second Volatile, which also fires.
#[test]
fn volatile_chain_reaction_spell_death() {
    let db = test_db();
    let mut state = test_state();
    let mut queue = EffectQueue::new();

    // P1: Volatile 2/2 (will be spell-killed, triggering 2 damage to all P2 creatures)
    state.players[0].creatures.push(make_creature(
        1, 2, PlayerId::PLAYER_ONE, 0, 2, 2, Keywords::none().with_volatile(),
    ));
    // P2: Volatile 2/2 at slot 0 (should take 2 from P1's Volatile, die, fire its own Volatile)
    state.players[1].creatures.push(make_creature(
        2, 2, PlayerId::PLAYER_TWO, 0, 2, 2, Keywords::none().with_volatile(),
    ));
    // P1: bystander 1/3 at slot 1 (should take 2 from P2's chain Volatile)
    state.players[0].creatures.push(make_creature(3, 3, PlayerId::PLAYER_ONE, 1, 1, 3, Keywords::none()));

    // Kill P1's Volatile creature via spell
    queue.push(
        Effect::Damage {
            target: EffectTarget::Creature { owner: PlayerId::PLAYER_ONE, slot: Slot(0) },
            amount: 10,
            filter: None,
        },
        EffectSource::System,
    );
    queue.process_all(&mut state, &db);

    // P1's Volatile: dead
    assert!(state.players[0].get_creature(Slot(0)).is_none(), "first Volatile dead");
    // P2's Volatile: should have taken 2 from P1's explosion → dead
    assert!(state.players[1].get_creature(Slot(0)).is_none(), "second Volatile chain-dead");
    // P1's bystander: should have taken 2 from P2's chain explosion → 1 health
    let bystander = state.players[0].get_creature(Slot(1)).expect("P1 bystander survives chain");
    assert_eq!(bystander.current_health, 1, "bystander takes 2 from chain Volatile (3-2=1)");
}

/// Silenced Volatile creature killed by spell does NOT fire.
#[test]
fn silenced_volatile_does_not_fire_on_spell_death() {
    let db = test_db();
    let mut state = test_state();
    let mut queue = EffectQueue::new();

    let mut volatile_creature = make_creature(
        1, 2, PlayerId::PLAYER_ONE, 0, 2, 2, Keywords::none().with_volatile(),
    );
    volatile_creature.status.set_silenced(true);

    state.players[0].creatures.push(volatile_creature);
    state.players[1].creatures.push(make_creature(2, 4, PlayerId::PLAYER_TWO, 0, 1, 2, Keywords::none()));

    queue.push(
        Effect::Damage {
            target: EffectTarget::Creature { owner: PlayerId::PLAYER_ONE, slot: Slot(0) },
            amount: 10,
            filter: None,
        },
        EffectSource::System,
    );
    queue.process_all(&mut state, &db);

    let bystander = state.players[1].get_creature(Slot(0)).expect("P2 bystander survives");
    assert_eq!(bystander.current_health, 2, "silenced Volatile should not fire");
}

/// Volatile deals damage to multiple enemies simultaneously on spell death.
#[test]
fn volatile_spell_death_damages_multiple_enemies() {
    let db = test_db();
    let mut state = test_state();
    let mut queue = EffectQueue::new();

    state.players[0].creatures.push(make_creature(
        1, 2, PlayerId::PLAYER_ONE, 0, 2, 2, Keywords::none().with_volatile(),
    ));
    // Three P2 bystanders at different health values
    state.players[1].creatures.push(make_creature(2, 3, PlayerId::PLAYER_TWO, 0, 1, 3, Keywords::none()));
    state.players[1].creatures.push(make_creature(3, 4, PlayerId::PLAYER_TWO, 1, 1, 2, Keywords::none()));
    state.players[1].creatures.push(make_creature(4, 3, PlayerId::PLAYER_TWO, 2, 1, 4, Keywords::none()));

    queue.push(
        Effect::Damage {
            target: EffectTarget::Creature { owner: PlayerId::PLAYER_ONE, slot: Slot(0) },
            amount: 10,
            filter: None,
        },
        EffectSource::System,
    );
    queue.process_all(&mut state, &db);

    assert!(state.players[0].get_creature(Slot(0)).is_none(), "Volatile creature dead");
    // 3-hp creature: 3-2=1 survives
    let c0 = state.players[1].get_creature(Slot(0)).expect("1-hp survivor");
    assert_eq!(c0.current_health, 1);
    // 2-hp creature: 2-2=0 dies
    assert!(state.players[1].get_creature(Slot(1)).is_none(), "2-hp creature dies to Volatile");
    // 4-hp creature: 4-2=2 survives
    let c2 = state.players[1].get_creature(Slot(2)).expect("2-hp survivor");
    assert_eq!(c2.current_health, 2);
}

/// Vanilla (non-Volatile) creature killed by spell does NOT deal explosion damage.
#[test]
fn non_volatile_spell_death_no_explosion() {
    let db = test_db();
    let mut state = test_state();
    let mut queue = EffectQueue::new();

    state.players[0].creatures.push(make_creature(1, 1, PlayerId::PLAYER_ONE, 0, 1, 1, Keywords::none()));
    state.players[1].creatures.push(make_creature(2, 4, PlayerId::PLAYER_TWO, 0, 1, 2, Keywords::none()));

    queue.push(
        Effect::Damage {
            target: EffectTarget::Creature { owner: PlayerId::PLAYER_ONE, slot: Slot(0) },
            amount: 10,
            filter: None,
        },
        EffectSource::System,
    );
    queue.process_all(&mut state, &db);

    let bystander = state.players[1].get_creature(Slot(0)).expect("bystander survives");
    assert_eq!(bystander.current_health, 2, "no explosion from non-Volatile death");
}
