//! Unit tests for the Essence Overload mechanic.
//!
//! Overload: pay `overload_cost` life instead of Essence to play a card.
//! - Player must have life > overload_cost (cannot Overload to 0 or below)
//! - AP cost is unchanged (1 AP)
//! - Normal PlayCard is still available if player can afford Essence cost
//! - PlayCardOverload is independent of current Essence

use cardgame::actions::Action;
use cardgame::cards::{CardDatabase, CardDefinition, CardType};
use cardgame::effects::TargetingRule;
use cardgame::engine::{execute_play_card_overload, ActionContext, EffectQueue};
use cardgame::legal::legal_actions;
use cardgame::state::{CardInstance, GameState};
use cardgame::types::{CardId, Rarity, Slot};

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

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

fn make_overload_spell(id: u16, cost: u8, overload_cost: u8) -> CardDefinition {
    CardDefinition {
        id,
        name: format!("Overload Spell {}", id),
        cost,
        overload_cost: Some(overload_cost),
        card_type: CardType::Spell {
            targeting: TargetingRule::NoTarget,
            effects: vec![cardgame::cards::EffectDefinition::Draw { count: 1 }],
            conditional_effects: vec![],
        },
        rarity: Rarity::Uncommon,
        flanking: false,
        tags: vec![],
    }
}

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

fn setup_state_with_card(card_id: CardId, life: i16, essence: u8, ap: u8) -> GameState {
    let mut state = GameState::new();
    state.players[0].life = life;
    state.players[1].life = 30;
    state.players[0].current_essence = essence;
    state.players[0].action_points = ap;
    state.players[0].hand.clear();
    state.players[0].hand.push(CardInstance { card_id });
    state.current_turn = 2;
    state
}

// =============================================================================
// Action Index Encoding
// =============================================================================

#[test]
fn overload_action_index_encoding() {
    // PlayCardOverload starts at index 422
    let action = Action::PlayCardOverload { hand_index: 0, slot: Slot(0) };
    assert_eq!(action.to_index(), 422);

    let action = Action::PlayCardOverload { hand_index: 0, slot: Slot(11) };
    assert_eq!(action.to_index(), 433);

    let action = Action::PlayCardOverload { hand_index: 9, slot: Slot(11) };
    assert_eq!(action.to_index(), 541);

    // EndTurn is still 421
    assert_eq!(Action::EndTurn.to_index(), 421);
}

#[test]
fn overload_action_index_roundtrip() {
    for hand in 0u8..10 {
        for slot in 0u8..12 {
            let action = Action::PlayCardOverload { hand_index: hand, slot: Slot(slot) };
            let idx = action.to_index();
            let decoded = Action::from_index(idx).expect("should decode");
            assert_eq!(action, decoded, "roundtrip failed for hand={} slot={}", hand, slot);
        }
    }
}

#[test]
fn overload_range_does_not_overlap_existing_actions() {
    // PlayCardOverload occupies 422-541; existing actions end at 421
    let overload_start = Action::PlayCardOverload { hand_index: 0, slot: Slot(0) }.to_index();
    assert_eq!(overload_start, 422);
    assert_eq!(Action::EndTurn.to_index(), 421);

    // Total action space is 542
    assert_eq!(Action::ACTION_SPACE_SIZE, 542);
}

// =============================================================================
// Legal Action Generation
// =============================================================================

#[test]
fn overload_legal_when_life_exceeds_cost() {
    let card_def = make_overload_creature(1, 5, 3, 3, 3); // cost 5, overload 3
    let db = CardDatabase::new(vec![card_def]);
    // Life 10 > overload_cost 3 → legal
    let state = setup_state_with_card(CardId(1), 10, 0, 1); // 0 essence, can't normal-play
    let actions = legal_actions(&state, &db);
    let has_overload = actions.iter().any(|a| matches!(a, Action::PlayCardOverload { .. }));
    assert!(has_overload, "Overload should be legal when life ({}) > overload_cost ({})", 10, 3);
}

#[test]
fn overload_illegal_when_life_equals_cost() {
    let card_def = make_overload_creature(1, 5, 3, 3, 3);
    let db = CardDatabase::new(vec![card_def]);
    // Life 3 == overload_cost 3 → cannot Overload (would reach 0)
    let state = setup_state_with_card(CardId(1), 3, 0, 1);
    let actions = legal_actions(&state, &db);
    let has_overload = actions.iter().any(|a| matches!(a, Action::PlayCardOverload { .. }));
    assert!(!has_overload, "Overload should be illegal when life ({}) == overload_cost ({})", 3, 3);
}

#[test]
fn overload_illegal_when_life_less_than_cost() {
    let card_def = make_overload_creature(1, 5, 5, 3, 3);
    let db = CardDatabase::new(vec![card_def]);
    // Life 4 < overload_cost 5 → illegal
    let state = setup_state_with_card(CardId(1), 4, 0, 1);
    let actions = legal_actions(&state, &db);
    let has_overload = actions.iter().any(|a| matches!(a, Action::PlayCardOverload { .. }));
    assert!(!has_overload, "Overload should be illegal when life ({}) < overload_cost ({})", 4, 5);
}

#[test]
fn overload_illegal_when_no_ap() {
    let card_def = make_overload_creature(1, 5, 3, 3, 3);
    let db = CardDatabase::new(vec![card_def]);
    // 0 AP → neither PlayCard nor PlayCardOverload is legal
    let state = setup_state_with_card(CardId(1), 20, 0, 0);
    let actions = legal_actions(&state, &db);
    let has_overload = actions.iter().any(|a| matches!(a, Action::PlayCardOverload { .. }));
    assert!(!has_overload, "Overload requires at least 1 AP");
}

#[test]
fn overload_available_alongside_normal_play_when_both_affordable() {
    let card_def = make_overload_creature(1, 3, 2, 3, 3); // cost 3, overload 2
    let db = CardDatabase::new(vec![card_def]);
    // Enough essence (3) AND enough life (10 > 2) → BOTH are legal
    let state = setup_state_with_card(CardId(1), 10, 3, 1);
    let actions = legal_actions(&state, &db);
    let has_normal = actions.iter().any(|a| matches!(a, Action::PlayCard { .. }));
    let has_overload = actions.iter().any(|a| matches!(a, Action::PlayCardOverload { .. }));
    assert!(has_normal, "Normal PlayCard should be legal when essence is sufficient");
    assert!(has_overload, "PlayCardOverload should also be legal when life > overload_cost");
}

#[test]
fn card_without_overload_cost_has_no_overload_actions() {
    let card_def = make_normal_creature(1, 3, 3, 3); // no overload_cost
    let db = CardDatabase::new(vec![card_def]);
    let state = setup_state_with_card(CardId(1), 30, 3, 1);
    let actions = legal_actions(&state, &db);
    let has_overload = actions.iter().any(|a| matches!(a, Action::PlayCardOverload { .. }));
    assert!(!has_overload, "Cards without overload_cost should never generate PlayCardOverload");
}

#[test]
fn overload_generates_one_action_per_empty_creature_slot() {
    let card_def = make_overload_creature(1, 5, 3, 3, 3);
    let db = CardDatabase::new(vec![card_def]);
    let state = setup_state_with_card(CardId(1), 20, 0, 1); // 0 essence forces overload path

    let actions = legal_actions(&state, &db);
    let overload_count = actions.iter().filter(|a| matches!(a, Action::PlayCardOverload { .. })).count();
    // 5 empty creature slots → 5 overload actions
    assert_eq!(overload_count, 5, "Expected 5 overload actions for 5 empty slots, got {}", overload_count);
}

#[test]
fn overload_spell_generates_legal_actions() {
    let card_def = make_overload_spell(1, 4, 3); // NoTarget spell, cost 4, overload 3
    let db = CardDatabase::new(vec![card_def]);
    let state = setup_state_with_card(CardId(1), 20, 0, 1); // 0 essence forces overload path

    let actions = legal_actions(&state, &db);
    let overload_count = actions.iter().filter(|a| matches!(a, Action::PlayCardOverload { .. })).count();
    assert_eq!(overload_count, 1, "NoTarget spell overload should generate exactly 1 action");
}

// =============================================================================
// Execution
// =============================================================================

#[test]
fn overload_deducts_life_not_essence() {
    let card_def = make_overload_creature(1, 5, 3, 3, 3); // cost 5, overload 3
    let db = CardDatabase::new(vec![card_def]);
    let mut state = setup_state_with_card(CardId(1), 20, 2, 1); // only 2 essence (can't normal-play)
    let mut queue = EffectQueue::new();

    let initial_life = state.players[0].life;
    let initial_essence = state.players[0].current_essence;

    let mut ctx = ActionContext::new(&mut state, &db, &mut queue, None);
    execute_play_card_overload(&mut ctx, 0, Slot(0)).expect("Overload should succeed");

    let life_after = ctx.state.players[0].life;
    let essence_after = ctx.state.players[0].current_essence;

    assert_eq!(life_after, initial_life - 3, "Should lose 3 life (overload cost)");
    assert_eq!(essence_after, initial_essence, "Essence should be unchanged");
}

#[test]
fn overload_deducts_ap() {
    let card_def = make_overload_creature(1, 5, 3, 3, 3);
    let db = CardDatabase::new(vec![card_def]);
    let mut state = setup_state_with_card(CardId(1), 20, 0, 3); // 3 AP
    let mut queue = EffectQueue::new();

    let mut ctx = ActionContext::new(&mut state, &db, &mut queue, None);
    execute_play_card_overload(&mut ctx, 0, Slot(0)).expect("Overload should succeed");

    assert_eq!(ctx.state.players[0].action_points, 2, "Should cost 1 AP");
}

#[test]
fn overload_places_creature_on_board() {
    let card_def = make_overload_creature(1, 5, 3, 4, 3); // 4/3 creature
    let db = CardDatabase::new(vec![card_def]);
    let mut state = setup_state_with_card(CardId(1), 20, 0, 1);
    let mut queue = EffectQueue::new();

    assert!(state.players[0].get_creature(Slot(0)).is_none());
    let mut ctx = ActionContext::new(&mut state, &db, &mut queue, None);
    execute_play_card_overload(&mut ctx, 0, Slot(0)).expect("Overload should succeed");

    let creature = ctx.state.players[0].get_creature(Slot(0));
    assert!(creature.is_some(), "Creature should be on board after overload");
    assert_eq!(creature.unwrap().attack, 4, "Creature has correct attack");
}

#[test]
fn overload_removes_card_from_hand() {
    let card_def = make_overload_creature(1, 5, 3, 3, 3);
    let db = CardDatabase::new(vec![card_def]);
    let mut state = setup_state_with_card(CardId(1), 20, 0, 1);
    let mut queue = EffectQueue::new();

    assert_eq!(state.players[0].hand.len(), 1);
    let mut ctx = ActionContext::new(&mut state, &db, &mut queue, None);
    execute_play_card_overload(&mut ctx, 0, Slot(0)).expect("Overload should succeed");

    assert_eq!(ctx.state.players[0].hand.len(), 0, "Hand should be empty after playing");
}

#[test]
fn overload_fails_when_life_at_exact_threshold() {
    let card_def = make_overload_creature(1, 5, 5, 3, 3); // overload_cost 5
    let db = CardDatabase::new(vec![card_def]);
    let mut state = setup_state_with_card(CardId(1), 5, 0, 1); // life == overload_cost
    let mut queue = EffectQueue::new();

    let mut ctx = ActionContext::new(&mut state, &db, &mut queue, None);
    let result = execute_play_card_overload(&mut ctx, 0, Slot(0));
    assert!(result.is_err(), "Overload should fail when life == overload_cost");
}

// =============================================================================
// YAML Loading
// =============================================================================

#[test]
fn overload_cost_loads_from_yaml() {
    let yaml = r#"
name: "Overload Test Set"
cards:
  - id: 100
    name: "Blood Surge"
    cost: 4
    overload_cost: 3
    card_type: spell
    targeting: TargetEnemyCreature
    effects:
      - type: damage
        amount: 5
    rarity: Rare
  - id: 101
    name: "Iron Zealot"
    cost: 5
    overload_cost: 4
    card_type: creature
    attack: 5
    health: 4
    rarity: Rare
  - id: 102
    name: "Normal Card"
    cost: 3
    card_type: creature
    attack: 3
    health: 3
    rarity: Common
"#;

    let db = CardDatabase::load_from_yaml(yaml).expect("Failed to parse YAML");

    let blood_surge = db.get(CardId(100)).expect("Blood Surge not found");
    assert_eq!(blood_surge.overload_cost, Some(3));

    let iron_zealot = db.get(CardId(101)).expect("Iron Zealot not found");
    assert_eq!(iron_zealot.overload_cost, Some(4));

    let normal = db.get(CardId(102)).expect("Normal card not found");
    assert_eq!(normal.overload_cost, None, "Cards without overload_cost field should be None");
}

#[test]
fn overload_cards_loaded_from_real_db() {
    use cardgame::embedded_data::load_embedded_cards;

    let db = load_embedded_cards().expect("should load embedded card db");

    // Blood Surge (id 3079) — Obsidion overload spell
    if let Some(card) = db.get(CardId(3079)) {
        assert_eq!(card.overload_cost, Some(3), "Blood Surge should have overload_cost 3");
        assert!(card.is_spell());
    }

    // Iron Zealot (id 1079) — Argentum overload creature
    if let Some(card) = db.get(CardId(1079)) {
        assert_eq!(card.overload_cost, Some(4), "Iron Zealot should have overload_cost 4");
        assert!(card.is_creature());
    }

    // Burst Runner (id 2078) — Symbiote overload creature
    if let Some(card) = db.get(CardId(2078)) {
        assert_eq!(card.overload_cost, Some(3), "Burst Runner should have overload_cost 3");
        assert!(card.is_creature());
    }
}
