use alloc::{
    string::String,
    vec::Vec,
};

use hashbrown::HashSet;
use serde::{
    Deserialize,
    Serialize,
};
use serde_string_enum::{
    DeserializeLabeledStringEnum,
    SerializeLabeledStringEnum,
};

use crate::{
    Accuracy,
    BoostTable,
    Fraction,
    MonOverride,
    MoveCategory,
    MoveFlag,
    MoveTarget,
    MultihitType,
    OhkoType,
    SelfDestructType,
    Stat,
    SwitchType,
    Type,
};

/// The effect of being hit by a move.
#[derive(Debug, Default, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct HitEffect {
    /// Stat boosts.
    pub boosts: Option<BoostTable>,
    /// Percentage of target's HP to heal.
    pub heal_percent: Option<Fraction<u16>>,
    /// Status applied.
    pub status: Option<String>,
    /// Volatile status applied.
    pub volatile_status: Option<String>,
    /// Side condition applied.
    pub side_condition: Option<String>,
    /// Slot condition applied.
    pub slot_condition: Option<String>,
    /// Weather applied.
    pub weather: Option<String>,
    /// Pseudo-weather applied.
    pub pseudo_weather: Option<String>,
    /// Terrain applied.
    pub terrain: Option<String>,
    /// Force the target to switch out?
    #[serde(default)]
    pub force_switch: bool,
}

/// Data about a secondary effect that occurs after a move is used.
#[derive(Debug, Default, Clone, Serialize, Deserialize)]
pub struct SecondaryEffectData {
    /// Chance of the effect occurring.
    pub chance: Option<Fraction<u16>>,
    /// Secondary hit effect on the target.
    pub target: Option<HitEffect>,
    /// Secondary hit effect on the user of the move.
    pub user: Option<HitEffect>,
    /// Source effect to use instead of the move.
    pub source_effect: Option<String>,
    /// Dynamic battle effects.
    #[serde(default)]
    pub effect: serde_json::Value,
}

/// Data for the Z-Power of a Z-Move.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum ZPower {
    #[serde(rename = "boosts")]
    Boosts(BoostTable),
    #[serde(rename = "effect")]
    Effect(String),
}

/// Data about how a move affects Z-Moves.
#[derive(Debug, Default, Clone, Serialize, Deserialize)]
pub struct ZMoveData {
    /// Base power.
    #[serde(default)]
    pub base_power: u32,
    /// Z-Power.
    #[serde(flatten)]
    pub z_power: Option<ZPower>,
}

/// Data about how a move affects Max Moves.
#[derive(Debug, Default, Clone, Serialize, Deserialize)]
pub struct MaxMoveData {
    /// Base power.
    pub base_power: u32,
}
/// The base number to use for recoil damage calculation.
#[derive(Debug, Default, Clone, SerializeLabeledStringEnum, DeserializeLabeledStringEnum)]
pub enum RecoilBase {
    /// Damage dealt.
    #[default]
    #[string = "Damage"]
    Damage,
    /// The move user's maximum HP.
    #[string = "UserMaxHp"]
    UserMaxHp,
    /// The move user's base maximum HP.
    #[string = "UserBaseMaxHp"]
    UserBaseMaxHp,
}

/// Data for a move's recoil damage to the user.
#[derive(Debug, Default, Clone, Serialize, Deserialize)]
pub struct RecoilData {
    /// The base number to use for recoil damage calculation.
    #[serde(default)]
    pub base: RecoilBase,
    /// Percent of base.
    pub percent: Fraction<u16>,
    /// Apply Struggle recoil directly?
    #[serde(default)]
    pub struggle: bool,
}

fn default_crit_ratio() -> Option<u8> {
    Some(1)
}

/// Data for advanced move targeting.
#[derive(Debug, Default, Clone, Serialize, Deserialize)]
pub struct AdvancedTargeting {
    /// Does the move avoid random targets?
    #[serde(default)]
    pub no_random_target: bool,

    /// Does the move track the target, even if they have moved?
    #[serde(default)]
    pub tracks_target: bool,

    /// Does the move intelligently target different Mons based on immunities, protection,
    /// accuracy, etc.?
    ///
    /// Only works for single-target moves. Adjacent Mons on the same side will be hit if possible
    /// for each hit of a multihit move.
    #[serde(default)]
    pub smart_target: bool,
}

/// Data about a particular move.
///
/// Moves are the primary effect that drive battle forward. Every Mon enters a battle with their
/// moveset. Each turn, a Mon uses one move to affect the battle. Moves can damage opposing Mons,
/// affect ally Mons or the user itself, boost or drop stats, apply conditions to Mons or the
/// battlefield itself, and more.
#[derive(Debug, Default, Clone, Serialize, Deserialize)]
pub struct MoveData {
    /// Name of the move.
    pub name: String,
    /// Move category.
    pub category: MoveCategory,
    /// Move type.
    pub primary_type: Type,
    /// Base power.
    #[serde(default)]
    pub base_power: u32,
    /// Base accuracy.
    pub accuracy: Accuracy,
    /// Total power points, which is the number of times this move can be used.
    #[serde(default)]
    pub pp: u8,
    /// Move priority.
    #[serde(default)]
    pub priority: i8,
    /// Move target(s).
    pub target: MoveTarget,
    /// Move flags.
    pub flags: HashSet<MoveFlag>,

    /// Static damage dealt.
    pub damage: Option<u16>,
    /// Disallow PP boosts?
    #[serde(default)]
    pub no_pp_boosts: bool,

    /// Type of OHKO, if any.
    ///
    /// If a target has this type, it is immune.
    pub ohko_type: Option<OhkoType>,
    /// Type of switch that occurs on the user.
    pub user_switch: Option<SwitchType>,
    /// How the user self destructs.
    pub self_destruct: Option<SelfDestructType>,
    /// Recoil damage to apply.
    pub recoil: Option<RecoilData>,
    /// The percentage of the target's HP to drain.
    pub drain_percent: Option<Fraction<u16>>,
    /// Force STAB?
    #[serde(default)]
    pub force_stab: bool,

    /// Primary effect applied to the target.
    ///
    /// Applied when the move hits.
    pub hit_effect: Option<HitEffect>,
    /// Primary effect on the user.
    ///
    /// Applied when the move hits.
    pub user_effect: Option<HitEffect>,
    /// Chance of the user effect occurring.
    pub user_effect_chance: Option<Fraction<u16>>,
    /// Secondary effects applied to the target.
    #[serde(default)]
    pub secondary_effects: Vec<SecondaryEffectData>,

    /// Mon override for offensive stat calculations.
    ///
    /// By default, the move user is used.
    pub override_offensive_mon: Option<MonOverride>,
    /// Stat override for offensive stat calculations.
    ///
    /// By default, Atk is used for physical moves and SpA is used for special moves.
    pub override_offensive_stat: Option<Stat>,
    /// Mon override for defensive stat calculations.
    ///
    /// By default, the move target is used.
    pub override_defensive_mon: Option<MonOverride>,
    /// Stat override for defensive stat calculations.
    ///
    /// By default, Def is used for physical moves and SpD is used for special moves.
    pub override_defensive_stat: Option<Stat>,

    /// Critical hit ratio.
    #[serde(default = "default_crit_ratio")]
    pub crit_ratio: Option<u8>,
    /// Ignore accuracy modifiers?
    #[serde(default)]
    pub ignore_accuracy: bool,
    /// Ignore defensive modifiers?
    #[serde(default)]
    pub ignore_defensive: bool,
    /// Ignore evasion modifiers?
    #[serde(default)]
    pub ignore_evasion: bool,
    /// Ignore offensive modifiers?
    #[serde(default)]
    pub ignore_offensive: bool,
    /// Accuracy calculations should be run multiple times.
    #[serde(default)]
    pub multiaccuracy: bool,
    /// The move hits multiple times.
    pub multihit: Option<MultihitType>,
    /// The move will always critical hit.
    #[serde(default)]
    pub will_crit: bool,

    /// Advanced targeting.
    #[serde(default)]
    pub advanced_targeting: AdvancedTargeting,

    /// Data about how the move affects Z-Moves.
    pub z_move: Option<ZMoveData>,
    /// Data about how the move affects Max Moves.
    pub max_move: Option<MaxMoveData>,

    /// Dynamic battle effects.
    #[serde(default)]
    pub effect: serde_json::Value,
    /// Dynamic battle effects of the condition created by this move.
    #[serde(default)]
    pub condition: serde_json::Value,
}
