from typing import Any, List, Literal

from ....utils.class_registry import register_class

from ...event import (
    ChooseCharacterEventArguments,
    CreateObjectEventArguments,
    RoundPrepareEventArguments,
    SkillEndEventArguments,
    SwitchCharacterEventArguments,
)

from ...struct import Cost, ObjectPosition

from ...action import Actions, CreateObjectAction, MakeDamageAction, RemoveObjectAction

from ...consts import (
    DamageElementalType,
    DamageType,
    IconType,
    ObjectPositionType,
    SkillType,
    WeaponType,
)

from ...modifiable_values import (
    DamageElementEnhanceValue,
    DamageIncreaseValue,
    DamageMultiplyValue,
    DamageValue,
)
from .base import (
    DefendTeamStatus,
    ElementalInfusionTeamStatus,
    ExtraAttackTeamStatus,
    RoundTeamStatus,
    TeamStatusBase,
    UsageTeamStatus,
)


class IllusoryBubble_3_3(UsageTeamStatus):
    """
    Team status generated by Mona.
    """

    name: Literal["Illusory Bubble"] = "Illusory Bubble"
    version: Literal["3.3"] = "3.3"
    usage: int = 1
    max_usage: int = 1
    icon_type: Literal[IconType.OTHERS] = IconType.OTHERS

    def value_modifier_DAMAGE_MULTIPLY(
        self, value: DamageMultiplyValue, match: Any, mode: Literal["TEST", "REAL"]
    ) -> DamageMultiplyValue:
        """
        Double damage when skill damage made.
        """
        if not value.is_corresponding_character_use_damage_skill(
            self.position, match, None
        ):
            # not this character use skill, not modify
            return value
        if self.usage > 0:
            value.damage *= 2
            assert mode == "REAL"
            self.usage -= 1
        return value


class RainbowBladework_3_6(UsageTeamStatus, ExtraAttackTeamStatus):
    name: Literal["Rainbow Bladework"] = "Rainbow Bladework"
    version: Literal["3.6"] = "3.6"
    usage: int = 3
    max_usage: int = 3
    icon_type: Literal[IconType.OTHERS] = IconType.OTHERS

    trigger_skill_type: SkillType | None = SkillType.NORMAL_ATTACK
    damage: int = 1
    damage_elemental_type: DamageElementalType = DamageElementalType.HYDRO
    decrease_usage: bool = True


class RainSword_4_2(DefendTeamStatus):
    name: Literal["Rain Sword"] = "Rain Sword"
    desc: Literal["", "talent"] = ""
    version: Literal["4.2"] = "4.2"
    usage: int = 2
    max_usage: int = 2
    min_damage_to_trigger: int = 2
    max_in_one_time: int = 1

    def __init__(self, *argv, **kwargs):
        super().__init__(*argv, **kwargs)
        if self.min_damage_to_trigger == 2:
            self.desc = "talent"

    def renew(self, new_status: "RainSword_4_2") -> None:
        super().renew(new_status)
        if new_status.min_damage_to_trigger == 2:
            self.desc = "talent"


class RainbowBladework_3_3(UsageTeamStatus, ExtraAttackTeamStatus):
    name: Literal["Rainbow Bladework"] = "Rainbow Bladework"
    version: Literal["3.3"]
    usage: int = 3
    max_usage: int = 3
    icon_type: Literal[IconType.OTHERS] = IconType.OTHERS

    trigger_skill_type: SkillType | None = SkillType.NORMAL_ATTACK
    damage: int = 2
    damage_elemental_type: DamageElementalType = DamageElementalType.HYDRO
    decrease_usage: bool = True


class RainSword_3_3(RainSword_4_2):
    version: Literal["3.3"]
    min_damage_to_trigger: int = 3


class PrayerOfTheCrimsonCrown_3_8(
    ElementalInfusionTeamStatus, RoundTeamStatus, ExtraAttackTeamStatus
):
    name: Literal["Prayer of the Crimson Crown"] = "Prayer of the Crimson Crown"
    version: Literal["3.8"] = "3.8"
    usage: int = 2
    max_usage: int = 2
    icon_type: Literal[IconType.OTHERS] = IconType.OTHERS

    switch_damage_usage: int = 1
    switch_damage_max_usage: int = 1
    extra_damage_usage: int = 1
    extra_damage_max_usage: int = 1

    # enhance args
    infused_elemental_type: DamageElementalType = DamageElementalType.HYDRO

    # extra attack args
    trigger_skill_type: SkillType | None = SkillType.NORMAL_ATTACK
    damage: int = 1
    damage_elemental_type: DamageElementalType = DamageElementalType.HYDRO
    decrease_usage: bool = False

    # switch character attack related

    def _attack(
        self, match: Any, usage_type: Literal["SWITCH", "EXTRA"], damage: int = 1
    ) -> List[MakeDamageAction]:
        """
        attack enemy active character
        """
        if usage_type == "SWITCH":
            if self.switch_damage_usage <= 0:
                # no usage
                return []
            self.switch_damage_usage -= 1
        else:
            assert usage_type == "EXTRA"
            if self.extra_damage_usage <= 0:
                # no usage
                return []
            self.extra_damage_usage -= 1
        active_character = match.player_tables[
            1 - self.position.player_idx
        ].get_active_character()
        return [
            MakeDamageAction(
                damage_value_list=[
                    DamageValue(
                        position=self.position,
                        damage_type=DamageType.DAMAGE,
                        target_position=active_character.position,
                        damage=damage,
                        damage_elemental_type=DamageElementalType.HYDRO,
                        cost=Cost(),
                    )
                ]
            )
        ]

    def event_handler_SWITCH_CHARACTER(
        self, event: SwitchCharacterEventArguments, match: Any
    ) -> List[MakeDamageAction]:
        """
        If self switch character, perform attack
        """
        if event.action.player_idx != self.position.player_idx:
            # not self switch character
            return []
        return self._attack(match, "SWITCH")

    def event_handler_CHOOSE_CHARACTER(
        self, event: ChooseCharacterEventArguments, match: Any
    ) -> List[MakeDamageAction]:
        """
        If self choose character (when ally defeated), perform attack
        """
        if event.action.player_idx != self.position.player_idx:
            # not self switch character
            return []
        return self._attack(match, "SWITCH")

    def event_handler_ROUND_PREPARE(
        self, event: RoundPrepareEventArguments, match: Any
    ) -> List[Actions]:
        """
        reset usage
        """
        self.switch_damage_usage = self.switch_damage_max_usage
        self.extra_damage_usage = self.extra_damage_max_usage
        return super().event_handler_ROUND_PREPARE(event, match)

    # renew

    def renew(self, new_status: "PrayerOfTheCrimsonCrown_3_8") -> None:
        self.switch_damage_max_usage = max(
            self.switch_damage_max_usage, new_status.switch_damage_max_usage
        )
        self.switch_damage_usage = max(
            self.switch_damage_usage, new_status.switch_damage_usage
        )
        self.extra_damage_max_usage = max(
            self.switch_damage_max_usage, new_status.extra_damage_max_usage
        )
        self.extra_damage_usage = max(
            self.switch_damage_usage, new_status.extra_damage_usage
        )
        return super().renew(new_status)

    # enhance and add damage

    def character_is_using_right_weapon(
        self, match: Any, position: ObjectPosition
    ) -> bool:
        """
        If position character using effected three weapon, return True.
        """
        character = match.player_tables[position.player_idx].characters[
            position.character_idx
        ]
        return character.weapon_type in [
            WeaponType.SWORD,
            WeaponType.CLAYMORE,
            WeaponType.POLEARM,
        ]

    def value_modifier_DAMAGE_ELEMENT_ENHANCE(
        self,
        value: DamageElementEnhanceValue,
        match: Any,
        mode: Literal["TEST", "REAL"],
    ) -> DamageElementEnhanceValue:
        """
        If weapon type not right, do not enhance.
        """
        if not self.character_is_using_right_weapon(match, value.position):
            return value
        return super().value_modifier_DAMAGE_ELEMENT_ENHANCE(value, match, mode)

    def value_modifier_DAMAGE_INCREASE(
        self, value: DamageIncreaseValue, match: Any, mode: Literal["TEST", "REAL"]
    ) -> DamageIncreaseValue:
        """
        +1 our normal attack DMG.
        """
        if not value.is_corresponding_character_use_damage_skill(
            self.position, match, SkillType.NORMAL_ATTACK
        ):
            # not corresponding character, do nothing
            return value
        # add damage
        assert mode == "REAL"
        value.damage += 1
        return value

    # extra attack

    def event_handler_SKILL_END(
        self, event: SkillEndEventArguments, match: Any
    ) -> List[MakeDamageAction]:
        """
        If candace have talent, and our character use normal attack, then
        1 hydro damage.
        """
        if event.action.position.player_idx != self.position.player_idx:
            # not our player
            return []
        if event.action.skill_type != SkillType.NORMAL_ATTACK:
            # not normal attack
            return []
        characters = match.player_tables[self.position.player_idx].characters
        candace_talent = False
        for character in characters:
            if character.name == "Candace" and character.talent is not None:
                candace_talent = True
                break
        if not candace_talent:
            # no candace with talent
            return []
        # make damage
        return self._attack(match, "EXTRA")


class GoldenChalicesBounty_4_2(TeamStatusBase):
    name: Literal["Golden Chalice's Bounty"] = "Golden Chalice's Bounty"
    version: Literal["4.2"] = "4.2"
    usage: int = 0
    max_usage: int = 0
    icon_type: Literal[IconType.OTHERS] = IconType.OTHERS

    def event_handler_CREATE_OBJECT(
        self, event: CreateObjectEventArguments, match: Any
    ) -> List[RemoveObjectAction | CreateObjectAction]:
        """
        If our create dendro core, remove the dendro core and create
        Bountiful Core.
        """
        if event.action.object_position.player_idx != self.position.player_idx:
            # not our create
            return []
        if (
            event.action.object_position.area != ObjectPositionType.TEAM_STATUS
            or event.action.object_name != "Dendro Core"
        ):
            # not teams status dendro core
            return []
        ret: List[RemoveObjectAction | CreateObjectAction] = []
        if event.create_result == "NEW":
            # is dendro core and newly create, remove it and create bountiful
            # core
            dendro_core = None
            for s in match.player_tables[self.position.player_idx].team_status:
                if s.name == "Dendro Core":
                    dendro_core = s
                    break
            else:
                raise AssertionError("Dendro Core not found")
            ret.append(
                RemoveObjectAction(
                    object_position=dendro_core.position,
                )
            )
        return ret + [
            CreateObjectAction(
                object_name="Bountiful Core",
                object_position=ObjectPosition(
                    player_idx=self.position.player_idx,
                    area=ObjectPositionType.SUMMON,
                    id=0,
                ),
                object_arguments={},
            )
        ]


register_class(
    IllusoryBubble_3_3
    | RainbowBladework_3_6
    | RainSword_4_2
    | RainSword_3_3
    | PrayerOfTheCrimsonCrown_3_8
    | GoldenChalicesBounty_4_2
    | RainbowBladework_3_3
)
