from typing import Any, List, Literal, Tuple

from ....utils.class_registry import register_class

from ...summon.base import AttackerSummonBase

from ...modifiable_values import CostValue, DamageValue
from ...event import RoundPrepareEventArguments

from ...action import Actions, CreateObjectAction, MakeDamageAction
from ...struct import Cost, ObjectPosition

from ...consts import (
    ELEMENT_TO_DAMAGE_TYPE,
    CostLabels,
    DamageElementalType,
    DamageType,
    DieColor,
    ElementType,
    FactionType,
    ObjectPositionType,
    PlayerActionLabels,
    WeaponType,
)
from ..character_base import (
    CreateStatusPassiveSkill,
    ElementalBurstBase,
    ElementalNormalAttackBase,
    ElementalSkillBase,
    CharacterBase,
    TalentBase,
)


# Summons


class ChainsOfWardingThunder_3_7(AttackerSummonBase):
    name: Literal["Chains of Warding Thunder"] = "Chains of Warding Thunder"
    version: Literal["3.7"] = "3.7"
    usage: int = 2
    max_usage: int = 2
    damage_elemental_type: DamageElementalType = DamageElementalType.ELECTRO
    damage: int = 1

    increase_cost_valid: bool = True

    def event_handler_ROUND_PREPARE(
        self, event: RoundPrepareEventArguments, match: Any
    ) -> List[Actions]:
        """
        When reaching prepare, reset usage
        """
        self.increase_cost_valid = True
        return []

    def value_modifier_COST(
        self, value: CostValue, match: Any, mode: Literal["TEST", "REAL"]
    ) -> CostValue:
        """
        When increase_cost_valid, enemy perform character switch will increase
        1 cost.
        """
        if not self.increase_cost_valid:
            # out of usage, not modify
            return value
        if value.cost.label & CostLabels.SWITCH_CHARACTER == 0:
            # not switch character, not modify
            return value
        if value.position.player_idx == self.position.player_idx:
            # self switch, not modify
            return value
        # add 1 cost
        value.cost.any_dice_number += 1
        if mode == "REAL":
            self.increase_cost_valid = False
        return value


# Skills


class RockPaperScissorsCombo(ElementalSkillBase):
    name: Literal["Rock-Paper-Scissors Combo"] = "Rock-Paper-Scissors Combo"
    damage: int = 2
    damage_type: DamageElementalType = DamageElementalType.ELECTRO
    cost: Cost = Cost(elemental_dice_color=DieColor.ELECTRO, elemental_dice_number=5)

    def get_actions(self, match: Any) -> List[Actions]:
        """
        Attack and create prepare skill
        """
        return super().get_actions(match) + [
            self.create_character_status("Rock-Paper-Scissors Combo: Scissors")
        ]


class RockPaperScissorsComboScissors(ElementalSkillBase):
    name: Literal[
        "Rock-Paper-Scissors Combo: Scissors"
    ] = "Rock-Paper-Scissors Combo: Scissors"
    damage: int = 2
    damage_type: DamageElementalType = DamageElementalType.ELECTRO
    cost: Cost = Cost()

    def is_valid(self, match: Any) -> bool:
        """
        Cannot use by player directly
        """
        return False

    def get_actions(self, match: Any) -> List[Actions]:
        """
        Attack and create prepare skill
        """
        return [
            self.attack_opposite_active(
                match,
                self.damage,
                self.damage_type,
            ),
            self.create_character_status("Rock-Paper-Scissors Combo: Paper"),
        ]


class RockPaperScissorsComboPaper(ElementalSkillBase):
    name: Literal[
        "Rock-Paper-Scissors Combo: Paper"
    ] = "Rock-Paper-Scissors Combo: Paper"
    damage_type: DamageElementalType = DamageElementalType.ELECTRO
    cost: Cost = Cost()

    def is_valid(self, match: Any) -> bool:
        """
        Cannot use by player directly
        """
        return False

    def get_actions(self, match: Any) -> List[Actions]:
        """
        Do not charge.
        """
        return [
            self.attack_opposite_active(match, self.damage, self.damage_type),
        ]


class LightningLockdown(ElementalBurstBase):
    name: Literal["Lightning Lockdown"] = "Lightning Lockdown"
    damage: int = 2
    damage_type: DamageElementalType = DamageElementalType.ELECTRO
    cost: Cost = Cost(
        elemental_dice_color=DieColor.ELECTRO, elemental_dice_number=3, charge=2
    )

    def get_actions(self, match: Any) -> List[Actions]:
        """
        Attack and create object
        """
        return super().get_actions(match) + [
            self.create_summon("Chains of Warding Thunder")
        ]


class ElectroCrystalCore(CreateStatusPassiveSkill):
    name: Literal["Electro Crystal Core"] = "Electro Crystal Core"
    status_name: Literal["Electro Crystal Core"] = "Electro Crystal Core"
    regenerate_when_revive: bool = False


# Talents


class AbsorbingPrism_3_7(TalentBase):
    name: Literal["Absorbing Prism"]
    version: Literal["3.7"]
    character_name: Literal["Electro Hypostasis"] = "Electro Hypostasis"
    cost: Cost = Cost(elemental_dice_color=DieColor.ELECTRO, elemental_dice_number=3)
    cost_label: int = (
        CostLabels.CARD.value | CostLabels.TALENT.value | CostLabels.EVENT.value
    )
    remove_when_used: bool = True

    def get_action_type(self, match: Any) -> Tuple[int, bool]:
        return PlayerActionLabels.CARD.value, True

    def get_actions(
        self, target: ObjectPosition | None, match: Any
    ) -> List[MakeDamageAction | CreateObjectAction]:
        """
        Using this card will heal electro hypostasis by 3 hp and attach
        electro crystal core to it. No need to equip it.
        """
        character = match.player_tables[self.position.player_idx].get_active_character()
        assert character.name == self.character_name
        return [
            MakeDamageAction(
                damage_value_list=[
                    DamageValue(
                        position=self.position,
                        damage_type=DamageType.HEAL,
                        target_position=character.position,
                        damage=-3,
                        damage_elemental_type=DamageElementalType.HEAL,
                        cost=Cost(),
                    )
                ],
            ),
            CreateObjectAction(
                object_name="Electro Crystal Core",
                object_position=character.position.set_area(
                    ObjectPositionType.CHARACTER_STATUS
                ),
                object_arguments={},
            ),
        ]


class AbsorbingPrism_4_2(AbsorbingPrism_3_7):
    version: Literal["4.2"] = "4.2"
    cost: Cost = Cost(elemental_dice_color=DieColor.ELECTRO, elemental_dice_number=2)


# character base


class ElectroHypostasis_3_7(CharacterBase):
    name: Literal["Electro Hypostasis"]
    version: Literal["3.7"] = "3.7"
    element: ElementType = ElementType.ELECTRO
    max_hp: int = 8
    max_charge: int = 2
    skills: List[
        ElementalNormalAttackBase
        | RockPaperScissorsCombo
        | RockPaperScissorsComboScissors
        | RockPaperScissorsComboPaper
        | LightningLockdown
        | ElectroCrystalCore
    ] = []
    faction: List[FactionType] = [FactionType.MONSTER]
    weapon_type: WeaponType = WeaponType.OTHER

    def _init_skills(self) -> None:
        self.skills = [
            ElementalNormalAttackBase(
                name="Electro Crystal Projection",
                damage_type=ELEMENT_TO_DAMAGE_TYPE[self.element],
                cost=ElementalNormalAttackBase.get_cost(self.element),
            ),
            RockPaperScissorsCombo(),
            RockPaperScissorsComboScissors(),
            RockPaperScissorsComboPaper(),
            LightningLockdown(),
            ElectroCrystalCore(),
        ]


register_class(
    ElectroHypostasis_3_7
    | AbsorbingPrism_3_7
    | AbsorbingPrism_4_2
    | ChainsOfWardingThunder_3_7
)
