"""
Action Prompt Model

This module defines the ActionPrompt dataclass for button-based story actions.
Each action prompt represents a predefined action that players can take during
story progression, with tier-based availability and context filtering.

Usage:
    from app.models.action_prompt import ActionPrompt, ActionCategory, LocationType

    action = ActionPrompt(
        prompt_id="ask_locals",
        category=ActionCategory.ASK_QUESTION,
        display_text="Ask locals for information",
        description="Talk to NPCs to learn about quests and rumors",
        tier_required=UserTier.FREE,
        context_filter=[LocationType.TOWN, LocationType.TAVERN],
        dm_prompt_template="The player asks locals about {{ topic }}..."
    )

    if action.is_available(UserTier.FREE, LocationType.TOWN):
        # Show action button to player
        pass
"""

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

from app.ai.model_selector import UserTier


@dataclass
class CheckRequirement:
    """
    Defines the dice check required for an action.

    Used to determine outcomes before AI narration.
    """
    check_type: str  # "search" or "skill"
    skill: Optional[str] = None  # For skill checks: perception, persuasion, etc.
    difficulty: str = "medium"  # trivial, easy, medium, hard, very_hard

    def to_dict(self) -> dict:
        """Serialize for API response."""
        return {
            "check_type": self.check_type,
            "skill": self.skill,
            "difficulty": self.difficulty,
        }

    @classmethod
    def from_dict(cls, data: dict) -> "CheckRequirement":
        """Create from dictionary."""
        return cls(
            check_type=data.get("check_type", "skill"),
            skill=data.get("skill"),
            difficulty=data.get("difficulty", "medium"),
        )


class ActionCategory(str, Enum):
    """Categories of story actions."""
    ASK_QUESTION = "ask_question"      # Gather information from NPCs
    TRAVEL = "travel"                  # Move to a new location
    GATHER_INFO = "gather_info"        # Search or investigate
    REST = "rest"                      # Rest and recover
    INTERACT = "interact"              # Interact with objects/environment
    EXPLORE = "explore"                # Explore the area
    SPECIAL = "special"                # Special tier-specific actions


class LocationType(str, Enum):
    """Types of locations in the game world."""
    TOWN = "town"                      # Populated settlements
    TAVERN = "tavern"                  # Taverns and inns
    WILDERNESS = "wilderness"          # Outdoor areas, forests, fields
    DUNGEON = "dungeon"                # Dungeons and caves
    SAFE_AREA = "safe_area"            # Protected zones, temples
    LIBRARY = "library"                # Libraries and archives
    ANY = "any"                        # Available in all locations


@dataclass
class ActionPrompt:
    """
    Represents a predefined story action that players can select.

    Action prompts are displayed as buttons in the story UI. Each action
    has tier requirements and context filters to determine availability.

    Attributes:
        prompt_id: Unique identifier for the action
        category: Category of action (ASK_QUESTION, TRAVEL, etc.)
        display_text: Text shown on the action button
        description: Tooltip/help text explaining the action
        tier_required: Minimum subscription tier required
        context_filter: List of location types where action is available
        dm_prompt_template: Jinja2 template for generating AI prompt
        icon: Optional icon name for the button
        cooldown_turns: Optional cooldown in turns before action can be used again
    """

    prompt_id: str
    category: ActionCategory
    display_text: str
    description: str
    tier_required: UserTier
    context_filter: List[LocationType]
    dm_prompt_template: str
    icon: Optional[str] = None
    cooldown_turns: int = 0
    requires_check: Optional[CheckRequirement] = None

    def is_available(self, user_tier: UserTier, location_type: LocationType) -> bool:
        """
        Check if this action is available for a user at a location.

        Args:
            user_tier: The user's subscription tier
            location_type: The current location type

        Returns:
            True if the action is available, False otherwise
        """
        # Check tier requirement
        if not self._tier_meets_requirement(user_tier):
            return False

        # Check location filter
        if not self._location_matches_filter(location_type):
            return False

        return True

    def _tier_meets_requirement(self, user_tier: UserTier) -> bool:
        """
        Check if user tier meets the minimum requirement.

        Tier hierarchy: FREE < BASIC < PREMIUM < ELITE

        Args:
            user_tier: The user's subscription tier

        Returns:
            True if tier requirement is met
        """
        tier_order = {
            UserTier.FREE: 0,
            UserTier.BASIC: 1,
            UserTier.PREMIUM: 2,
            UserTier.ELITE: 3,
        }

        user_level = tier_order.get(user_tier, 0)
        required_level = tier_order.get(self.tier_required, 0)

        return user_level >= required_level

    def _location_matches_filter(self, location_type: LocationType) -> bool:
        """
        Check if location matches the context filter.

        Args:
            location_type: The current location type

        Returns:
            True if location matches filter
        """
        # ANY location type matches everything
        if LocationType.ANY in self.context_filter:
            return True

        # Check if location is in the filter list
        return location_type in self.context_filter

    def is_locked(self, user_tier: UserTier) -> bool:
        """
        Check if this action is locked due to tier restriction.

        Used to show locked actions with upgrade prompts.

        Args:
            user_tier: The user's subscription tier

        Returns:
            True if the action is locked (tier too low)
        """
        return not self._tier_meets_requirement(user_tier)

    def get_lock_reason(self, user_tier: UserTier) -> Optional[str]:
        """
        Get the reason why an action is locked.

        Args:
            user_tier: The user's subscription tier

        Returns:
            Lock reason message, or None if not locked
        """
        if not self._tier_meets_requirement(user_tier):
            tier_names = {
                UserTier.FREE: "Free",
                UserTier.BASIC: "Basic",
                UserTier.PREMIUM: "Premium",
                UserTier.ELITE: "Elite",
            }
            required_name = tier_names.get(self.tier_required, "Unknown")
            return f"Requires {required_name} tier or higher"

        return None

    def to_dict(self) -> dict:
        """
        Convert to dictionary for JSON serialization.

        Returns:
            Dictionary representation of the action prompt
        """
        result = {
            "prompt_id": self.prompt_id,
            "category": self.category.value,
            "display_text": self.display_text,
            "description": self.description,
            "tier_required": self.tier_required.value,
            "context_filter": [loc.value for loc in self.context_filter],
            "dm_prompt_template": self.dm_prompt_template,
            "icon": self.icon,
            "cooldown_turns": self.cooldown_turns,
        }
        if self.requires_check:
            result["requires_check"] = self.requires_check.to_dict()
        return result

    @classmethod
    def from_dict(cls, data: dict) -> "ActionPrompt":
        """
        Create an ActionPrompt from a dictionary.

        Args:
            data: Dictionary containing action prompt data

        Returns:
            ActionPrompt instance

        Raises:
            ValueError: If required fields are missing or invalid
        """
        # Parse category enum
        category_str = data.get("category", "")
        try:
            category = ActionCategory(category_str)
        except ValueError:
            raise ValueError(f"Invalid action category: {category_str}")

        # Parse tier enum
        tier_str = data.get("tier_required", "free")
        try:
            tier_required = UserTier(tier_str)
        except ValueError:
            raise ValueError(f"Invalid user tier: {tier_str}")

        # Parse location types
        context_filter_raw = data.get("context_filter", ["any"])
        context_filter = []
        for loc_str in context_filter_raw:
            try:
                context_filter.append(LocationType(loc_str.lower()))
            except ValueError:
                raise ValueError(f"Invalid location type: {loc_str}")

        # Parse requires_check if present
        requires_check = None
        if "requires_check" in data and data["requires_check"]:
            requires_check = CheckRequirement.from_dict(data["requires_check"])

        return cls(
            prompt_id=data.get("prompt_id", ""),
            category=category,
            display_text=data.get("display_text", ""),
            description=data.get("description", ""),
            tier_required=tier_required,
            context_filter=context_filter,
            dm_prompt_template=data.get("dm_prompt_template", ""),
            icon=data.get("icon"),
            cooldown_turns=data.get("cooldown_turns", 0),
            requires_check=requires_check,
        )

    def __repr__(self) -> str:
        """String representation for debugging."""
        return (
            f"ActionPrompt(prompt_id='{self.prompt_id}', "
            f"category={self.category.value}, "
            f"tier={self.tier_required.value})"
        )
