//! Module for game action definitions.
//!
//! Actions represent all possible moves a player can make, such as
//! playing cards, attacking, activating abilities, and passing priority.
//!
//! The game uses a fixed action space of 422 actions for neural network compatibility:
//! - Index 0-119:    PlayCard(hand_idx 0-9, target_slot 0-11)
//! - Index 120-144:  Attack(attacker_slot 0-4, defender_slot 0-4)
//! - Index 145-419:  UseAbility(slot, ability_idx, target)
//! - Index 420:      CommanderInsight (draw a card for 4 essence, catch-up mechanic)
//! - Index 421:      EndTurn

use serde::{Deserialize, Serialize};
use crate::core::config::actions as action_config;
use crate::core::types::Slot;

/// Target for ability effects
///
/// Target encoding for neural network:
/// - 0: no target (face damage / no-target abilities)
/// - 1-5: enemy slots 0-4
/// - 6-10: ally slots 0-4
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub enum Target {
    /// No target required (also used for face-targeting abilities)
    NoTarget,
    /// Target an enemy creature in a specific slot (0-4)
    EnemySlot(Slot),
    /// Target an ally creature in a specific slot (0-4), including self
    AllySlot(Slot),
}

impl Target {
    /// Convert target to index for neural network encoding
    /// - 0: no target
    /// - 1-5: enemy slots 0-4
    /// - 6-10: ally slots 0-4
    pub fn to_index(&self) -> u8 {
        match self {
            Target::NoTarget => 0,
            Target::EnemySlot(slot) => 1 + slot.0,
            Target::AllySlot(slot) => 6 + slot.0,
        }
    }

    /// Convert index back to target
    /// Returns None if index is out of range (> 10)
    pub fn from_index(index: u8) -> Option<Target> {
        match index {
            0 => Some(Target::NoTarget),
            1..=5 => Some(Target::EnemySlot(Slot(index - 1))),
            6..=10 => Some(Target::AllySlot(Slot(index - 6))),
            _ => None,
        }
    }
}

/// Represents all possible game actions
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub enum Action {
    /// Play a card from hand to a board slot
    ///
    /// For creatures/supports: slot is the board placement slot (0-4)
    /// For targeted spells: slot encodes the target (see PlayCard target encoding)
    PlayCard {
        /// Index in hand (0-9)
        hand_index: u8,
        /// Target slot (0-11, see config::actions::PLAY_CARD_TARGET_SLOTS)
        slot: Slot,
    },
    /// Attack with a creature
    Attack {
        /// Slot of attacking creature (0-4)
        attacker: Slot,
        /// Slot of defending creature (0-4)
        defender: Slot,
    },
    /// Use a creature's activated ability
    UseAbility {
        /// Slot of creature using ability (0-4)
        slot: Slot,
        /// Index of ability to use (0-4)
        ability_index: u8,
        /// Target for the ability
        target: Target,
    },
    /// Commander's Insight - Draw a card for 4 essence (catch-up mechanic)
    ///
    /// Available when:
    /// - Turn >= 10
    /// - Hand size <= 1
    /// - Behind on creatures OR behind on life
    /// - Have 4+ essence
    /// - Not used this turn
    CommanderInsight,
    /// End the current turn
    EndTurn,
}

impl Action {
    /// Total number of possible action indices for neural network
    pub const ACTION_SPACE_SIZE: usize = action_config::ACTION_SPACE_SIZE;

    // Index ranges
    const PLAY_CARD_START: u16 = 0;
    const PLAY_CARD_END: u16 = 119; // 10 * 12 - 1
    const ATTACK_START: u16 = 120;
    const ATTACK_END: u16 = 144; // 120 + 5*5 - 1
    const USE_ABILITY_START: u16 = 145;
    const USE_ABILITY_END: u16 = 419; // 145 + 5*5*11 - 1
    const COMMANDER_INSIGHT_INDEX: u16 = 420;
    const END_TURN_INDEX: u16 = 421;

    // Limits
    const NUM_BOARD_SLOTS: u16 = 5;
    const NUM_PLAY_CARD_TARGETS: u16 = action_config::PLAY_CARD_TARGET_SLOTS as u16;
    const MAX_ABILITIES: u16 = action_config::MAX_ABILITIES as u16;
    const NUM_ABILITY_TARGETS: u16 = action_config::ABILITY_NUM_TARGETS as u16;

    /// Convert action to neural network output index (0-421)
    ///
    /// Index mapping:
    /// - PlayCard: hand_idx * 12 + target_slot (0-119)
    /// - Attack: 120 + attacker * 5 + defender (120-144)
    /// - UseAbility: 145 + slot * 55 + ability * 11 + target (145-419)
    /// - CommanderInsight: 420
    /// - EndTurn: 421
    pub fn to_index(&self) -> u16 {
        match self {
            Action::PlayCard { hand_index, slot } => {
                (*hand_index as u16) * Self::NUM_PLAY_CARD_TARGETS + slot.0 as u16
            }
            Action::Attack { attacker, defender } => {
                Self::ATTACK_START + attacker.0 as u16 * Self::NUM_BOARD_SLOTS + defender.0 as u16
            }
            Action::UseAbility {
                slot,
                ability_index,
                target,
            } => {
                Self::USE_ABILITY_START
                    + slot.0 as u16 * (Self::MAX_ABILITIES * Self::NUM_ABILITY_TARGETS)
                    + *ability_index as u16 * Self::NUM_ABILITY_TARGETS
                    + target.to_index() as u16
            }
            Action::CommanderInsight => Self::COMMANDER_INSIGHT_INDEX,
            Action::EndTurn => Self::END_TURN_INDEX,
        }
    }

    /// Convert neural network output index back to action
    ///
    /// Returns None if index is invalid or out of range
    pub fn from_index(index: u16) -> Option<Action> {
        match index {
            Self::PLAY_CARD_START..=Self::PLAY_CARD_END => {
                let hand_index = (index / Self::NUM_PLAY_CARD_TARGETS) as u8;
                let slot = (index % Self::NUM_PLAY_CARD_TARGETS) as u8;
                Some(Action::PlayCard {
                    hand_index,
                    slot: Slot(slot),
                })
            }
            Self::ATTACK_START..=Self::ATTACK_END => {
                let offset = index - Self::ATTACK_START;
                let attacker = (offset / Self::NUM_BOARD_SLOTS) as u8;
                let defender = (offset % Self::NUM_BOARD_SLOTS) as u8;
                Some(Action::Attack {
                    attacker: Slot(attacker),
                    defender: Slot(defender),
                })
            }
            Self::USE_ABILITY_START..=Self::USE_ABILITY_END => {
                let offset = index - Self::USE_ABILITY_START;
                let abilities_per_slot = Self::MAX_ABILITIES * Self::NUM_ABILITY_TARGETS;
                let slot = (offset / abilities_per_slot) as u8;
                let remaining = offset % abilities_per_slot;
                let ability_index = (remaining / Self::NUM_ABILITY_TARGETS) as u8;
                let target_index = (remaining % Self::NUM_ABILITY_TARGETS) as u8;

                // Validate slot is within range (0-4)
                if slot >= Self::NUM_BOARD_SLOTS as u8 {
                    return None;
                }

                let target = Target::from_index(target_index)?;

                Some(Action::UseAbility {
                    slot: Slot(slot),
                    ability_index,
                    target,
                })
            }
            Self::COMMANDER_INSIGHT_INDEX => Some(Action::CommanderInsight),
            Self::END_TURN_INDEX => Some(Action::EndTurn),
            _ => None,
        }
    }
}
