//! Legal action generation for the card game engine.
//!
//! This module generates all legal actions from a GameState, which is critical for:
//! - Game rule enforcement
//! - MCTS exploration
//! - Neural network action masking

use arrayvec::ArrayVec;
use crate::core::config::{board, insight};
use crate::core::types::Slot;
use crate::core::actions::{Action, Target};
use crate::core::state::GameState;
use crate::core::cards::{CardDatabase, CardType};
use crate::core::effects::{TargetingRule, Trigger};

/// Maximum number of legal actions possible in any game state
pub const MAX_LEGAL_ACTIONS: usize = 128;

/// Generate all legal actions for the current player
pub fn legal_actions(
    state: &GameState,
    card_db: &CardDatabase,
) -> ArrayVec<Action, MAX_LEGAL_ACTIONS> {
    let mut actions = ArrayVec::new();

    // If game is over, no actions are legal
    if state.is_terminal() {
        return actions;
    }

    // Generate PlayCard actions
    generate_play_card_actions(state, card_db, &mut actions);

    // Generate Attack actions
    generate_attack_actions(state, &mut actions);

    // Generate UseAbility actions
    generate_ability_actions(state, card_db, &mut actions);

    // Generate CommanderInsight action (if eligible)
    if is_commander_insight_legal(state) && actions.len() < MAX_LEGAL_ACTIONS {
        actions.push(Action::CommanderInsight);
    }

    // EndTurn is always legal
    if actions.len() < MAX_LEGAL_ACTIONS {
        actions.push(Action::EndTurn);
    }

    actions
}

/// Generate a legal action mask for neural network output masking
pub fn legal_action_mask(
    state: &GameState,
    card_db: &CardDatabase,
) -> [bool; Action::ACTION_SPACE_SIZE] {
    let mut mask = [false; Action::ACTION_SPACE_SIZE];
    for action in legal_actions(state, card_db) {
        mask[action.to_index() as usize] = true;
    }
    mask
}

/// Generate all legal PlayCard actions
fn generate_play_card_actions(
    state: &GameState,
    card_db: &CardDatabase,
    actions: &mut ArrayVec<Action, MAX_LEGAL_ACTIONS>,
) {
    let player = state.active_player_state();

    for (hand_idx, card_instance) in player.hand.iter().enumerate() {
        // Look up the card definition
        let Some(card_def) = card_db.get(card_instance.card_id) else {
            continue;
        };

        // Check if player can afford the card:
        // - 1 AP per action (playing a card is an action)
        // - Essence equal to card's cost
        if player.action_points < 1 || card_def.cost > player.current_essence {
            continue;
        }

        match &card_def.card_type {
            CardType::Creature { .. } => {
                // For creatures: generate action for each empty creature slot
                for slot_idx in 0..board::CREATURE_SLOTS as u8 {
                    let slot = Slot(slot_idx);
                    if player.get_creature(slot).is_none()
                        && actions.len() < MAX_LEGAL_ACTIONS {
                        actions.push(Action::PlayCard {
                            hand_index: hand_idx as u8,
                            slot,
                        });
                    }
                }
            }
            CardType::Spell { targeting, .. } => {
                // For spells: slot encodes the target based on targeting rule
                generate_spell_play_actions(
                    hand_idx as u8,
                    targeting,
                    state,
                    actions,
                );
            }
            CardType::Support { .. } => {
                // For supports: generate action for each empty support slot
                for slot_idx in 0..board::SUPPORT_SLOTS as u8 {
                    let slot = Slot(slot_idx);
                    if player.get_support(slot).is_none()
                        && actions.len() < MAX_LEGAL_ACTIONS {
                        actions.push(Action::PlayCard {
                            hand_index: hand_idx as u8,
                            slot,
                        });
                    }
                }
            }
        }
    }
}

/// Generate PlayCard actions for a spell based on its targeting rule.
///
/// Slot encoding for spells (matches resolve_spell_target):
/// - NoTarget: slot 0 (ignored)
/// - TargetEnemyCreature: slot 0-4 = enemy creature slots (one per occupied slot)
/// - TargetAllyCreature: slot 5-9 = ally creature slots (one per occupied slot)
/// - TargetAny: slot 0-4 enemy creature, 5-9 ally creature, 10 enemy player
/// - TargetCreature: slot 0-4 enemy creature, 5-9 ally creature
/// - TargetPlayer: slot 10 = enemy, slot 11 = self
/// - TargetEnemyPlayer: slot 10 = enemy
/// - TargetSlot: slot 0-4 = board slots (for summoning)
fn generate_spell_play_actions(
    hand_index: u8,
    targeting: &TargetingRule,
    state: &GameState,
    actions: &mut ArrayVec<Action, MAX_LEGAL_ACTIONS>,
) {
    let player = state.active_player_state();
    let opponent = state.opponent_state();

    match targeting {
        TargetingRule::NoTarget => {
            if actions.len() < MAX_LEGAL_ACTIONS {
                actions.push(Action::PlayCard {
                    hand_index,
                    slot: Slot(0),
                });
            }
        }
        TargetingRule::TargetEnemyCreature => {
            // One action per occupied enemy creature slot
            for enemy_creature in &opponent.creatures {
                if enemy_creature.keywords.has_stealth() {
                    continue;
                }
                if actions.len() < MAX_LEGAL_ACTIONS {
                    actions.push(Action::PlayCard {
                        hand_index,
                        slot: enemy_creature.slot, // 0-4
                    });
                }
            }
        }
        TargetingRule::TargetAllyCreature => {
            // One action per occupied ally creature slot (encoded as slot+5)
            for ally_creature in &player.creatures {
                if actions.len() < MAX_LEGAL_ACTIONS {
                    actions.push(Action::PlayCard {
                        hand_index,
                        slot: Slot(ally_creature.slot.0 + 5), // 5-9
                    });
                }
            }
        }
        TargetingRule::TargetAny => {
            // Enemy creatures (0-4)
            for enemy_creature in &opponent.creatures {
                if enemy_creature.keywords.has_stealth() {
                    continue;
                }
                if actions.len() < MAX_LEGAL_ACTIONS {
                    actions.push(Action::PlayCard {
                        hand_index,
                        slot: enemy_creature.slot, // 0-4
                    });
                }
            }
            // Ally creatures (5-9)
            for ally_creature in &player.creatures {
                if actions.len() < MAX_LEGAL_ACTIONS {
                    actions.push(Action::PlayCard {
                        hand_index,
                        slot: Slot(ally_creature.slot.0 + 5), // 5-9
                    });
                }
            }
            // Enemy player (10)
            if actions.len() < MAX_LEGAL_ACTIONS {
                actions.push(Action::PlayCard {
                    hand_index,
                    slot: Slot(10),
                });
            }
        }
        TargetingRule::TargetCreature(_) => {
            // Enemy creatures (0-4)
            for enemy_creature in &opponent.creatures {
                if enemy_creature.keywords.has_stealth() {
                    continue;
                }
                if actions.len() < MAX_LEGAL_ACTIONS {
                    actions.push(Action::PlayCard {
                        hand_index,
                        slot: enemy_creature.slot, // 0-4
                    });
                }
            }
            // Ally creatures (5-9)
            for ally_creature in &player.creatures {
                if actions.len() < MAX_LEGAL_ACTIONS {
                    actions.push(Action::PlayCard {
                        hand_index,
                        slot: Slot(ally_creature.slot.0 + 5), // 5-9
                    });
                }
            }
        }
        TargetingRule::TargetPlayer => {
            // Enemy player (10)
            if actions.len() < MAX_LEGAL_ACTIONS {
                actions.push(Action::PlayCard {
                    hand_index,
                    slot: Slot(10),
                });
            }
            // Self player (11)
            if actions.len() < MAX_LEGAL_ACTIONS {
                actions.push(Action::PlayCard {
                    hand_index,
                    slot: Slot(11),
                });
            }
        }
        TargetingRule::TargetEnemyPlayer => {
            // Enemy player (10)
            if actions.len() < MAX_LEGAL_ACTIONS {
                actions.push(Action::PlayCard {
                    hand_index,
                    slot: Slot(10),
                });
            }
        }
        TargetingRule::TargetSlot => {
            // For summoning spells: one per empty ally creature slot
            for slot_idx in 0..board::CREATURE_SLOTS as u8 {
                let slot = Slot(slot_idx);
                if player.get_creature(slot).is_none() && actions.len() < MAX_LEGAL_ACTIONS {
                    actions.push(Action::PlayCard {
                        hand_index,
                        slot,
                    });
                }
            }
        }
    }
}

/// Generate all legal Attack actions
fn generate_attack_actions(
    state: &GameState,
    actions: &mut ArrayVec<Action, MAX_LEGAL_ACTIONS>,
) {
    let player_state = state.active_player_state();
    let opponent_state = state.opponent_state();

    // Check if any enemy creature has GUARD (and is not stealthed - Stealth masks Guard)
    let guards_present = opponent_state.creatures.iter()
        .any(|c| c.keywords.has_guard() && !c.keywords.has_stealth());

    // For each of our creatures that can attack
    for attacker in &player_state.creatures {
        // Check if creature can attack
        if !attacker.can_attack(state.current_turn) {
            continue;
        }

        let attacker_slot = attacker.slot;
        let has_ranged = attacker.keywords.has_ranged();

        // Determine valid target slots
        if has_ranged {
            // Ranged creatures can target any enemy slot (0-4)
            for defender_slot_idx in 0..board::CREATURE_SLOTS as u8 {
                let defender_slot = Slot(defender_slot_idx);

                // Stealth creatures can't be targeted — treat their slot as empty
                // (attack goes to face, not the creature)
                let slot_effectively_occupied = opponent_state.get_creature(defender_slot)
                    .is_some_and(|c| !c.keywords.has_stealth());

                // Check GUARD enforcement for ranged attacks on creatures
                if guards_present {
                    if slot_effectively_occupied {
                        let defender = opponent_state.get_creature(defender_slot).unwrap();
                        if !defender.keywords.has_guard() {
                            continue;
                        }
                    } else {
                        // Cannot attack empty/stealth slot if guards are present
                        continue;
                    }
                }

                if actions.len() < MAX_LEGAL_ACTIONS {
                    actions.push(Action::Attack {
                        attacker: attacker_slot,
                        defender: defender_slot,
                    });
                }
            }
        } else {
            // Non-ranged creatures can only target adjacent slots
            for &defender_slot in attacker_slot.adjacent_slots() {
                // Stealth creatures can't be targeted — treat their slot as empty
                // (attack goes to face, not the creature)
                let slot_effectively_occupied = opponent_state.get_creature(defender_slot)
                    .is_some_and(|c| !c.keywords.has_stealth());

                // Check GUARD enforcement for non-ranged attacks
                if guards_present {
                    if slot_effectively_occupied {
                        let defender = opponent_state.get_creature(defender_slot).unwrap();
                        if !defender.keywords.has_guard() {
                            continue;
                        }
                    } else {
                        // Cannot attack empty/stealth slot if guards are present
                        continue;
                    }
                }

                if actions.len() < MAX_LEGAL_ACTIONS {
                    actions.push(Action::Attack {
                        attacker: attacker_slot,
                        defender: defender_slot,
                    });
                }
            }
        }
    }
}

/// Generate all legal UseAbility actions
///
/// This generates actions for:
/// 1. Token abilities (stored in creature.token_data)
/// 2. Card-based activated abilities (with Trigger::Activated)
///
/// UseAbility actions use action space indices 145-419.
fn generate_ability_actions(
    state: &GameState,
    card_db: &CardDatabase,
    actions: &mut ArrayVec<Action, MAX_LEGAL_ACTIONS>,
) {
    let player = state.active_player_state();
    let opponent = state.opponent_state();

    for creature in &player.creatures {
        // Skip silenced creatures - they can't use abilities
        if creature.status.is_silenced() {
            continue;
        }

        // Skip exhausted creatures - activated abilities require tapping
        if creature.status.is_exhausted() {
            continue;
        }

        // Check token abilities first (using helper method for token_data access)
        if let Some(abilities) = creature.token_abilities() {
            generate_actions_for_abilities_token(
                creature.slot,
                abilities,
                player.current_essence,
                opponent,
                actions,
            );
        }

        // Check card-based activated abilities
        if let Some(card_def) = card_db.get(creature.card_id) {
            if let CardType::Creature { abilities, .. } = &card_def.card_type {
                generate_actions_for_abilities_card(
                    creature.slot,
                    abilities,
                    player.current_essence,
                    player,
                    opponent,
                    actions,
                );
            }
        }
    }
}

/// Generate UseAbility actions for token abilities
fn generate_actions_for_abilities_token(
    slot: Slot,
    abilities: &[crate::core::effects::TokenAbility],
    current_essence: u8,
    opponent: &crate::core::state::PlayerState,
    actions: &mut ArrayVec<Action, MAX_LEGAL_ACTIONS>,
) {
    for (ability_idx, ability) in abilities.iter().enumerate() {
        // Check essence cost
        if ability.essence_cost > current_essence {
            continue;
        }

        generate_targeting_actions(
            slot,
            ability_idx as u8,
            &ability.targeting,
            None, // player state not needed for token abilities
            opponent,
            actions,
        );
    }
}

/// Generate UseAbility actions for card-based abilities
fn generate_actions_for_abilities_card(
    slot: Slot,
    abilities: &[crate::core::cards::AbilityDefinition],
    current_essence: u8,
    player: &crate::core::state::PlayerState,
    opponent: &crate::core::state::PlayerState,
    actions: &mut ArrayVec<Action, MAX_LEGAL_ACTIONS>,
) {
    for (ability_idx, ability) in abilities.iter().enumerate() {
        // Only process Activated trigger abilities
        if ability.trigger != Trigger::Activated {
            continue;
        }

        // Check essence cost
        if ability.essence_cost > current_essence {
            continue;
        }

        generate_targeting_actions(
            slot,
            ability_idx as u8,
            &ability.targeting,
            Some(player),
            opponent,
            actions,
        );
    }
}

/// Generate actions based on targeting rule (shared by token and card abilities)
fn generate_targeting_actions(
    slot: Slot,
    ability_idx: u8,
    targeting: &TargetingRule,
    player: Option<&crate::core::state::PlayerState>,
    opponent: &crate::core::state::PlayerState,
    actions: &mut ArrayVec<Action, MAX_LEGAL_ACTIONS>,
) {
    match targeting {
        TargetingRule::TargetAny => {
            // Can target any enemy creature
            for enemy_creature in &opponent.creatures {
                if enemy_creature.keywords.has_stealth() {
                    continue;
                }
                if actions.len() < MAX_LEGAL_ACTIONS {
                    actions.push(Action::UseAbility {
                        slot,
                        ability_index: ability_idx,
                        target: Target::EnemySlot(enemy_creature.slot),
                    });
                }
            }
            // Can also target enemy commander (NoTarget = face)
            if actions.len() < MAX_LEGAL_ACTIONS {
                actions.push(Action::UseAbility {
                    slot,
                    ability_index: ability_idx,
                    target: Target::NoTarget,
                });
            }
        }
        TargetingRule::TargetEnemyCreature => {
            for enemy_creature in &opponent.creatures {
                if enemy_creature.keywords.has_stealth() {
                    continue;
                }
                if actions.len() < MAX_LEGAL_ACTIONS {
                    actions.push(Action::UseAbility {
                        slot,
                        ability_index: ability_idx,
                        target: Target::EnemySlot(enemy_creature.slot),
                    });
                }
            }
        }
        TargetingRule::TargetAllyCreature => {
            // Can target any ally creature (including self)
            if let Some(player_state) = player {
                for ally_creature in &player_state.creatures {
                    if actions.len() < MAX_LEGAL_ACTIONS {
                        actions.push(Action::UseAbility {
                            slot,
                            ability_index: ability_idx,
                            target: Target::AllySlot(ally_creature.slot),
                        });
                    }
                }
            }
        }
        TargetingRule::TargetEnemyPlayer => {
            if actions.len() < MAX_LEGAL_ACTIONS {
                actions.push(Action::UseAbility {
                    slot,
                    ability_index: ability_idx,
                    target: Target::NoTarget,
                });
            }
        }
        TargetingRule::NoTarget => {
            if actions.len() < MAX_LEGAL_ACTIONS {
                actions.push(Action::UseAbility {
                    slot,
                    ability_index: ability_idx,
                    target: Target::NoTarget,
                });
            }
        }
        // Other targeting rules not yet fully implemented
        _ => {}
    }
}

/// Check if Commander's Insight is legal for the current player.
///
/// Commander's Insight is a catch-up mechanic that allows struggling players
/// to draw a card for essence (no AP cost). It's available when:
/// - Turn >= 10 (late game only)
/// - Hand size <= 1 (top-decking)
/// - Behind on creatures OR behind on life (catch-up restriction)
/// - Have enough essence (4)
/// - Haven't used it this turn
pub fn is_commander_insight_legal(state: &GameState) -> bool {
    // Must be turn 10 or later
    if state.current_turn < insight::MIN_TURN {
        return false;
    }

    let player = state.active_player_state();
    let opponent = state.opponent_state();

    // Must not have used insight this turn
    if player.used_commander_insight {
        return false;
    }

    // Must have hand size <= 1 (starving)
    if player.hand.len() > insight::MAX_HAND_SIZE {
        return false;
    }

    // Must have enough essence
    if player.current_essence < insight::ESSENCE_COST {
        return false;
    }

    // Must be behind on creatures OR behind on life (strict inequality)
    let behind_on_creatures = player.creatures.len() < opponent.creatures.len();
    let behind_on_life = player.life < opponent.life;

    if !behind_on_creatures && !behind_on_life {
        return false;
    }

    true
}
