"""
FreeExile Party Engine Data Types & Transfer Objects.
Defines 6-player party models, member snapshots, allocation modes, and lifecycle enums.
Follows Engineering Standards 2026: Soft cap <= 350 lines, strict typing, memory efficiency.
"""

from __future__ import annotations
import time
from dataclasses import dataclass, field
from enum import Enum
from typing import Any, Dict, List, Optional


class LootAllocationMode(str, Enum):
    """3 Canonical PoE2 Loot Allocation Modes."""
    PERMANENT = "PERMANENT"        # Locked permanently to allocated player
    SHORT = "SHORT"                # Locked 5.0s to allocated player, then converts to FFA
    FREE_FOR_ALL = "FREE_FOR_ALL"  # Instantly available for any party member to pickup


class PartyRole(str, Enum):
    """Role within a 6-player party."""
    LEADER = "LEADER"
    MEMBER = "MEMBER"


class PartyAction(str, Enum):
    """Supported party lifecycle actions."""
    CREATE = "CREATE"
    INVITE = "INVITE"
    ACCEPT = "ACCEPT"
    DECLINE = "DECLINE"
    PROMOTE = "PROMOTE"
    LEAVE = "LEAVE"
    KICK = "KICK"
    DISBAND = "DISBAND"
    SET_LOOT_MODE = "SET_LOOT_MODE"


@dataclass(slots=True, frozen=True)
class PartyMemberState:
    """Immutable snapshot of an individual party member's state."""
    player_id: str
    character_name: str
    level: int
    zone_id: str
    instance_id: Optional[str] = "inst_1"
    wx: float = 0.0
    wy: float = 0.0
    role: PartyRole = PartyRole.MEMBER
    is_online: bool = True
    hp: float = 100.0
    max_hp: float = 100.0

    @property
    def name(self) -> str:
        """Alias for character_name."""
        return self.character_name

    @property
    def x(self) -> float:
        """Alias for wx."""
        return self.wx

    @property
    def y(self) -> float:
        """Alias for wy."""
        return self.wy

    @property
    def is_alive(self) -> bool:
        """True if current HP > 0.0."""
        return self.hp > 0.0

    def with_position(self, wx: float, wy: float, zone_id: Optional[str] = None, instance_id: Optional[str] = None) -> PartyMemberState:
        """Returns a new immutable instance with updated coordinates."""
        return PartyMemberState(
            player_id=self.player_id,
            character_name=self.character_name,
            level=self.level,
            zone_id=zone_id if zone_id is not None else self.zone_id,
            instance_id=instance_id if instance_id is not None else self.instance_id,
            wx=wx,
            wy=wy,
            role=self.role,
            is_online=self.is_online,
            hp=self.hp,
            max_hp=self.max_hp,
        )

    def with_vitals(self, hp: float, max_hp: float, is_online: Optional[bool] = None) -> PartyMemberState:
        """Returns a new immutable instance with updated vitals."""
        return PartyMemberState(
            player_id=self.player_id,
            character_name=self.character_name,
            level=self.level,
            zone_id=self.zone_id,
            instance_id=self.instance_id,
            wx=self.wx,
            wy=self.wy,
            role=self.role,
            is_online=self.is_online if is_online is None else is_online,
            hp=hp,
            max_hp=max_hp,
        )


@dataclass(slots=True, frozen=True)
class PartyInvite:
    """Represents a pending invitation to join a party."""
    invite_id: str
    party_id: str
    inviter_id: str
    inviter_name: str
    target_player_id: str
    created_at_ms: int
    expires_at_ms: int

    def is_expired(self, current_time_ms: Optional[int] = None) -> bool:
        """Checks if the invitation has exceeded its 30-second TTL."""
        now = current_time_ms if current_time_ms is not None else int(time.time() * 1000)
        return now >= self.expires_at_ms


@dataclass(slots=True)
class PartyState:
    """Mutable container tracking active party members, leader, and configuration."""
    party_id: str
    leader_id: str
    members: Dict[str, PartyMemberState] = field(default_factory=dict)
    loot_mode: LootAllocationMode = LootAllocationMode.SHORT
    pending_invites: Dict[str, PartyInvite] = field(default_factory=dict)
    max_members: int = 6
    created_at: float = field(default_factory=time.time)

    @property
    def loot_allocation_mode(self) -> LootAllocationMode:
        """Alias for loot_mode for test harness compatibility."""
        return self.loot_mode

    @loot_allocation_mode.setter
    def loot_allocation_mode(self, mode: LootAllocationMode) -> None:
        self.loot_mode = mode


@dataclass(slots=True, frozen=True)
class PartyActionResult:
    """Standardized response from party lifecycle operations."""
    success: bool
    message: str
    party: Optional[PartyState] = None
    error_code: Optional[str] = None
