from typing import Any, List, Literal

from ....utils.class_registry import register_class

from ...event import (
    ChooseCharactorEventArguments, CreateObjectEventArguments, 
    RoundPrepareEventArguments, SkillEndEventArguments, 
    SwitchCharactorEventArguments
)

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_charactor_use_damage_skill(
            self.position, match, None
        ):
            # not this charactor 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 charactor attack related

    def _attack(
        self, match: Any, usage_type: Literal['SWITCH', 'EXTRA'], 
        damage: int = 1
    ) -> List[MakeDamageAction]:
        """
        attack enemy active charactor
        """
        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_charactor = match.player_tables[
            1 - self.position.player_idx].get_active_charactor()
        return [MakeDamageAction(
            damage_value_list = [
                DamageValue(
                    position = self.position,
                    damage_type = DamageType.DAMAGE,
                    target_position = active_charactor.position,
                    damage = damage,
                    damage_elemental_type = DamageElementalType.HYDRO,
                    cost = Cost()
                )
            ]
        )]

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

    def event_handler_CHOOSE_CHARACTOR(
        self, event: ChooseCharactorEventArguments, match: Any
    ) -> List[MakeDamageAction]:
        """
        If self choose charactor (when ally defeated), perform attack
        """
        if event.action.player_idx != self.position.player_idx:
            # not self switch charactor
            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 charactor_is_using_right_weapon(
        self, match: Any, position: ObjectPosition
    ) -> bool:
        """
        If position charactor using effected three weapon, return True.
        """
        charactor = match.player_tables[position.player_idx].charactors[
            position.charactor_idx]
        return charactor.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.charactor_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_charactor_use_damage_skill(
            self.position, match, SkillType.NORMAL_ATTACK
        ):
            # not corresponding charactor, 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 charactor 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 []
        charactors = match.player_tables[self.position.player_idx].charactors
        candace_talent = False
        for charactor in charactors:
            if charactor.name == 'Candace' and charactor.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):
    """
    TODO:
    Currently after a dendro core is generated, it removes dendro core and
    create bountiful core. But it will clear existing dendro core. Need to be
    fixed, as dendro core can be triggerd by Abyss Summon.
    """
    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 []
        # is dendro core, remove it and create bountiful core
        dendro_core = match.player_tables[
            self.position.player_idx].team_status[event.create_idx]
        assert dendro_core.name == 'Dendro Core'
        return [
            RemoveObjectAction(
                object_position = dendro_core.position,
            ),
            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
)
