import sys
import os
sys.path.insert(0, os.path.abspath("."))
from server.world.combat_engine import CombatEngine, CombatActor, DamageEventResult
from server.world.martial_matrix import FiveElements
from server.world.level_progression_service import LevelProgressionService

d = list(CombatActor.__init__.__defaults__)
d[6] = -1000
CombatActor.__init__.__defaults__ = tuple(d)

orig_check = CombatEngine._check_special_damage_cases
def proposed_check_special_damage_cases(
    self,
    attacker,
    defender,
    attacker_id: int,
    defender_id: int,
    raw_damage: float,
    damage_element: FiveElements,
    current_timestamp_ms: int,
):
    if not attacker or not defender:
        return DamageEventResult(
            attacker_id=attacker_id,
            defender_id=defender_id,
            raw_damage=raw_damage,
            final_damage=0.0,
            is_critical=False,
            is_evaded=False,
            element=damage_element,
            is_fatal=False,
        )
    if defender.last_evasion_timestamp_ms >= 0:
        elapsed = current_timestamp_ms - defender.last_evasion_timestamp_ms
        if 0 <= elapsed <= defender.evasion_iframe_duration_ms:
            return DamageEventResult(
                attacker_id=attacker_id,
                defender_id=defender_id,
                raw_damage=raw_damage,
                final_damage=0.0,
                is_critical=False,
                is_evaded=True,
                element=damage_element,
                is_fatal=False,
            )
    return None

def proposed_calculate_damage(
    self,
    attacker_id: int,
    defender_id: int,
    raw_damage: float,
    damage_element: FiveElements,
    current_timestamp_ms: int,
    force_crit=None,
) -> DamageEventResult:
    attacker = self.actors.get(attacker_id)
    defender = self.actors.get(defender_id)

    special = self._check_special_damage_cases(
        attacker, defender, attacker_id, defender_id, raw_damage, damage_element, current_timestamp_ms
    )
    if special is not None:
        return special
    assert attacker is not None and defender is not None

    was_alive = (defender.current_hp > 0.0)

    final_damage, is_crit = self._calculate_mitigated_damage(
        attacker, defender, raw_damage, damage_element, force_crit
    )
    new_hp = max(0.0, defender.current_hp - final_damage)
    defender.current_hp = new_hp
    is_fatal = was_alive and (new_hp <= 0.0)

    result = DamageEventResult(
        attacker_id=attacker_id,
        defender_id=defender_id,
        raw_damage=raw_damage,
        final_damage=round(final_damage, 2),
        is_critical=is_crit,
        is_evaded=False,
        element=damage_element,
        is_fatal=is_fatal,
    )

    if is_fatal and self.on_fatal_damage is not None:
        self.on_fatal_damage(result, attacker, defender)

    return result

CombatEngine._check_special_damage_cases = proposed_check_special_damage_cases
CombatEngine.calculate_damage = proposed_calculate_damage

# 1. Test Phantom Evasion Fix
e1 = CombatEngine()
a1 = CombatActor(1, "M", FiveElements.KIM, base_attack=100)
d1 = CombatActor(2, "P", FiveElements.KIM, current_hp=500)
e1.register_actor(a1)
e1.register_actor(d1)
res_ev1 = e1.calculate_damage(1, 2, 100, FiveElements.KIM, current_timestamp_ms=100)
assert res_ev1.is_evaded is False, "Phantom evasion failed at 100ms!"
res_ev0 = e1.calculate_damage(1, 2, 100, FiveElements.KIM, current_timestamp_ms=0)
assert res_ev0.is_evaded is False, "Phantom evasion failed at 0ms!"

# 2. Test Legitimate Evasion Still Works
assert e1.trigger_phantom_evasion(2, timestamp_ms=500) is True
res_ev_dodge = e1.calculate_damage(1, 2, 100, FiveElements.KIM, current_timestamp_ms=600)
assert res_ev_dodge.is_evaded is True, "Legitimate evasion failed to activate!"
assert res_ev_dodge.final_damage == 0.0

# 3. Test Corpse Multi-Hit Overkill Fix (Player Death Penalty)
s = LevelProgressionService()
s.set_player_state("p_corpse", level=80, current_exp=10000)
e2 = CombatEngine()
e2.attach_progression_service(s)
a2 = CombatActor(10, "Boss", FiveElements.KIM, base_attack=1000)
d2 = CombatActor(20, "Player", FiveElements.KIM, current_hp=500, is_player=True, player_id="p_corpse")
e2.register_actor(a2)
e2.register_actor(d2)

# Hit 1: Lethal blow
res1 = e2.calculate_damage(10, 20, 1000, FiveElements.KIM, 1000)
assert res1.is_fatal is True
assert s.get_level_info("p_corpse").deaths_count == 1

# Hit 2: Corpse strike
res2 = e2.calculate_damage(10, 20, 1000, FiveElements.KIM, 1020)
assert res2.is_fatal is False
assert s.get_level_info("p_corpse").deaths_count == 1, "Duplicate death penalty applied on corpse!"

# Hit 3: Another corpse strike
res3 = e2.calculate_damage(10, 20, 1000, FiveElements.KIM, 1040)
assert res3.is_fatal is False
assert s.get_level_info("p_corpse").deaths_count == 1, "Duplicate death penalty applied on corpse!"

# 4. Test Corpse Multi-Hit Overkill Fix (Monster EXP Exploit)
s_mob = LevelProgressionService()
s_mob.set_player_state("p_hero", level=50, current_exp=0)
e3 = CombatEngine()
e3.attach_progression_service(s_mob)
hero = CombatActor(30, "Hero", FiveElements.HOA, base_attack=500, is_player=True, player_id="p_hero")
goblin = CombatActor(40, "Goblin", FiveElements.KIM, current_hp=100, is_player=False, level=50)
e3.register_actor(hero)
e3.register_actor(goblin)

res_mob1 = e3.calculate_damage(30, 40, 500, FiveElements.HOA, 2000)
assert res_mob1.is_fatal is True
exp_kill = s_mob.get_level_info("p_hero").current_exp
assert exp_kill > 0, "No EXP awarded on initial kill!"

res_mob2 = e3.calculate_damage(30, 40, 500, FiveElements.HOA, 2020)
assert res_mob2.is_fatal is False
exp_corpse = s_mob.get_level_info("p_hero").current_exp
assert exp_corpse == exp_kill, f"Corpse hit awarded duplicate EXP! {exp_corpse} vs {exp_kill}"

print("ALL VERIFICATION CHECKS PASSED PERFECTLY!")
