"""
Effect system for temporary status modifiers in combat.

This module defines the Effect dataclass which represents temporary buffs,
debuffs, damage over time, healing over time, stuns, and shields.
"""

from dataclasses import dataclass, asdict
from typing import Dict, Any, Optional
from app.models.enums import EffectType, StatType


@dataclass
class Effect:
    """
    Represents a temporary effect applied to a combatant.

    Effects are processed at the start of each turn via the tick() method.
    They can stack up to max_stacks, and duration refreshes on re-application.

    Attributes:
        effect_id: Unique identifier for this effect instance
        name: Display name of the effect
        effect_type: Type of effect (BUFF, DEBUFF, DOT, HOT, STUN, SHIELD)
        duration: Turns remaining before effect expires
        power: Damage/healing per turn OR stat modifier amount
        stat_affected: Which stat is modified (for BUFF/DEBUFF only)
        stacks: Number of times this effect has been stacked
        max_stacks: Maximum number of stacks allowed (default 5)
        source: Who/what applied this effect (character_id or ability_id)
    """

    effect_id: str
    name: str
    effect_type: EffectType
    duration: int
    power: int
    stat_affected: Optional[StatType] = None
    stacks: int = 1
    max_stacks: int = 5
    source: str = ""

    def tick(self) -> Dict[str, Any]:
        """
        Process one turn of this effect.

        Returns a dictionary describing what happened this turn, including:
        - effect_name: Name of the effect
        - effect_type: Type of effect
        - value: Damage dealt (DOT) or healing done (HOT)
        - shield_remaining: Current shield strength (SHIELD only)
        - stunned: True if this is a stun effect (STUN only)
        - stat_modifier: Amount stats are modified (BUFF/DEBUFF only)
        - expired: True if effect duration reached 0

        Returns:
            Dictionary with effect processing results
        """
        result = {
            "effect_name": self.name,
            "effect_type": self.effect_type.value,
            "value": 0,
            "expired": False,
        }

        # Process effect based on type
        if self.effect_type == EffectType.DOT:
            # Damage over time: deal damage equal to power × stacks
            result["value"] = self.power * self.stacks
            result["message"] = f"{self.name} deals {result['value']} damage"

        elif self.effect_type == EffectType.HOT:
            # Heal over time: heal equal to power × stacks
            result["value"] = self.power * self.stacks
            result["message"] = f"{self.name} heals {result['value']} HP"

        elif self.effect_type == EffectType.STUN:
            # Stun: prevents actions this turn
            result["stunned"] = True
            result["message"] = f"{self.name} prevents actions"

        elif self.effect_type == EffectType.SHIELD:
            # Shield: absorbs damage (power × stacks = shield strength)
            result["shield_remaining"] = self.power * self.stacks
            result["message"] = f"{self.name} absorbs up to {result['shield_remaining']} damage"

        elif self.effect_type in [EffectType.BUFF, EffectType.DEBUFF]:
            # Buff/Debuff: modify stats
            result["stat_affected"] = self.stat_affected.value if self.stat_affected else None
            result["stat_modifier"] = self.power * self.stacks
            if self.effect_type == EffectType.BUFF:
                result["message"] = f"{self.name} increases {result['stat_affected']} by {result['stat_modifier']}"
            else:
                result["message"] = f"{self.name} decreases {result['stat_affected']} by {result['stat_modifier']}"

        # Decrease duration
        self.duration -= 1
        if self.duration <= 0:
            result["expired"] = True
            result["message"] = f"{self.name} has expired"

        return result

    def apply_stack(self, additional_duration: int = 0) -> None:
        """
        Apply an additional stack of this effect.

        Increases stack count (up to max_stacks) and refreshes duration.
        If additional_duration is provided, it's added to current duration.

        Args:
            additional_duration: Extra turns to add (default 0 = refresh only)
        """
        if self.stacks < self.max_stacks:
            self.stacks += 1

        # Refresh duration or extend it
        if additional_duration > 0:
            self.duration = max(self.duration, additional_duration)
        else:
            # Find the base duration (current + turns already consumed)
            # For refresh behavior, we'd need to store original_duration
            # For now, just use the provided duration
            pass

    def reduce_shield(self, damage: int) -> int:
        """
        Reduce shield strength by damage amount.

        Only applicable for SHIELD effects. Returns remaining damage after shield.

        Args:
            damage: Amount of damage to absorb

        Returns:
            Remaining damage after shield absorption
        """
        if self.effect_type != EffectType.SHIELD:
            return damage

        shield_strength = self.power * self.stacks
        if damage >= shield_strength:
            # Shield breaks completely
            remaining_damage = damage - shield_strength
            self.power = 0  # Shield depleted
            self.duration = 0  # Effect expires
            return remaining_damage
        else:
            # Shield partially absorbs damage
            damage_per_stack = damage / self.stacks
            self.power = max(0, int(self.power - damage_per_stack))
            return 0

    def to_dict(self) -> Dict[str, Any]:
        """
        Serialize effect to a dictionary.

        Returns:
            Dictionary containing all effect data
        """
        data = asdict(self)
        data["effect_type"] = self.effect_type.value
        if self.stat_affected:
            data["stat_affected"] = self.stat_affected.value
        return data

    @classmethod
    def from_dict(cls, data: Dict[str, Any]) -> 'Effect':
        """
        Deserialize effect from a dictionary.

        Args:
            data: Dictionary containing effect data

        Returns:
            Effect instance
        """
        # Convert string values back to enums
        effect_type = EffectType(data["effect_type"])
        stat_affected = StatType(data["stat_affected"]) if data.get("stat_affected") else None

        return cls(
            effect_id=data["effect_id"],
            name=data["name"],
            effect_type=effect_type,
            duration=data["duration"],
            power=data["power"],
            stat_affected=stat_affected,
            stacks=data.get("stacks", 1),
            max_stacks=data.get("max_stacks", 5),
            source=data.get("source", ""),
        )

    def __repr__(self) -> str:
        """String representation of the effect."""
        if self.effect_type in [EffectType.BUFF, EffectType.DEBUFF]:
            return (
                f"Effect({self.name}, {self.effect_type.value}, "
                f"{self.stat_affected.value if self.stat_affected else 'N/A'} "
                f"{'+' if self.effect_type == EffectType.BUFF else '-'}{self.power * self.stacks}, "
                f"{self.duration}t, {self.stacks}x)"
            )
        else:
            return (
                f"Effect({self.name}, {self.effect_type.value}, "
                f"power={self.power * self.stacks}, "
                f"duration={self.duration}t, stacks={self.stacks}x)"
            )
