use rand::{thread_rng, Rng};
use uuid::Uuid;

use cryp::{Cryp, CrypEffect, EffectMeta, Stat};
use vbox::{Var};

pub fn resolve(skill: Skill, source: &mut Cryp, target: &mut Cryp, mut resolutions: Vec<Resolution>) -> Resolutions {
    let mut rng = thread_rng();
    let _base: u64 = rng.gen();

    if let Some(disable) = source.disabled(skill) {
        resolutions.push(Resolution::new(source, target).event(Event::Disable { disable, skill }));
        return resolutions;
    }

    if target.is_ko() {
        resolutions.push(Resolution::new(source, target).event(Event::TargetKo { skill }));
        return resolutions;
    }

    if target.affected(Effect::Reflect) {
        // guard against overflow
        if source.affected(Effect::Reflect) {
            return resolutions;
        }
        resolutions.push(Resolution::new(source, target).event(Event::Reflection { skill }));
        return resolve(skill, target, source, resolutions);
    }

    // match self.category() == Category::Red {
    //     true => {
    //         if let Some(evasion) = target.evade(*self) {
    //             resolutions.push(evasion);
    //             return Event;
    //         }
    //     },
    //     false => (),
    // }

    resolutions = match skill {
        Skill::Amplify => amplify(source, target, resolutions), // increase magic damage
        Skill::Attack => attack(source, target, resolutions),
        Skill::Banish => banish(source, target, resolutions), // TODO prevent all actions
        Skill::Blast => blast(source, target, resolutions),
        Skill::Block => block(source, target, resolutions),
        Skill::Clutch => clutch(source, target, resolutions),
        Skill::Corrupt => corrupt(source, target, resolutions),
        Skill::CorruptionTick => corruption_tick(source, target, resolutions),
        Skill::Curse => curse(source, target, resolutions),
        Skill::Decay => decay(source, target, resolutions), // dot
        Skill::DecayTick => decay_tick(source, target, resolutions), // dot
        Skill::Empower => empower(source, target, resolutions), // increased phys damage
        Skill::Haste => haste(source, target, resolutions), // speed slow
        Skill::Heal => heal(source, target, resolutions),
        Skill::Hex => hex(source, target, resolutions),
        Skill::Hostility => hostility(source, target, resolutions),
        Skill::Invert => invert(source, target, resolutions),
        Skill::Injure => injure(source, target, resolutions),
        Skill::Parry => parry(source, target, resolutions),
        Skill::Purge => purge(source, target, resolutions), // dispel all buffs
        Skill::Purify => purify(source, target, resolutions), // dispel all debuffs
        Skill::Recharge => recharge(source, target, resolutions), // target is immune to magic damage and fx
        Skill::Reflect => reflect(source, target, resolutions),
        Skill::Riposte => panic!("riposte should not be caste"),
        Skill::Ruin => ruin(source, target, resolutions),
        Skill::Shield => shield(source, target, resolutions), // target is immune to magic damage and fx
        Skill::Silence => silence(source, target, resolutions), // target cannot cast spells
        Skill::Siphon => siphon(source, target, resolutions),
        Skill::SiphonTick => siphon_tick(source, target, resolutions), // hot
        Skill::Slay => unimplemented!(),
        Skill::Slow => slow(source, target, resolutions), // speed slow
        Skill::Snare => snare(source, target, resolutions),
        Skill::Strangle => strangle(source, target, resolutions),
        Skill::StrangleTick => strangle_tick(source, target, resolutions),
        Skill::Strike => strike(source, target, resolutions),
        Skill::Stun => stun(source, target, resolutions),
        Skill::Taunt => taunt(source, target, resolutions),
        Skill::Throw => throw(source, target, resolutions), // no damage stun, adds vulnerable
        Skill::Triage => triage(source, target, resolutions), // hot
        Skill::TriageTick => triage_tick(source, target, resolutions), // hot

        // -----------------
        // Test
        // -----------------
        Skill::TestTouch => touch(source, target, resolutions),
        Skill::TestStun => stun(source, target, resolutions),
        Skill::TestBlock => block(source, target, resolutions),
        Skill::TestParry => parry(source, target, resolutions),
        Skill::TestSiphon => siphon(source, target, resolutions),
    };

    // if any event dealt damage to target cryp
    // hit them with corruption

    // on damage events
    // todo not sure if this fucks up with multiple calls to resolve
    // have to think
    for r in resolutions.clone() {
        match r.event {
            Event::Damage { amount, skill, mitigation: _, category: _ } => {
                if target.affected(Effect::Corrupt) {
                    resolutions = corruption(target, source, resolutions);
                }

                if target.affected(Effect::Hostility) {
                    resolutions = hatred(target, source, resolutions, skill, amount);
                }
            },
            Event::Immunity { skill: _, immunity } => match immunity.contains(&Effect::Parry) {
                true => resolutions = riposte(target, source, resolutions),
                false => (),
            },
            _ => (),
        }
    };

    // i don't think we need to check the source being ko
    if target.is_ko() {
        resolutions.push(Resolution::new(source, target).event(Event::Ko));
        target.effects.clear();
    }

    return resolutions;
}



#[derive(Debug,Clone,PartialEq,Serialize,Deserialize)]
pub struct Cast {
    pub id: Uuid,
    pub source_team_id: Uuid,
    pub source_cryp_id: Uuid,
    pub target_cryp_id: Uuid,
    pub skill: Skill,
    pub speed: u64,
    pub resolutions: Resolutions,
}

impl Cast {
    pub fn new(source_cryp_id: Uuid, source_team_id: Uuid, target_cryp_id: Uuid, skill: Skill) -> Cast {
        return Cast {
            id: Uuid::new_v4(),
            source_cryp_id,
            source_team_id,
            target_cryp_id,
            skill,
            speed: 0,
            resolutions: vec![],
        };
    }

    pub fn new_tick(source: &mut Cryp, target: &mut Cryp, skill: Skill) -> Cast {
        Cast::new(source.id, source.account, target.id, skill)
    }

    pub fn used_cooldown(&self) -> bool {
        return self.skill.base_cd().is_some();
    }
}

pub type Disable = Vec<Effect>;
pub type Immunity = Vec<Effect>;

#[derive(Debug,Clone,PartialEq,Serialize,Deserialize)]
pub struct LogCryp {
    pub id: Uuid,
    pub name: String,
}

#[derive(Debug,Clone,PartialEq,Serialize,Deserialize)]
pub struct Resolution {
    pub source: LogCryp,
    pub target: LogCryp,
    pub event: Event,
}

impl Resolution {
    fn new(source: &Cryp, target: &Cryp) -> Resolution {
        Resolution {
            source: LogCryp { id: source.id, name: source.name.clone() },
            target: LogCryp { id: target.id, name: target.name.clone() },
            event: Event::Incomplete,
        }
    }

    fn event(mut self, e: Event) -> Resolution {
        self.event = e;
        self
    }
}


#[derive(Debug,Clone,PartialEq,Serialize,Deserialize)]
pub enum Event {
    Disable     { skill: Skill, disable: Disable },
    Immunity    { skill: Skill, immunity: Immunity },
    Damage      { skill: Skill, amount: u64, mitigation: u64, category: Category },
    Healing     { skill: Skill, amount: u64, overhealing: u64 },
    Recharge    { skill: Skill, red: u64, blue: u64 },
    Inversion   { skill: Skill },
    Reflection  { skill: Skill },
    Effect      { skill: Skill, effect: Effect, duration: u8 },
    Removal     { effect: Effect },
    Evasion     { skill: Skill, evasion_rating: u64 },
    TargetKo    { skill: Skill },
    Ko,

    Incomplete,
}

type Resolutions = Vec<Resolution>;
pub type Cooldown = Option<u8>;

#[derive(Debug,Clone,Copy,PartialEq,Serialize,Deserialize)]
pub enum Effect {
    // physical
    Stun,
    Parry,
    Block,
    Bleed,
    Leech,
    Airborne,
    Untouchable,
    Deadly,
    Vulnerable,
    Fury,
    Blind,
    Snare,
    Clutch,
    Injured,

    Reflect,

    Empower,
    Taunt,

    Invert,
    Strangle,
    Strangling,

    // magic
    Hex,
    Ruin,
    Curse,
    Banish,
    Slow,
    Haste,
    Enslave,
    Mesmerise,
    Amplify,
    Silence,

    // corrupt is the buff that applies
    // corruption the dmg debuff
    Corrupt,
    Corruption,

    // hostility is the buff
    // hatred is the increased damage
    Hostility,
    Hatred,

    // magic immunity
    Shield,

    // effects over time
    Triage,
    Decay,
    Regen,
    Siphon,

    SpeedSiphon,
    SpeedIncrease,

    Ko,
}

impl Effect {
    pub fn immune(&self, skill: Skill) -> bool {
        match self {
            Effect::Parry => match skill.category() {
                Category::Blue => false,
                Category::Red => true,
                _ => false,
            },
            Effect::Shield => match skill.category() {
                Category::Blue => true,
                Category::Red => false,
                _ => false,
            },
            Effect::Strangle => skill != Skill::StrangleTick,
            Effect::Strangling => match skill.category() {
                Category::BlueTick => false,
                Category::RedTick => false,
                _ => true,
            },
            Effect::Banish => true,
            Effect::Injured => match skill.category() {
                Category::Green => true,
                Category::GreenTick => true,
                _ => false,
            },
            _ => false,
        }
    }

    pub fn disables_skill(&self, skill: Skill) -> bool {
        match self {
            Effect::Stun => true,
            Effect::Hex => true,
            Effect::Ruin => true,
            Effect::Banish => true,
            Effect::Strangle => true,
            Effect::Strangling => skill != Skill::StrangleTick,
            Effect::Silence => match skill.category() {
                Category::Blue => true,
                Category::Red => false,
                _ => false,
            },
            Effect::Snare => match skill.category() {
                Category::Blue => false,
                Category::Red => true,
                _ => false,
            },

            Effect::Ko => match skill.category() {
                Category::BlueTick => false,
                Category::GreenTick => false,
                _ => true,
            },
            _ => false,
        }
    }

    pub fn modifications(&self) -> Vec<Stat> {
        match self {
            Effect::Empower => vec![Stat::RedDamage],
            Effect::Vulnerable => vec![Stat::RedDamageTaken],
            Effect::Block => vec![Stat::RedDamageTaken],

            Effect::Hatred => vec![Stat::RedDamage, Stat::BlueDamage],

            Effect::Amplify => vec![Stat::BlueDamage],
            Effect::Curse => vec![Stat::BlueDamageTaken],

            Effect::Haste => vec![Stat::Speed],
            Effect::Slow => vec![Stat::Speed],

            _ => vec![],
        }
    }

    pub fn apply(&self, value: u64, meta: Option<EffectMeta>) -> u64 {
        match self {
            Effect::Empower     => value << 1,
            Effect::Vulnerable  => value << 1,
            Effect::Block       => value >> 1,

            Effect::Amplify     => value << 1,
            Effect::Curse       => value << 1,

            Effect::Haste       => value << 1,
            Effect::Slow        => value >> 1,

            Effect::Hatred      => value + match meta {
                Some(EffectMeta::AddedDamage(d)) => d,
                _ => panic!("hatred meta not damage"),
            },

            _ => {
                println!("{:?} does not have a mod effect", self);
                return value;
            },
        }
    }

    pub fn category(&self) -> Category {
        match self {
            // physical
            Effect::Stun => Category::RedDebuff,
            Effect::Block => Category::RedBuff,
            Effect::Parry => Category::RedBuff,
            Effect::Bleed => Category::RedDebuff,
            Effect::Leech => Category::RedDebuff,
            Effect::Airborne => Category::RedDebuff,
            Effect::Untouchable => Category::RedBuff,
            Effect::Deadly => Category::RedBuff,
            Effect::Vulnerable => Category::RedDebuff,
            Effect::Fury => Category::RedBuff,
            Effect::Blind => Category::RedDebuff,
            Effect::Snare => Category::RedDebuff,
            Effect::Clutch => Category::RedBuff,
            Effect::Taunt => Category::RedBuff,
            Effect::Empower => Category::RedBuff,
            Effect::Injured => Category::RedDebuff,
            Effect::Strangle => Category::RedDebuff,
            Effect::Strangling => Category::RedBuff,

            // magic
            Effect::Hex => Category::BlueDebuff,
            Effect::Ruin => Category::BlueDebuff,
            Effect::Curse => Category::BlueDebuff,
            Effect::Banish => Category::BlueDebuff, // todo randomise
            Effect::Slow => Category::BlueDebuff,
            Effect::Haste => Category::BlueBuff,
            Effect::Hatred => Category::BlueBuff,
            Effect::Reflect => Category::BlueBuff,
            Effect::Enslave => Category::BlueDebuff,
            Effect::Mesmerise => Category::BlueDebuff,
            Effect::Amplify => Category::BlueBuff,
            Effect::Silence => Category::BlueDebuff,

            Effect::Corrupt => Category::BlueBuff,
            Effect::Corruption => Category::BlueDebuff,

            Effect::Hostility => Category::BlueBuff,

            // magic immunity
            Effect::Shield => Category::BlueBuff,
            Effect::Invert => Category::GreenBuff,

            // effects over time
            Effect::Triage => Category::BlueBuff,
            Effect::Decay => Category::BlueDebuff,
            Effect::Regen => Category::BlueBuff,
            Effect::Siphon => Category::BlueDebuff,

            Effect::SpeedSiphon => Category::BlueDebuff,
            Effect::SpeedIncrease => Category::BlueBuff,

            Effect::Ko => Category::Ko,
        }
    }

    pub fn duration(&self) -> u8 {
        match self {
            Effect::Stun => 2,
            Effect::Block => 1,
            Effect::Parry => 1,
            Effect::Clutch => 1,

            Effect::Reflect => 1,

            Effect::Injured => 2,

            Effect::Strangle => 2,
            Effect::Strangling => 2,

            Effect::Vulnerable => 2,
            Effect::Snare => 2,

            Effect::Taunt => 1,
            Effect::Empower => 2,
            Effect::Invert => 1,

            Effect::Hex => 2,
            Effect::Ruin => 1,
            Effect::Curse => 2,
            Effect::Banish => 1,

            Effect::Slow => 2,
            Effect::Haste => 2,

            Effect::Amplify => 2,
            Effect::Silence => 2,

            Effect::Hostility => 2,
            Effect::Hatred => 5,

            Effect::Corrupt => 2,
            Effect::Corruption => 3,

            Effect::Shield => 2,

            Effect::Triage => 3,
            Effect::Decay => 3,
            Effect::Siphon => 2,

            _ => {
                println!("{:?} does not have a duration", self);
                return 1;
            },
        }
    }

}

#[derive(Debug,Clone,Copy,PartialEq,Serialize,Deserialize)]
pub enum Category {
    Red,
    RedDamage,
    RedDebuff,
    RedBuff,
    RedTick,
    Blue,
    BlueDamage,
    BlueDebuff,
    BlueBuff,
    BlueTick,
    Green,
    GreenTick,
    GreenDamage,
    GreenBuff,
    Ko,
}

#[derive(Debug,Clone,Copy,PartialEq,Serialize,Deserialize)]
pub enum Skill {
    Attack,

    // -----------------
    // Nature
    // -----------------
    Block, // reduce damage
    Parry, // avoid all damage
    Riposte,
    Snare,
    Injure,

    Recharge,
    Reflect,
    Ruin,
    Slay,
    Clutch,
    Taunt,
    Invert,

    Strangle,
    StrangleTick,

    Strike,
    Stun,
    // Evade, // actively evade

    // -----------------
    // Nonviolence
    // -----------------
    Heal,
    Triage, // hot
    TriageTick,
    Throw, // no damage stun, adds vulnerable

    // Sleep,
    // Nightmare,

    // -------------------
    // Destruction
    // -------------------
    Blast,
    Amplify,
    Decay, // dot
    DecayTick, // dot
    Siphon,
    SiphonTick,
    Curse,

    Hostility,

    Corrupt,
    CorruptionTick,


    // -----------------
    // Purity
    // -----------------
    Empower,
    Shield,
    Silence,
    Purify,
    Purge,

    // -----------------
    // Chaos
    // -----------------
    Banish,
    Hex,
    Haste,
    Slow,

    // used by tests, no cd, no damage
    TestTouch,
    TestStun,
    TestBlock,
    TestParry,
    TestSiphon,
}

impl Skill {
    pub fn base_cd(&self) -> Cooldown {
        match self {
            Skill::Attack => None,
            Skill::Strike => None,
            Skill::Block => None, // reduce damage
            Skill::Parry => None, // avoid all damage
            Skill::Riposte => None, // used on parry
            Skill::Snare => Some(1),
            Skill::Stun => Some(1),
            Skill::Heal => None,
            Skill::Triage => None, // hot
            Skill::TriageTick => None,
            Skill::Throw => Some(1), // no damage stun, adds vulnerable
            Skill::Blast => None,
            Skill::Amplify => Some(1),
            Skill::Invert => Some(2),
            Skill::Decay => None, // dot
            Skill::DecayTick => None,
            Skill::Siphon => Some(1),
            Skill::SiphonTick => None,
            Skill::Curse => Some(1),
            Skill::Empower => Some(1),
            Skill::Shield => None,
            Skill::Silence => Some(1),
            Skill::Purify => None,
            Skill::Purge => None,
            Skill::Banish => Some(1),
            Skill::Hex => Some(1),
            Skill::Haste => None,
            Skill::Slow => None,
            Skill::Reflect => Some(2),
            Skill::Recharge => Some(2),
            Skill::Ruin => Some(3),
            Skill::Slay => None,
            Skill::Strangle => Some(2),
            Skill::StrangleTick => None,
            Skill::Clutch => Some(2),
            Skill::Taunt => Some(1),
            Skill::Injure => Some(2),

            Skill::Corrupt => Some(1),
            Skill::CorruptionTick => None,

            Skill::Hostility => Some(1),

            // -----------------
            // Test
            // -----------------
            Skill::TestTouch => None,
            Skill::TestStun => None,
            Skill::TestBlock => None,
            Skill::TestSiphon => None,
            Skill::TestParry => None,
        }
    }

    pub fn category(&self) -> Category {
        match self {
            Skill::Attack => Category::Red,
            Skill::Strike => Category::Red,
            Skill::Injure => Category::Red,
            Skill::Strangle => Category::Red,
            Skill::StrangleTick => Category::Red,
            Skill::Block => Category::Red, // reduce damage
            Skill::Parry => Category::Red, // avoid all damage
            Skill::Riposte => Category::Red, // avoid all damage
            Skill::Snare => Category::Red,
            Skill::Clutch => Category::Red,
            Skill::Stun => Category::Red,
            Skill::Slay => Category::Red,
            Skill::Taunt => Category::Red,

            Skill::Heal => Category::Green,
            Skill::Triage => Category::Green, // hot
            Skill::TriageTick => Category::GreenTick, // hot
            Skill::Throw => Category::Green,
            Skill::Empower => Category::Green,
            Skill::Shield => Category::Green,
            Skill::Purify => Category::Green,
            Skill::Recharge => Category::Green,
            Skill::Reflect => Category::Green,
            Skill::Haste => Category::Green,
            Skill::Invert => Category::Green,

            Skill::Blast => Category::Blue,
            Skill::Amplify => Category::Blue,
            Skill::Decay => Category::Blue, // dot
            Skill::DecayTick => Category::BlueTick, // hot
            Skill::Siphon => Category::Blue,
            Skill::SiphonTick => Category::BlueTick, // hot
            Skill::Curse => Category::Blue,
            Skill::Silence => Category::Blue,
            Skill::Purge => Category::Blue,
            Skill::Banish => Category::Blue,
            Skill::Hex => Category::Blue,
            Skill::Slow => Category::Blue,
            Skill::Ruin => Category::Blue,
            Skill::Hostility => Category::Blue,
            Skill::Corrupt => Category::Blue,
            Skill::CorruptionTick => Category::Blue,

            // -----------------
            // Test
            // -----------------
            Skill::TestTouch => Category::Red,
            Skill::TestStun => Category::Red,
            Skill::TestParry => Category::Red,
            Skill::TestBlock => Category::Red,
            Skill::TestSiphon => Category::Blue,
        }
    }

    pub fn ko_castable(&self) -> bool {
        match self {
            Skill::TriageTick => true,
            Skill::DecayTick => true,
            Skill::SiphonTick => true,
            Skill::CorruptionTick => true,
            _ => false,
        }
    }

    pub fn speed(&self) -> u8 {
        match self {
            // -----------------
            // Test
            // -----------------
            Skill::TestTouch => 10,
            Skill::TestStun => 5,
            Skill::TestBlock => 10,
            Skill::TestParry => 10,
            Skill::TestSiphon => 10,

            Skill::Strike => u8::max_value(),

            Skill::SiphonTick => Var::from(Skill::Siphon).speed(),
            Skill::DecayTick => Var::from(Skill::Decay).speed(),
            Skill::TriageTick => Var::from(Skill::Triage).speed(),
            Skill::StrangleTick => Var::from(Skill::Strangle).speed(),
            Skill::CorruptionTick => Var::from(Skill::Corrupt).speed(),

            _ => Var::from(*self).speed(),
        }
    }

    pub fn aoe(&self) -> bool {
        match self {
            Skill::Ruin => true,
            _ => false,
        }
    }

    pub fn self_targeting(&self) -> bool {
        match self {
            Skill::Block => true,
            Skill::Parry => true,
            Skill::Clutch => true,
            Skill::Taunt => true,
            Skill::Corrupt => true,

            Skill::TestBlock => true,
            Skill::TestParry => true,
            _ => false,
        }
    }

    pub fn defensive(&self) -> bool {
        match self {
            Skill::Heal |
            Skill::Triage |
            Skill::Empower |
            Skill::Purify |
            Skill::Parry |
            Skill::Block => true,
            _ => false,
        }
    }
}

fn touch(source: &mut Cryp, target: &mut Cryp, mut results: Resolutions) -> Resolutions {
    target.deal_red_damage(Skill::TestTouch, 0)
        .into_iter()
        .for_each(|e| results.push(Resolution::new(source, target).event(e)));

    return results;
}

fn attack(source: &mut Cryp, target: &mut Cryp, mut results: Resolutions) -> Resolutions {
    let amount = source.red_damage();
    target.deal_red_damage(Skill::Attack, amount)
        .into_iter()
        .for_each(|e| results.push(Resolution::new(source, target).event(e)));

    return results;
}

fn strike(source: &mut Cryp, target: &mut Cryp, mut results: Resolutions) -> Resolutions {
    let amount = source.red_damage();
    target.deal_red_damage(Skill::Strike, amount)
        .into_iter()
        .for_each(|e| results.push(Resolution::new(source, target).event(e)));

    return results;
}

fn injure(source: &mut Cryp, target: &mut Cryp, mut results: Resolutions) -> Resolutions {
    let amount = source.red_damage();
   target.deal_red_damage(Skill::Injure, amount)
        .into_iter()
        .for_each(|e| results.push(Resolution::new(source, target).event(e)));

    let effect = CrypEffect::new(Effect::Injured);
    results.push(Resolution::new(source, target).event(target.add_effect(Skill::Injure, effect)));
    return results;
}

fn stun(source: &mut Cryp, target: &mut Cryp, mut results: Resolutions) -> Resolutions {
    let effect = CrypEffect::new(Effect::Stun);
    results.push(Resolution::new(source, target).event(target.add_effect(Skill::Stun, effect)));
    return results;
}

fn clutch(source: &mut Cryp, target: &mut Cryp, mut results: Resolutions) -> Resolutions {
    let effect = CrypEffect::new(Effect::Clutch);
    results.push(Resolution::new(source, target).event(target.add_effect(Skill::Clutch, effect)));
    return results;
}

fn taunt(source: &mut Cryp, target: &mut Cryp, mut results: Resolutions) -> Resolutions {
    let effect = CrypEffect::new(Effect::Taunt);
    results.push(Resolution::new(source, target).event(target.add_effect(Skill::Taunt, effect)));
    return results;
}

fn throw(source: &mut Cryp, target: &mut Cryp, mut results: Resolutions) -> Resolutions {
    let stun = CrypEffect::new(Effect::Stun);
    let vulnerable = CrypEffect::new(Effect::Vulnerable);

    results.push(Resolution::new(source, target).event(target.add_effect(Skill::Throw, stun)));
    results.push(Resolution::new(source, target).event(target.add_effect(Skill::Throw, vulnerable)));

    return results;
}

fn strangle(source: &mut Cryp, target: &mut Cryp, mut results: Resolutions) -> Resolutions {
    let target_stun = CrypEffect::new(Effect::Strangle)
        .set_tick(Cast::new_tick(source, target, Skill::StrangleTick));

    let attacker_immunity = CrypEffect::new(Effect::Strangling);

    results.push(Resolution::new(source, target).event(target.add_effect(Skill::Strangle, target_stun)));
    results.push(Resolution::new(source, source).event(source.add_effect(Skill::Strangle, attacker_immunity)));
    return strangle_tick(source, target, results);
}

fn strangle_tick(source: &mut Cryp, target: &mut Cryp, mut results: Resolutions) -> Resolutions {
    let amount = source.red_damage();
    target.deal_red_damage(Skill::StrangleTick, amount)
        .into_iter()
        .for_each(|e| results.push(Resolution::new(source, target).event(e)));

    // remove immunity if target ko
    if target.is_ko() {
        let i = source.effects
            .iter()
            .position(|e| e.effect == Effect::Strangling)
            .expect("no strangling on cryp");
        source.effects.remove(i);
        results.push(Resolution::new(source, source).event(Event::Removal { effect: Effect::Strangling }));
    }

    return results;
}

fn block(source: &mut Cryp, target: &mut Cryp, mut results: Resolutions) -> Resolutions {
    let block = CrypEffect::new(Effect::Block);
    results.push(Resolution::new(source, target).event(target.add_effect(Skill::Block, block)));
    return results;
}

fn parry(source: &mut Cryp, target: &mut Cryp, mut results: Resolutions) -> Resolutions {
    let effect = CrypEffect::new(Effect::Parry);
    results.push(Resolution::new(source, target).event(target.add_effect(Skill::Parry, effect)));
    return results;
}

fn riposte(source: &mut Cryp, target: &mut Cryp, mut results: Resolutions) -> Resolutions {
    let amount = source.red_damage();
    target.deal_red_damage(Skill::Riposte, amount)
        .into_iter()
        .for_each(|e| results.push(Resolution::new(source, target).event(e)));

    return results;
}

fn snare(source: &mut Cryp, target: &mut Cryp, mut results: Resolutions) -> Resolutions {
    let snare = CrypEffect::new(Effect::Snare);
    results.push(Resolution::new(source, target).event(target.add_effect(Skill::Snare, snare)));
    return results;
}

fn empower(source: &mut Cryp, target: &mut Cryp, mut results: Resolutions) -> Resolutions {
    let empower = CrypEffect::new(Effect::Empower);
    results.push(Resolution::new(source, target).event(target.add_effect(Skill::Empower, empower)));
    return results;
}

fn heal(source: &mut Cryp, target: &mut Cryp, mut results: Resolutions) -> Resolutions {
    let amount = source.green_damage();
    target.deal_green_damage(Skill::Heal, amount)
        .into_iter()
        .for_each(|e| results.push(Resolution::new(source, target).event(e)));
    return results;
}

fn triage(source: &mut Cryp, target: &mut Cryp, mut results: Resolutions) -> Resolutions {
    let effect = CrypEffect::new(Effect::Triage)
        .set_tick(Cast::new_tick(source, target, Skill::TriageTick));

    results.push(Resolution::new(source, target).event(target.add_effect(Skill::Triage, effect)));
    return triage_tick(source, target, results);
}

fn triage_tick(source: &mut Cryp, target: &mut Cryp, mut results: Resolutions) -> Resolutions {
    let amount = source.green_damage().wrapping_div(2);
    target.deal_green_damage(Skill::TriageTick, amount)
        .into_iter()
        .for_each(|e| results.push(Resolution::new(source, target).event(e)));
    return results;
}

fn blast(source: &mut Cryp, target: &mut Cryp, mut results: Resolutions) -> Resolutions {
    let amount = source.blue_damage();
    target.deal_blue_damage(Skill::Blast, amount)
        .into_iter()
        .for_each(|e| results.push(Resolution::new(source, target).event(e)));
    return results;
}

fn amplify(source: &mut Cryp, target: &mut Cryp, mut results: Resolutions) -> Resolutions {
    let amplify = CrypEffect::new(Effect::Amplify);
    results.push(Resolution::new(source, target).event(target.add_effect(Skill::Amplify, amplify)));
    return results;;
}

fn haste(source: &mut Cryp, target: &mut Cryp, mut results: Resolutions) -> Resolutions {
    let effect = CrypEffect::new(Effect::Haste);
    results.push(Resolution::new(source, target).event(target.add_effect(Skill::Haste, effect)));
    return results;;
}

fn slow(source: &mut Cryp, target: &mut Cryp, mut results: Resolutions) -> Resolutions {
    let effect = CrypEffect::new(Effect::Slow);
    results.push(Resolution::new(source, target).event(target.add_effect(Skill::Slow, effect)));
    return results;;
}

fn decay(source: &mut Cryp, target: &mut Cryp, mut results: Resolutions) -> Resolutions {
    let decay = CrypEffect::new(Effect::Decay)
        .set_tick(Cast::new_tick(source, target, Skill::DecayTick));

    results.push(Resolution::new(source, target).event(target.add_effect(Skill::Decay, decay)));
    return decay_tick(source, target, results);
}

fn decay_tick(source: &mut Cryp, target: &mut Cryp, mut results: Resolutions) -> Resolutions {
    let amount = source.blue_damage() / 2;
    target.deal_blue_damage(Skill::DecayTick, amount)
        .into_iter()
        .for_each(|e| results.push(Resolution::new(source, target).event(e)));
    return results;
}

// corrupt is the buff effect
// when attacked it runs corruption and applies a debuff
fn corrupt(source: &mut Cryp, target: &mut Cryp, mut results: Resolutions) -> Resolutions {
    let effect = CrypEffect::new(Effect::Corrupt);
    results.push(Resolution::new(source, target).event(target.add_effect(Skill::Corrupt, effect)));
    return results;;
}

fn corruption(source: &mut Cryp, target: &mut Cryp, mut results: Resolutions) -> Resolutions {
    let effect = CrypEffect::new(Effect::Corruption)
        .set_tick(Cast::new_tick(source, target, Skill::CorruptionTick));

    results.push(Resolution::new(source, target).event(target.add_effect(Skill::Corrupt, effect)));
    return corruption_tick(source, target, results);
}

fn corruption_tick(source: &mut Cryp, target: &mut Cryp, mut results: Resolutions) -> Resolutions {
    let amount = source.blue_damage() / 2;
    target.deal_blue_damage(Skill::CorruptionTick, amount)
        .into_iter()
        .for_each(|e| results.push(Resolution::new(source, target).event(e)));
    return results;
}

fn ruin(source: &mut Cryp, target: &mut Cryp, mut results: Resolutions) -> Resolutions {
    let effect = CrypEffect::new(Effect::Ruin);
    results.push(Resolution::new(source, target).event(target.add_effect(Skill::Ruin, effect)));
    return results;;
}


fn hex(source: &mut Cryp, target: &mut Cryp, mut results: Resolutions) -> Resolutions {
    let hex = CrypEffect::new(Effect::Hex);
    results.push(Resolution::new(source, target).event(target.add_effect(Skill::Hex, hex)));
    return results;;
}

fn hostility(source: &mut Cryp, target: &mut Cryp, mut results: Resolutions) -> Resolutions {
    let effect = CrypEffect::new(Effect::Hostility);
    results.push(Resolution::new(source, target).event(target.add_effect(Skill::Hostility, effect)));
    return results;;
}

fn hatred(source: &mut Cryp, target: &mut Cryp, mut results: Resolutions, skill: Skill, amount: u64) -> Resolutions {
    let effect = CrypEffect::new(Effect::Hatred)
        .set_meta(EffectMeta::AddedDamage(amount));

    results.push(Resolution::new(source, target).event(target.add_effect(skill, effect)));
    return results;;
}

fn curse(source: &mut Cryp, target: &mut Cryp, mut results: Resolutions) -> Resolutions {
    let curse = CrypEffect::new(Effect::Curse);
    results.push(Resolution::new(source, target).event(target.add_effect(Skill::Curse, curse)));
    return results;;
}

fn invert(source: &mut Cryp, target: &mut Cryp, mut results: Resolutions) -> Resolutions {
    let effect = CrypEffect::new(Effect::Invert);
    results.push(Resolution::new(source, target).event(target.add_effect(Skill::Invert, effect)));
    return results;;
}

fn reflect(source: &mut Cryp, target: &mut Cryp, mut results: Resolutions) -> Resolutions {
    let effect = CrypEffect::new(Effect::Reflect);
    results.push(Resolution::new(source, target).event(target.add_effect(Skill::Reflect, effect)));
    return results;;
}

fn recharge(source: &mut Cryp, target: &mut Cryp, mut results: Resolutions) -> Resolutions {
    results.push(Resolution::new(source, target).event(target.recharge()));
    return results;
}

fn siphon(source: &mut Cryp, target: &mut Cryp, mut results: Resolutions) -> Resolutions {
    let siphon = CrypEffect::new(Effect::Siphon)
        .set_tick(Cast::new_tick(source, target, Skill::SiphonTick));

    results.push(Resolution::new(source, target).event(target.add_effect(Skill::Siphon, siphon)));
    return siphon_tick(source, target, results);
}

fn siphon_tick(source: &mut Cryp, target: &mut Cryp, mut results: Resolutions) -> Resolutions {
    let amount = source.blue_damage();
    let siphon_events = target.deal_blue_damage(Skill::SiphonTick, amount);

    for e in siphon_events {
        match e {
            Event::Damage { amount, mitigation: _, category: _, skill: _ } => {
                let dmg = source.deal_green_damage(Skill::Siphon, amount);
                for e in dmg {
                    results.push(Resolution::new(source, target).event(e));
                };
            },
            _ => (),
        }

        results.push(Resolution::new(source, target).event(e));
    }

    return results;
}

fn shield(source: &mut Cryp, target: &mut Cryp, mut results: Resolutions) -> Resolutions {
    let shield = CrypEffect::new(Effect::Shield);
    results.push(Resolution::new(source, target).event(target.add_effect(Skill::Shield, shield)));
    return results;
}

fn silence(source: &mut Cryp, target: &mut Cryp, mut results: Resolutions) -> Resolutions {
    let silence = CrypEffect::new(Effect::Silence);
    results.push(Resolution::new(source, target).event(target.add_effect(Skill::Silence, silence)));
    return results;
}

fn purge(source: &mut Cryp, target: &mut Cryp, mut results: Resolutions) -> Resolutions {
    for (i, ce) in target.effects.clone().iter_mut().enumerate() {
        if [Category::BlueBuff, Category::RedBuff].contains(&ce.effect.category()) {
            target.effects.remove(i);
            results.push(Resolution::new(source, target).event(Event::Removal { effect: ce.effect }));
        }
    }

    return results;
}

fn purify(source: &mut Cryp, target: &mut Cryp, mut results: Resolutions) -> Resolutions {
    for (i, ce) in target.effects.clone().iter_mut().enumerate() {
        if [Category::BlueDebuff, Category::RedDebuff].contains(&ce.effect.category()) {
            target.effects.remove(i);
            results.push(Resolution::new(source, target).event(Event::Removal { effect: ce.effect }));
        }
    }

    return results;
}

fn banish(source: &mut Cryp, target: &mut Cryp, mut results: Resolutions) -> Resolutions {
    let banish = CrypEffect::new(Effect::Banish);
    results.push(Resolution::new(source, target).event(target.add_effect(Skill::Banish, banish)));
    return results;
}

#[cfg(test)]
mod tests {
    use skill::*;

    #[test]
    fn heal_test() {
        let mut x = Cryp::new()
            .named(&"muji".to_string())
            .learn(Skill::Heal);

        let mut y = Cryp::new()
            .named(&"camel".to_string())
            .learn(Skill::Heal);

        x.deal_red_damage(Skill::Attack, 5);

        heal(&mut y, &mut x, vec![]);
    }

    #[test]
    fn decay_test() {
        let mut x = Cryp::new()
            .named(&"muji".to_string());

        let mut y = Cryp::new()
            .named(&"camel".to_string());

        decay(&mut x, &mut y, vec![]);

        assert!(y.effects.iter().any(|e| e.effect == Effect::Decay));

        y.reduce_effect_durations();
        let _decay = y.effects.iter().find(|e| e.effect == Effect::Decay);
        // assert!(y.hp() == y.hp().saturating_sub(decay.unwrap().tick.unwrap().amount));
    }

    #[test]
    fn block_test() {
        let mut x = Cryp::new()
            .named(&"muji".to_string());

        let mut y = Cryp::new()
            .named(&"camel".to_string());

        // ensure it doesn't have 0 pd
        x.red_damage.force(100);
        y.hp.force(500);

        block(&mut y.clone(), &mut y, vec![]);
        assert!(y.effects.iter().any(|e| e.effect == Effect::Block));

        let mut results = attack(&mut x, &mut y, vec![]);

        let Resolution { source: _, target: _, event } = results.remove(0);
        match event {
            Event::Damage { amount, mitigation: _, category: _, skill: _ } => assert_eq!(amount, 50),
            _ => panic!("not damage"),
        };
    }

    #[test]
    fn clutch_test() {
        let mut x = Cryp::new()
            .named(&"muji".to_string());

        let mut y = Cryp::new()
            .named(&"camel".to_string());

        x.red_damage.force(u64::max_value());

        clutch(&mut y.clone(), &mut y, vec![]);
        assert!(y.affected(Effect::Clutch));

        let mut results = attack(&mut x, &mut y, vec![]);
        assert!(y.hp() == 1);

        let Resolution { source: _, target: _, event } = results.remove(0);
        match event {
            Event::Damage { amount, mitigation: _, category: _, skill: _ } => assert_eq!(amount, 1023),
            _ => panic!("not damage"),
        };
    }

    #[test]
    fn injure_test() {
        let mut x = Cryp::new()
            .named(&"muji".to_string());

        let mut y = Cryp::new()
            .named(&"camel".to_string());

        resolve(Skill::Injure, &mut x, &mut y, vec![]);
        assert!(y.immune(Skill::Heal).is_some());
        // resolutions = heal(&mut y.clone(), &mut y, resolutions);
    }

    #[test]
    fn invert_test() {
        let mut x = Cryp::new()
            .named(&"muji".to_string());

        let mut y = Cryp::new()
            .named(&"camel".to_string());

        // give red shield but reduce to 0
        y.red_shield.force(64);
        y.red_shield.reduce(64);
        x.red_damage.force(256 + 64);

        invert(&mut y.clone(), &mut y, vec![]);
        assert!(y.affected(Effect::Invert));

        // heal should deal green damage
        heal(&mut x, &mut y, vec![]);
        assert!(y.hp() == 768);

        // attack should heal and recharge red shield
        let mut results = attack(&mut x, &mut y, vec![]);
        assert!(y.hp() == 1024);

        match results.remove(0).event {
            Event::Inversion { skill } => assert_eq!(skill, Skill::Attack),
            _ => panic!("not inversion"),
        };

        match results.remove(0).event {
            Event::Healing { skill: _, overhealing: _, amount } => assert_eq!(amount, 256),
            _ => panic!("not healing from inversion"),
        };

        match results.remove(0).event {
            Event::Recharge { skill: _, red, blue: _ } => assert_eq!(red, 64),
            _ => panic!("not recharge from inversion"),
        };
    }

    #[test]
    fn reflect_test() {
        let mut x = Cryp::new()
            .named(&"muji".to_string());

        let mut y = Cryp::new()
            .named(&"camel".to_string());

        reflect(&mut y.clone(), &mut y, vec![]);
        assert!(y.affected(Effect::Reflect));

        let mut results = vec![];
        results = resolve(Skill::Attack, &mut x, &mut y, results);

        assert!(x.hp() == 768);

        let Resolution { source: _, target: _, event } = results.remove(0);
        match event {
            Event::Reflection { skill } => assert_eq!(skill, Skill::Attack),
            _ => panic!("not reflection"),
        };

        let Resolution { source: _, target: _, event } = results.remove(0);
        match event {
            Event::Damage { amount, mitigation: _, category: _, skill: _ } => assert_eq!(amount, 256),
            _ => panic!("not damage"),
        };
    }

    #[test]
    fn siphon_test() {
        let mut x = Cryp::new()
            .named(&"muji".to_string());

        let mut y = Cryp::new()
            .named(&"camel".to_string());

        x.hp.reduce(512);

        let mut results = resolve(Skill::Siphon, &mut x, &mut y, vec![]);

        assert!(y.affected(Effect::Siphon));
        assert!(x.hp() == 768);

        let Resolution { source: _, target: _, event } = results.remove(0);
        match event {
            Event::Effect { effect, skill: _, duration: _ } => assert_eq!(effect, Effect::Siphon),
            _ => panic!("not siphon"),
        };

        let Resolution { source: _, target: _, event } = results.remove(0);
        match event {
            Event::Healing { amount, skill: _, overhealing: _ } => assert_eq!(amount, 256),
            _ => panic!("not healing"),
        };

        let Resolution { source: _, target: _, event } = results.remove(0);
        match event {
            Event::Damage { amount, skill: _, mitigation: _, category: _} => assert_eq!(amount, 256),
            _ => panic!("not damage siphon"),
        };

    }


    #[test]
    fn corrupt_test() {
        let mut x = Cryp::new()
            .named(&"muji".to_string());

        let mut y = Cryp::new()
            .named(&"camel".to_string());

        corrupt(&mut y.clone(), &mut y, vec![]);
        assert!(y.affected(Effect::Corrupt));

        resolve(Skill::Attack, &mut x, &mut y, vec![]);

        assert!(x.affected(Effect::Corruption));
    }

    #[test]
    fn hatred_test() {
        let mut x = Cryp::new()
            .named(&"muji".to_string());

        let mut y = Cryp::new()
            .named(&"camel".to_string());

        hostility(&mut y.clone(), &mut y, vec![]);
        assert!(y.affected(Effect::Hostility));

        resolve(Skill::Attack, &mut x, &mut y, vec![]);

        assert!(x.affected(Effect::Hatred));
    }

    #[test]
    fn triage_test() {
        let mut x = Cryp::new()
            .named(&"muji".to_string());

        let mut y = Cryp::new()
            .named(&"pretaliation".to_string());

        // ensure it doesn't have 0 sd
        x.blue_damage.force(50);

        // remove all mitigation
        y.red_shield.force(0);
        y.blue_shield.force(0);

        y.deal_red_damage(Skill::Attack, 5);
        let prev_hp = y.hp();

        triage(&mut x, &mut y, vec![]);

        assert!(y.effects.iter().any(|e| e.effect == Effect::Triage));
        assert!(y.hp() > prev_hp);
    }

    #[test]
    fn recharge_test() {
        let mut x = Cryp::new()
            .named(&"muji".to_string());

        let mut y = Cryp::new()
            .named(&"pretaliation".to_string());

        y.red_shield.force(50);
        y.blue_shield.force(50);

        y.deal_red_damage(Skill::Attack, 5);
        y.deal_blue_damage(Skill::Blast, 5);

        let mut results = recharge(&mut x, &mut y, vec![]);

        let Resolution { source: _, target: _, event } = results.remove(0);
        match event {
            Event::Recharge { red, blue, skill: _ } => {
                assert!(red == 5);
                assert!(blue == 5);
            }
            _ => panic!("result was not recharge"),
        }
    }


    #[test]
    fn silence_test() {
        let mut x = Cryp::new()
            .named(&"muji".to_string());

        silence(&mut x.clone(), &mut x, vec![]);
        assert!(x.effects.iter().any(|e| e.effect == Effect::Silence));
        assert!(x.disabled(Skill::Silence).is_some());
    }

    #[test]
    fn amplify_test() {
        let mut x = Cryp::new()
            .named(&"muji".to_string());

        x.blue_damage.force(50);

        amplify(&mut x.clone(), &mut x, vec![]);
        assert!(x.effects.iter().any(|e| e.effect == Effect::Amplify));
        assert_eq!(x.blue_damage(), 100);
    }

    #[test]
    fn purify_test() {
        let mut x = Cryp::new()
            .named(&"muji".to_string());

        decay(&mut x.clone(), &mut x, vec![]);
        assert!(x.effects.iter().any(|e| e.effect == Effect::Decay));

        purify(&mut x.clone(), &mut x, vec![]);
        assert!(!x.effects.iter().any(|e| e.effect == Effect::Decay));
    }
}






// pub enum Skill {
//     Attack,

//     // -----------------
//     // Nature
//     // -----------------
//     Block, // reduce damage
//     Parry, // avoid all damage
//     Snare,

//     Paralyse,

//     Strangle, // physical dot and disable
//     Strike,
//     Stun,
//     // Evade, // actively evade


//     // -----------------
//     // Technology
//     // -----------------
//     Replicate,
//     Swarm,
//     Orbit,
//     Repair,
//     Scan, // track?

//     // -----------------
//     // Nonviolence
//     // -----------------
//     Heal,
//     Triage, // hot
//     TriageTick,
//     Throw, // no damage stun, adds vulnerable
//     Charm,
//     Calm,
//     Rez,

//     // Sleep,
//     // Nightmare,

//     // -------------------
//     // Destruction
//     // -------------------
//     Blast,
//     Amplify,
//     Decay, // dot
//     DecayTick, // dot
//     Siphon,
//     SiphonTick,
//     Curse,
//     Corrupt, // aoe dot
//     Ruin, // aoe

//     // -----------------
//     // Purity
//     // -----------------
//     Empower,
//     Slay,
//     Shield,
//     Silence,
//     Inquiry,
//     Purify,
//     Purge,
//     // Precision,

//     // -----------------
//     // Chaos
//     // -----------------
//     Banish,
//     Hex,
//     Fear,
//     Taunt,
//     Pause, // speed slow
//     Haste,
//     Slow,

//     // used by tests, no cd, no damage
//     TestTouch,
//     TestStun,
//     TestBlock,
//     TestParry,
//     TestSiphon,
// }
