"""
Standalone world loader for the API.
Loads game data from JSON files without bot dependencies.
"""
import json
from pathlib import Path
from typing import Dict, List, Any, Optional, Union
from dataclasses import dataclass, field


@dataclass
class Outcome:
    """Represents an outcome of an action"""
    text: Union[str, Dict[str, str]]
    items_reward: Dict[str, int] = field(default_factory=dict)
    damage_taken: int = 0


@dataclass
class Action:
    """Represents an action that can be performed on an interactable"""
    id: str
    label: Union[str, Dict[str, str]]
    stamina_cost: int = 2
    outcomes: Dict[str, Outcome] = field(default_factory=dict)
    
    def add_outcome(self, outcome_type: str, outcome: Outcome):
        self.outcomes[outcome_type] = outcome


@dataclass
class Interactable:
    """Represents an interactable object"""
    id: str
    name: Union[str, Dict[str, str]]
    image_path: str = ""
    actions: List[Action] = field(default_factory=list)
    unlocked_by: str = ""
    locked: bool = False
    
    def add_action(self, action: Action):
        self.actions.append(action)


@dataclass
class Exit:
    """Represents an exit from a location"""
    direction: str
    destination: str
    description: str = ""


@dataclass
class Location:
    """Represents a location in the game world"""
    id: str
    name: Union[str, Dict[str, str]]
    description: Union[str, Dict[str, str]]
    image_path: str = ""
    exits: Dict[str, str] = field(default_factory=dict)  # direction -> destination_id
    exit_stamina: Dict[str, int] = field(default_factory=dict)  # direction -> stamina_cost
    interactables: List[Interactable] = field(default_factory=list)
    npcs: List[str] = field(default_factory=list)
    tags: List[str] = field(default_factory=list)  # Location tags like 'workbench', 'safe_zone'
    x: float = 0.0  # X coordinate for distance calculations
    y: float = 0.0  # Y coordinate for distance calculations
    danger_level: int = 0  # Danger level (0-5)
    unlocked_by: str = ""
    locked: bool = False
    
    def add_exit(self, direction: str, destination: str, stamina_cost: int = 5):
        self.exits[direction] = destination
        self.exit_stamina[direction] = stamina_cost
    
    def add_interactable(self, interactable: Interactable):
        self.interactables.append(interactable)


@dataclass
class World:
    """Represents the entire game world"""
    locations: Dict[str, Location] = field(default_factory=dict)
    
    def add_location(self, location: Location):
        self.locations[location.id] = location


class WorldLoader:
    """Loads world data from JSON files"""
    
    def __init__(self, gamedata_path: str = "./gamedata"):
        self.gamedata_path = Path(gamedata_path)
        self.interactable_templates = {}
    
    def load_interactable_templates(self) -> Dict[str, Any]:
        """Load interactable templates from interactables.json"""
        json_path = self.gamedata_path / 'interactables.json'
        
        try:
            with open(json_path, 'r') as f:
                data = json.load(f)
            self.interactable_templates = data.get('interactables', {})
            print(f"📦 Loaded {len(self.interactable_templates)} interactable templates")
        except FileNotFoundError:
            print("⚠️  interactables.json not found")
        except Exception as e:
            print(f"⚠️  Error loading interactables.json: {e}")
        
        return self.interactable_templates
    
    def create_interactable_from_template(
        self, 
        template_id: str, 
        template_data: Dict[str, Any], 
        instance_data: Dict[str, Any]
    ) -> Interactable:
        """Create an Interactable object from template and instance data"""
        interactable = Interactable(
            id=template_id,
            name=template_data.get('name', 'Unknown'),
            image_path=template_data.get('image_path', ''),
            unlocked_by=instance_data.get('unlocked_by', template_data.get('unlocked_by', '')),
        )
        
        # Set locked status if unlocked_by is present
        if interactable.unlocked_by:
            interactable.locked = True
        
        # Get actions from template
        template_actions = template_data.get('actions', {})
        
        # Get outcomes from instance
        instance_outcomes = instance_data.get('outcomes', {})
        
        # Build actions by merging template actions with instance outcomes
        for action_id, action_template in template_actions.items():
            action = Action(
                id=action_template['id'],
                label=action_template['label'],
                stamina_cost=action_template.get('stamina_cost', 2)
            )
            
            # Get instance-specific outcome data for this action
            if action_id in instance_outcomes:
                outcome_data = instance_outcomes[action_id]
                
                # Build outcomes from the instance data
                text_dict = outcome_data.get('text', {})
                rewards = outcome_data.get('rewards', {})
                
                # Add success outcome
                if text_dict.get('success'):
                    items_reward = {}
                    if 'items' in rewards:
                        for item in rewards['items']:
                            items_reward[item['item_id']] = item.get('quantity', 1)
                    
                    outcome = Outcome(
                        text=text_dict['success'],
                        items_reward=items_reward,
                        damage_taken=rewards.get('damage', 0)
                    )
                    action.add_outcome('success', outcome)
                
                # Add failure outcome
                if text_dict.get('failure'):
                    outcome = Outcome(
                        text=text_dict['failure'],
                        items_reward={},
                        damage_taken=0
                    )
                    action.add_outcome('failure', outcome)
                
                # Add critical failure outcome
                if text_dict.get('crit_failure'):
                    outcome = Outcome(
                        text=text_dict['crit_failure'],
                        items_reward={},
                        damage_taken=rewards.get('crit_damage', 0)
                    )
                    action.add_outcome('critical_failure', outcome)
            
            interactable.add_action(action)
        
        return interactable
    
    def load_locations(self) -> Dict[str, Location]:
        """Load all locations from locations.json"""
        json_path = self.gamedata_path / 'locations.json'
        locations = {}
        
        try:
            with open(json_path, 'r') as f:
                data = json.load(f)
            
            # Get danger config
            danger_config = data.get('danger_config', {})
            
            # First pass: create all locations
            locations_data = data.get('locations', [])
            if isinstance(locations_data, dict):
                # Old format: dict of locations
                locations_iter = locations_data.items()
            else:
                # New format: list of locations
                locations_iter = [(loc['id'], loc) for loc in locations_data]
            
            for loc_id, loc_data in locations_iter:
                # Get danger level from danger_config
                danger_level = 0
                if loc_id in danger_config:
                    danger_level = danger_config[loc_id].get('danger_level', 0)
                
                location = Location(
                    id=loc_id,
                    name=loc_data.get('name', 'Unknown Location'),
                    description=loc_data.get('description', ''),
                    image_path=loc_data.get('image_path', ''),
                    x=float(loc_data.get('x', 0.0)),
                    y=float(loc_data.get('y', 0.0)),
                    danger_level=danger_level,
                    tags=loc_data.get('tags', []),
                    npcs=loc_data.get('npcs', []),
                    unlocked_by=loc_data.get('unlocked_by', '')
                )
                
                # Set locked status if unlocked_by is present
                if location.unlocked_by:
                    location.locked = True
                
                # Add exits
                for direction, destination in loc_data.get('exits', {}).items():
                    location.add_exit(direction, destination)
                
                # Add NPCs
                location.npcs = loc_data.get('npcs', [])
                
                # Add interactables
                interactables_data = loc_data.get('interactables', {})
                if isinstance(interactables_data, dict):
                    # New format: dict of interactables
                    interactables_list = [
                        {**data, 'instance_id': inst_id, 'id': data.get('template_id', inst_id)}
                        for inst_id, data in interactables_data.items()
                    ]
                else:
                    # Old format: list of interactables
                    interactables_list = interactables_data
                
                for interactable_data in interactables_list:
                    template_id = interactable_data.get('id')
                    instance_id = interactable_data.get('instance_id', template_id)
                    
                    if template_id in self.interactable_templates:
                        template = self.interactable_templates[template_id]
                        interactable = self.create_interactable_from_template(
                            instance_id,
                            template,
                            interactable_data
                        )
                        location.add_interactable(interactable)
                
                locations[loc_id] = location
            
            # Second pass: add connections from the connections array
            connections = data.get('connections', [])
            for conn in connections:
                from_id = conn.get('from')
                to_id = conn.get('to')
                direction = conn.get('direction')
                stamina_cost = conn.get('stamina_cost', 5)  # Default 5 if not specified
                
                if from_id in locations and direction:
                    locations[from_id].add_exit(direction, to_id, stamina_cost)
            
            print(f"🗺️  Loaded {len(locations)} locations with {len(connections)} connections")
        except FileNotFoundError:
            print("⚠️  locations.json not found")
        except Exception as e:
            print(f"⚠️  Error loading locations.json: {e}")
            import traceback
            traceback.print_exc()
        
        return locations
    
    def load_world(self) -> World:
        """Load the entire world"""
        world = World()
        
        # Load interactable templates first
        self.load_interactable_templates()
        
        # Load locations
        locations = self.load_locations()
        for location in locations.values():
            world.add_location(location)
        
        return world


def load_world() -> World:
    """Convenience function to load the world"""
    loader = WorldLoader()
    return loader.load_world()
