"""
Response parser for AI narrative responses.

This module handles AI response parsing. Game state changes (items, gold, XP)
are now handled exclusively through predetermined dice check outcomes in
action templates, not through AI-generated JSON.
"""

from dataclasses import dataclass, field
from typing import Any, Optional

import structlog

logger = structlog.get_logger(__name__)


@dataclass
class ItemGrant:
    """
    Represents an item granted by the AI during gameplay.

    The AI can grant items in two ways:
    1. By item_id - References an existing item from game data
    2. By name/type/description - Creates a generic item
    """
    item_id: Optional[str] = None  # For existing items
    name: Optional[str] = None  # For generic items
    item_type: Optional[str] = None  # consumable, weapon, armor, quest_item
    description: Optional[str] = None
    value: int = 0
    quantity: int = 1

    def is_existing_item(self) -> bool:
        """Check if this references an existing item by ID."""
        return self.item_id is not None

    def is_generic_item(self) -> bool:
        """Check if this is a generic item created by the AI."""
        return self.item_id is None and self.name is not None


@dataclass
class GameStateChanges:
    """
    Structured game state changes extracted from AI response.

    These changes are validated and applied to the character after
    the AI generates its narrative response.
    """
    items_given: list[ItemGrant] = field(default_factory=list)
    items_taken: list[str] = field(default_factory=list)  # item_ids to remove
    gold_given: int = 0
    gold_taken: int = 0
    experience_given: int = 0
    quest_offered: Optional[str] = None  # quest_id
    quest_completed: Optional[str] = None  # quest_id
    location_change: Optional[str] = None


@dataclass
class ParsedAIResponse:
    """
    Complete parsed AI response with narrative and game state changes.

    Attributes:
        narrative: The narrative text to display to the player
        game_changes: Structured game state changes to apply
        raw_response: The original unparsed response from AI
        parse_success: Whether parsing succeeded
        parse_errors: Any errors encountered during parsing
    """
    narrative: str
    game_changes: GameStateChanges
    raw_response: str
    parse_success: bool = True
    parse_errors: list[str] = field(default_factory=list)


class ResponseParserError(Exception):
    """Exception raised when response parsing fails critically."""
    pass


def parse_ai_response(response_text: str) -> ParsedAIResponse:
    """
    Parse an AI response to extract the narrative text.

    Game state changes (items, gold, XP) are now handled exclusively through
    predetermined dice check outcomes, not through AI-generated structured data.

    Args:
        response_text: The raw AI response text

    Returns:
        ParsedAIResponse with narrative (game_changes will be empty)
    """
    logger.debug("Parsing AI response", response_length=len(response_text))

    # Return the full response as narrative
    # Game state changes come from predetermined check_outcomes, not AI
    return ParsedAIResponse(
        narrative=response_text.strip(),
        game_changes=GameStateChanges(),
        raw_response=response_text,
        parse_success=True,
        parse_errors=[]
    )


def _parse_game_actions(data: dict[str, Any]) -> GameStateChanges:
    """
    Parse the game actions dictionary into a GameStateChanges object.

    Args:
        data: Dictionary from parsed JSON

    Returns:
        GameStateChanges object with parsed data
    """
    changes = GameStateChanges()

    # Parse items_given
    if "items_given" in data and data["items_given"]:
        for item_data in data["items_given"]:
            if isinstance(item_data, dict):
                item_grant = ItemGrant(
                    item_id=item_data.get("item_id"),
                    name=item_data.get("name"),
                    item_type=item_data.get("type"),
                    description=item_data.get("description"),
                    value=item_data.get("value", 0),
                    quantity=item_data.get("quantity", 1)
                )
                changes.items_given.append(item_grant)
            elif isinstance(item_data, str):
                # Simple string format - treat as item_id
                changes.items_given.append(ItemGrant(item_id=item_data))

    # Parse items_taken
    if "items_taken" in data and data["items_taken"]:
        changes.items_taken = [
            item_id for item_id in data["items_taken"]
            if isinstance(item_id, str)
        ]

    # Parse gold changes
    changes.gold_given = int(data.get("gold_given", 0))
    changes.gold_taken = int(data.get("gold_taken", 0))

    # Parse experience
    changes.experience_given = int(data.get("experience_given", 0))

    # Parse quest changes
    changes.quest_offered = data.get("quest_offered")
    changes.quest_completed = data.get("quest_completed")

    # Parse location change
    changes.location_change = data.get("location_change")

    return changes
