use std::{borrow::Cow, fmt};
use enum_iterator::Sequence;
use num_derive::FromPrimitive;
use num_traits::FromPrimitive;
use serde::{
    de::{Error as _, Unexpected},
    Deserialize, Serialize,
};
use serde_repr::{Deserialize_repr, Serialize_repr};
use wasm_bindgen::prelude::*;
use super::{macros::named_enum_serialize_deserialize, InvalidConstantString};
use crate::{JsCollectionFromValue, JsCollectionIntoValue};
#[wasm_bindgen]
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Sequence)]
pub enum StructureType {
    Spawn = "spawn",
    Extension = "extension",
    Road = "road",
    Wall = "constructedWall",
    Rampart = "rampart",
    KeeperLair = "keeperLair",
    Portal = "portal",
    Controller = "controller",
    Link = "link",
    Storage = "storage",
    Tower = "tower",
    Observer = "observer",
    PowerBank = "powerBank",
    PowerSpawn = "powerSpawn",
    Extractor = "extractor",
    Lab = "lab",
    Terminal = "terminal",
    Container = "container",
    Nuker = "nuker",
    Factory = "factory",
    InvaderCore = "invaderCore",
}
named_enum_serialize_deserialize!(StructureType);
impl StructureType {
    #[inline]
    pub const fn construction_cost(self) -> Option<u32> {
        use self::StructureType::*;
        let cost = match self {
            Spawn => 15_000,
            Extension => 3_000,
            Road => 300,
            Wall => 1,
            Rampart => 1,
            Link => 5_000,
            Storage => 30_000,
            Tower => 5_000,
            Observer => 8_000,
            PowerSpawn => 100_000,
            Extractor => 5_000,
            Lab => 50_000,
            Terminal => 100_000,
            Container => 5_000,
            Nuker => 100_000,
            Factory => 100_000,
            _ => return None,
        };
        Some(cost)
    }
    #[inline]
    pub const fn controller_structures(self, current_rcl: u32) -> u32 {
        use self::StructureType::*;
        match self {
            Spawn => match current_rcl {
                0 => 0,
                1..=6 => 1,
                7 => 2,
                _ => 3,
            },
            Extension => match current_rcl {
                0 | 1 => 0,
                2 => 5,
                3 => 10,
                4 => 20,
                5 => 30,
                6 => 40,
                7 => 50,
                _ => 60,
            },
            Road => 2500,
            Wall => match current_rcl {
                0 | 1 => 0,
                _ => 2500,
            },
            Rampart => match current_rcl {
                0 | 1 => 0,
                _ => 2500,
            },
            Link => match current_rcl {
                0..=4 => 0,
                5 => 2,
                6 => 3,
                7 => 4,
                _ => 6,
            },
            Storage => match current_rcl {
                0..=3 => 0,
                _ => 1,
            },
            Tower => match current_rcl {
                0..=2 => 0,
                3 | 4 => 1,
                5 | 6 => 2,
                7 => 3,
                _ => 6,
            },
            Observer => match current_rcl {
                0..=7 => 0,
                _ => 1,
            },
            PowerSpawn => match current_rcl {
                0..=7 => 0,
                _ => 1,
            },
            Extractor => match current_rcl {
                0..=5 => 0,
                _ => 1,
            },
            Lab => match current_rcl {
                0..=5 => 0,
                6 => 3,
                7 => 6,
                _ => 10,
            },
            Terminal => match current_rcl {
                0..=5 => 0,
                _ => 1,
            },
            Container => 5,
            Nuker => match current_rcl {
                0..=7 => 0,
                _ => 1,
            },
            Factory => match current_rcl {
                0..=6 => 0,
                _ => 1,
            },
            _ => 0,
        }
    }
    #[inline]
    pub const fn initial_hits(self) -> Option<u32> {
        use self::StructureType::*;
        use super::numbers::*;
        let hits = match self {
            Spawn => SPAWN_HITS,
            Extension => EXTENSION_HITS,
            Road => ROAD_HITS,
            Wall => WALL_HITS,
            Rampart => RAMPART_HITS,
            Link => LINK_HITS,
            Storage => STORAGE_HITS,
            Tower => TOWER_HITS,
            Observer => OBSERVER_HITS,
            PowerBank => POWER_BANK_HITS,
            PowerSpawn => POWER_SPAWN_HITS,
            Extractor => EXTRACTOR_HITS,
            Lab => LAB_HITS,
            Terminal => TERMINAL_HITS,
            Container => CONTAINER_HITS,
            Nuker => NUKER_HITS,
            Factory => FACTORY_HITS,
            InvaderCore => INVADER_CORE_HITS,
            _ => return None,
        };
        Some(hits)
    }
}
#[wasm_bindgen]
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Sequence)]
pub enum IntershardResourceType {
    CpuUnlock = "cpuUnlock",
    Pixel = "pixel",
    AccessKey = "accessKey",
}
named_enum_serialize_deserialize!(IntershardResourceType);
#[wasm_bindgen]
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Sequence)]
pub enum ResourceType {
    Energy = "energy",
    Power = "power",
    Hydrogen = "H",
    Oxygen = "O",
    Utrium = "U",
    Lemergium = "L",
    Keanium = "K",
    Zynthium = "Z",
    Catalyst = "X",
    Ghodium = "G",
    Silicon = "silicon",
    Metal = "metal",
    Biomass = "biomass",
    Mist = "mist",
    Hydroxide = "OH",
    ZynthiumKeanite = "ZK",
    UtriumLemergite = "UL",
    UtriumHydride = "UH",
    UtriumOxide = "UO",
    KeaniumHydride = "KH",
    KeaniumOxide = "KO",
    LemergiumHydride = "LH",
    LemergiumOxide = "LO",
    ZynthiumHydride = "ZH",
    ZynthiumOxide = "ZO",
    GhodiumHydride = "GH",
    GhodiumOxide = "GO",
    UtriumAcid = "UH2O",
    UtriumAlkalide = "UHO2",
    KeaniumAcid = "KH2O",
    KeaniumAlkalide = "KHO2",
    LemergiumAcid = "LH2O",
    LemergiumAlkalide = "LHO2",
    ZynthiumAcid = "ZH2O",
    ZynthiumAlkalide = "ZHO2",
    GhodiumAcid = "GH2O",
    GhodiumAlkalide = "GHO2",
    CatalyzedUtriumAcid = "XUH2O",
    CatalyzedUtriumAlkalide = "XUHO2",
    CatalyzedKeaniumAcid = "XKH2O",
    CatalyzedKeaniumAlkalide = "XKHO2",
    CatalyzedLemergiumAcid = "XLH2O",
    CatalyzedLemergiumAlkalide = "XLHO2",
    CatalyzedZynthiumAcid = "XZH2O",
    CatalyzedZynthiumAlkalide = "XZHO2",
    CatalyzedGhodiumAcid = "XGH2O",
    CatalyzedGhodiumAlkalide = "XGHO2",
    Ops = "ops",
    UtriumBar = "utrium_bar",
    LemergiumBar = "lemergium_bar",
    ZynthiumBar = "zynthium_bar",
    KeaniumBar = "keanium_bar",
    GhodiumMelt = "ghodium_melt",
    Oxidant = "oxidant",
    Reductant = "reductant",
    Purifier = "purifier",
    Battery = "battery",
    Composite = "composite",
    Crystal = "crystal",
    Liquid = "liquid",
    Wire = "wire",
    Switch = "switch",
    Transistor = "transistor",
    Microchip = "microchip",
    Circuit = "circuit",
    Device = "device",
    Cell = "cell",
    Phlegm = "phlegm",
    Tissue = "tissue",
    Muscle = "muscle",
    Organoid = "organoid",
    Organism = "organism",
    Alloy = "alloy",
    Tube = "tube",
    Fixtures = "fixtures",
    Frame = "frame",
    Hydraulics = "hydraulics",
    Machine = "machine",
    Condensate = "condensate",
    Concentrate = "concentrate",
    Extract = "extract",
    Spirit = "spirit",
    Emanation = "emanation",
    Essence = "essence",
    #[cfg(feature = "score")]
    Score = "score",
    #[cfg(feature = "symbols")]
    SymbolAleph = "symbol_aleph",
    #[cfg(feature = "symbols")]
    SymbolBeth = "symbol_beth",
    #[cfg(feature = "symbols")]
    SymbolGimmel = "symbol_gimmel",
    #[cfg(feature = "symbols")]
    SymbolDaleth = "symbol_daleth",
    #[cfg(feature = "symbols")]
    SymbolHe = "symbol_he",
    #[cfg(feature = "symbols")]
    SymbolWaw = "symbol_waw",
    #[cfg(feature = "symbols")]
    SymbolZayin = "symbol_zayin",
    #[cfg(feature = "symbols")]
    SymbolHeth = "symbol_heth",
    #[cfg(feature = "symbols")]
    SymbolTeth = "symbol_teth",
    #[cfg(feature = "symbols")]
    SymbolYodh = "symbol_yodh",
    #[cfg(feature = "symbols")]
    SymbolKaph = "symbol_kaph",
    #[cfg(feature = "symbols")]
    SymbolLamedh = "symbol_lamedh",
    #[cfg(feature = "symbols")]
    SymbolMem = "symbol_mem",
    #[cfg(feature = "symbols")]
    SymbolNun = "symbol_nun",
    #[cfg(feature = "symbols")]
    SymbolSamekh = "symbol_samekh",
    #[cfg(feature = "symbols")]
    SymbolAyin = "symbol_ayin",
    #[cfg(feature = "symbols")]
    SymbolPe = "symbol_pe",
    #[cfg(feature = "symbols")]
    SymbolTsade = "symbol_tsade",
    #[cfg(feature = "symbols")]
    SymbolQoph = "symbol_qoph",
    #[cfg(feature = "symbols")]
    SymbolRes = "symbol_res",
    #[cfg(feature = "symbols")]
    SymbolSin = "symbol_sim",
    #[cfg(feature = "symbols")]
    SymbolTaw = "symbol_taw",
    #[cfg(feature = "thorium")]
    Thorium = "T",
}
named_enum_serialize_deserialize!(ResourceType);
impl ResourceType {
    #[inline]
    pub const fn boost(self) -> Option<Boost> {
        use ResourceType::*;
        let boost = match self {
            UtriumHydride => Boost::Attack(2),
            UtriumAcid => Boost::Attack(3),
            CatalyzedUtriumAcid => Boost::Attack(4),
            UtriumOxide => Boost::Harvest(3),
            UtriumAlkalide => Boost::Harvest(5),
            CatalyzedUtriumAlkalide => Boost::Harvest(7),
            KeaniumHydride => Boost::Carry(2),
            KeaniumAcid => Boost::Carry(3),
            CatalyzedKeaniumAcid => Boost::Carry(4),
            KeaniumOxide => Boost::RangedAttack(2),
            KeaniumAlkalide => Boost::RangedAttack(3),
            CatalyzedKeaniumAlkalide => Boost::RangedAttack(4),
            LemergiumHydride => Boost::BuildAndRepair(1.5),
            LemergiumAcid => Boost::BuildAndRepair(1.8),
            CatalyzedLemergiumAcid => Boost::BuildAndRepair(2.0),
            LemergiumOxide => Boost::Heal(2),
            LemergiumAlkalide => Boost::Heal(3),
            CatalyzedLemergiumAlkalide => Boost::Heal(4),
            ZynthiumHydride => Boost::Dismantle(2),
            ZynthiumAcid => Boost::Dismantle(3),
            CatalyzedZynthiumAcid => Boost::Dismantle(4),
            ZynthiumOxide => Boost::Move(2),
            ZynthiumAlkalide => Boost::Move(3),
            CatalyzedZynthiumAlkalide => Boost::Move(4),
            GhodiumHydride => Boost::UpgradeController(1.5),
            GhodiumAcid => Boost::UpgradeController(1.8),
            CatalyzedGhodiumAcid => Boost::UpgradeController(2.0),
            GhodiumOxide => Boost::Tough(0.7),
            GhodiumAlkalide => Boost::Tough(0.5),
            CatalyzedGhodiumAlkalide => Boost::Tough(0.3),
            _ => return None,
        };
        Some(boost)
    }
}
#[derive(Copy, Clone, Debug)]
pub enum Boost {
    Harvest(u32),
    BuildAndRepair(f32),
    Dismantle(u32),
    UpgradeController(f32),
    Attack(u32),
    RangedAttack(u32),
    Heal(u32),
    Carry(u32),
    Move(u32),
    Tough(f32),
}
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize, Sequence)]
#[serde(untagged)]
pub enum MarketResourceType {
    Resource(ResourceType),
    IntershardResource(IntershardResourceType),
}
impl wasm_bindgen::convert::FromWasmAbi for MarketResourceType {
    type Abi = <wasm_bindgen::JsValue as wasm_bindgen::convert::FromWasmAbi>::Abi;
    #[inline]
    unsafe fn from_abi(js: Self::Abi) -> Self {
        let s = <wasm_bindgen::JsValue as wasm_bindgen::convert::FromWasmAbi>::from_abi(js);
        match ResourceType::from_js_value(&s) {
            Some(r) => Self::Resource(r),
            None => {
                match IntershardResourceType::from_js_value(&s) {
                    Some(r) => Self::IntershardResource(r),
                    None => Self::Resource(ResourceType::__Nonexhaustive),
                }
            }
        }
    }
}
impl wasm_bindgen::convert::IntoWasmAbi for MarketResourceType {
    type Abi = <wasm_bindgen::JsValue as wasm_bindgen::convert::IntoWasmAbi>::Abi;
    #[inline]
    fn into_abi(self) -> Self::Abi {
        match self {
            MarketResourceType::Resource(r) => {
                <wasm_bindgen::JsValue as wasm_bindgen::convert::IntoWasmAbi>::into_abi(r.into())
            }
            MarketResourceType::IntershardResource(r) => {
                <wasm_bindgen::JsValue as wasm_bindgen::convert::IntoWasmAbi>::into_abi(r.into())
            }
        }
    }
}
impl wasm_bindgen::describe::WasmDescribe for MarketResourceType {
    fn describe() {
        <wasm_bindgen::JsValue as wasm_bindgen::describe::WasmDescribe>::describe()
    }
}
#[wasm_bindgen]
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Sequence)]
pub enum PowerCreepClass {
    Operator = "operator",
}
named_enum_serialize_deserialize!(PowerCreepClass);
#[wasm_bindgen]
#[derive(
    Debug,
    PartialEq,
    Eq,
    Clone,
    Copy,
    Hash,
    FromPrimitive,
    Deserialize_repr,
    Serialize_repr,
    Sequence,
)]
#[repr(u32)]
pub enum PowerType {
    GenerateOps = 1,
    OperateSpawn = 2,
    OperateTower = 3,
    OperateStorage = 4,
    OperateLab = 5,
    OperateExtension = 6,
    OperateObserver = 7,
    OperateTerminal = 8,
    DisruptSpawn = 9,
    DisruptTower = 10,
    Shield = 12,
    RegenSource = 13,
    RegenMineral = 14,
    DisruptTerminal = 15,
    OperatePower = 16,
    Fortify = 17,
    OperateController = 18,
    OperateFactory = 19,
}
impl JsCollectionFromValue for PowerType {
    fn from_value(val: JsValue) -> Self {
        let power_type_id = if let Some(val) = val.as_string() {
            val.parse::<u32>().expect("expected parseable u32 string")
        } else {
            val.as_f64().expect("expected number value") as u32
        };
        Self::from_u32(power_type_id).expect("unknown power type")
    }
}
impl JsCollectionIntoValue for PowerType {
    fn into_value(self) -> JsValue {
        JsValue::from_f64(self as u32 as f64)
    }
}
#[wasm_bindgen]
#[derive(
    Copy,
    Clone,
    Debug,
    PartialEq,
    Eq,
    Hash,
    FromPrimitive,
    Serialize_repr,
    Deserialize_repr,
    Sequence,
)]
#[repr(u32)]
pub enum NaturalEffectType {
    Invulnerability = 1001,
    CollapseTimer = 1002,
}
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Sequence)]
pub enum EffectType {
    PowerEffect(PowerType),
    NaturalEffect(NaturalEffectType),
}
impl wasm_bindgen::convert::IntoWasmAbi for EffectType {
    type Abi = u32;
    #[inline]
    fn into_abi(self) -> Self::Abi {
        match self {
            EffectType::PowerEffect(e) => (e as u32).into_abi(),
            EffectType::NaturalEffect(e) => (e as u32).into_abi(),
        }
    }
}
impl wasm_bindgen::convert::FromWasmAbi for EffectType {
    type Abi = u32;
    #[inline]
    unsafe fn from_abi(js: u32) -> Self {
        match PowerType::from_u32(js) {
            Some(pt) => Self::PowerEffect(pt),
            None => {
                Self::NaturalEffect(NaturalEffectType::from_u32(js).expect("unknown effect id!"))
            }
        }
    }
}
impl wasm_bindgen::describe::WasmDescribe for EffectType {
    fn describe() {
        wasm_bindgen::describe::inform(wasm_bindgen::describe::U32)
    }
}
#[cfg(test)]
mod test {
    use super::*;
    #[test]
    fn resources_rust_to_serde_json_from_serde_json_roundtrip() {
        for resource in enum_iterator::all::<ResourceType>() {
            if resource != ResourceType::__Nonexhaustive {
                let serialized = serde_json::to_string(&resource).unwrap();
                let parsed: ResourceType = serde_json::from_str(&serialized).unwrap();
                assert_eq!(resource, parsed);
            }
        }
    }
    #[test]
    fn resources_rust_to_display_from_str_roundtrip() {
        for resource in enum_iterator::all::<ResourceType>() {
            if resource != ResourceType::__Nonexhaustive {
                let string = format!("{}", resource);
                let parsed = ResourceType::from_str(&string).unwrap();
                assert_eq!(resource, parsed);
            }
        }
    }
    #[test]
    fn resources_rust_vec_to_serde_json_from_serde_json_roundtrip() {
        let mut resources = vec![];
        for resource in enum_iterator::all::<ResourceType>() {
            if resource != ResourceType::__Nonexhaustive {
                resources.push(resource);
            }
        }
        let serialized = serde_json::to_string(&resources).unwrap();
        let resources_reparsed: Vec<ResourceType> = serde_json::from_str(&serialized).unwrap();
        assert_eq!(resources, resources_reparsed);
    }
    #[test]
    fn resources_rust_vec_to_serde_json_from_serde_json_roundtrip_via_values() {
        let mut resources = vec![];
        for resource in enum_iterator::all::<ResourceType>() {
            if resource != ResourceType::__Nonexhaustive {
                resources.push(resource);
            }
        }
        let serialized = serde_json::to_string(&resources).unwrap();
        let resources_reparsed_values: Vec<serde_json::Value> =
            serde_json::from_str(&serialized).unwrap();
        let resources_reparsed_native: Vec<ResourceType> = resources_reparsed_values
            .iter()
            .map(|v| serde_json::from_value(v.clone()).unwrap())
            .collect();
        assert_eq!(resources, resources_reparsed_native);
    }
    #[test]
    fn intershard_resources_rust_to_serde_json_from_serde_json_roundtrip() {
        for resource in enum_iterator::all::<IntershardResourceType>() {
            if resource != IntershardResourceType::__Nonexhaustive {
                let serialized = serde_json::to_string(&resource).unwrap();
                let parsed: IntershardResourceType = serde_json::from_str(&serialized).unwrap();
                assert_eq!(resource, parsed);
            }
        }
    }
    #[test]
    fn intershard_resources_rust_to_display_from_str_roundtrip() {
        for resource in enum_iterator::all::<IntershardResourceType>() {
            if resource != IntershardResourceType::__Nonexhaustive {
                let string = format!("{}", resource);
                let parsed = IntershardResourceType::from_str(&string).unwrap();
                assert_eq!(resource, parsed);
            }
        }
    }
    #[test]
    fn intershard_resources_rust_vec_to_serde_json_from_serde_json_roundtrip() {
        let mut resources = vec![];
        for resource in enum_iterator::all::<IntershardResourceType>() {
            if resource != IntershardResourceType::__Nonexhaustive {
                resources.push(resource);
            }
        }
        let serialized = serde_json::to_string(&resources).unwrap();
        let resources_reparsed: Vec<IntershardResourceType> =
            serde_json::from_str(&serialized).unwrap();
        assert_eq!(resources, resources_reparsed);
    }
    #[test]
    fn intershard_resources_rust_vec_to_serde_json_from_serde_json_roundtrip_via_values() {
        let mut resources = vec![];
        for resource in enum_iterator::all::<IntershardResourceType>() {
            if resource != IntershardResourceType::__Nonexhaustive {
                resources.push(resource);
            }
        }
        let serialized = serde_json::to_string(&resources).unwrap();
        let resources_reparsed_values: Vec<serde_json::Value> =
            serde_json::from_str(&serialized).unwrap();
        let resources_reparsed_native: Vec<IntershardResourceType> = resources_reparsed_values
            .iter()
            .map(|v| serde_json::from_value(v.clone()).unwrap())
            .collect();
        assert_eq!(resources, resources_reparsed_native);
    }
    #[test]
    fn market_resources_rust_to_serde_json_from_serde_json_roundtrip() {
        for resource in enum_iterator::all::<MarketResourceType>() {
            if resource != MarketResourceType::Resource(ResourceType::__Nonexhaustive)
                && resource
                    != MarketResourceType::IntershardResource(
                        IntershardResourceType::__Nonexhaustive,
                    )
            {
                let serialized = serde_json::to_string(&resource).unwrap();
                let parsed: MarketResourceType = serde_json::from_str(&serialized).unwrap();
                assert_eq!(resource, parsed);
            }
        }
    }
    #[test]
    fn market_resources_rust_vec_to_serde_json_from_serde_json_roundtrip() {
        let mut resources = vec![];
        for resource in enum_iterator::all::<MarketResourceType>() {
            if resource != MarketResourceType::Resource(ResourceType::__Nonexhaustive)
                && resource
                    != MarketResourceType::IntershardResource(
                        IntershardResourceType::__Nonexhaustive,
                    )
            {
                resources.push(resource);
            }
        }
        let serialized = serde_json::to_string(&resources).unwrap();
        let resources_reparsed: Vec<MarketResourceType> =
            serde_json::from_str(&serialized).unwrap();
        assert_eq!(resources, resources_reparsed);
    }
    #[test]
    fn market_resources_rust_vec_to_serde_json_from_serde_json_roundtrip_via_values() {
        let mut resources = vec![];
        for resource in enum_iterator::all::<MarketResourceType>() {
            if resource != MarketResourceType::Resource(ResourceType::__Nonexhaustive)
                && resource
                    != MarketResourceType::IntershardResource(
                        IntershardResourceType::__Nonexhaustive,
                    )
            {
                resources.push(resource);
            }
        }
        let serialized = serde_json::to_string(&resources).unwrap();
        let resources_reparsed_values: Vec<serde_json::Value> =
            serde_json::from_str(&serialized).unwrap();
        let resources_reparsed_native: Vec<MarketResourceType> = resources_reparsed_values
            .iter()
            .map(|v| serde_json::from_value(v.clone()).unwrap())
            .collect();
        assert_eq!(resources, resources_reparsed_native);
    }
}