"""
Enhanced Map Server with Editor and Authentication
"""
from flask import Flask, jsonify, request, send_from_directory, session, redirect, url_for
from flask_cors import CORS
from pathlib import Path
import json
import os
import sys
import secrets
from werkzeug.utils import secure_filename
from functools import wraps

# Get the directory of this script
SCRIPT_DIR = Path(__file__).parent.resolve()
PARENT_DIR = SCRIPT_DIR.parent

# Add parent directory to path for imports
sys.path.insert(0, str(PARENT_DIR))

app = Flask(__name__, static_folder=str(SCRIPT_DIR))
app.secret_key = os.environ.get('EDITOR_SECRET_KEY', secrets.token_hex(32))
CORS(app)

# Configuration
ADMIN_PASSWORD = os.environ.get('EDITOR_PASSWORD', 'admin123')  # Change this!
UPLOAD_FOLDER = PARENT_DIR / 'images' / 'locations'
UPLOAD_FOLDER.mkdir(parents=True, exist_ok=True)
ALLOWED_EXTENSIONS = {'png', 'jpg', 'jpeg', 'gif', 'webp'}

# JSON data files
GAMEDATA_DIR = PARENT_DIR / 'gamedata'
LOCATIONS_FILE = GAMEDATA_DIR / 'locations.json'
NPCS_FILE = GAMEDATA_DIR / 'npcs.json'
ITEMS_FILE = GAMEDATA_DIR / 'items.json'
INTERACTABLES_FILE = GAMEDATA_DIR / 'interactables.json'

def allowed_file(filename):
    return '.' in filename and filename.rsplit('.', 1)[1].lower() in ALLOWED_EXTENSIONS

def load_json_file(filepath):
    """Helper to load JSON file"""
    try:
        with open(filepath, 'r') as f:
            return json.load(f)
    except FileNotFoundError:
        return None
    except Exception as e:
        print(f"Error loading {filepath}: {e}")
        return None

def save_json_file(filepath, data):
    """Helper to save JSON file"""
    try:
        with open(filepath, 'w') as f:
            json.dump(data, f, indent=2)
        return True
    except Exception as e:
        print(f"Error saving {filepath}: {e}")
        return False

def require_auth(f):
    """Decorator to require authentication for editor routes"""
    @wraps(f)
    def decorated_function(*args, **kwargs):
        if not session.get('authenticated'):
            return jsonify({'error': 'Unauthorized'}), 401
        return f(*args, **kwargs)
    return decorated_function

# ==================== AUTHENTICATION ROUTES ====================

@app.route('/api/login', methods=['POST'])
def login():
    """Authenticate user with password"""
    data = request.get_json()
    password = data.get('password', '')
    
    if password == ADMIN_PASSWORD:
        session['authenticated'] = True
        session.permanent = True
        return jsonify({'success': True, 'message': 'Authenticated successfully'})
    else:
        return jsonify({'success': False, 'message': 'Invalid password'}), 401

@app.route('/api/logout', methods=['POST'])
def logout():
    """Logout user"""
    session.pop('authenticated', None)
    return jsonify({'success': True, 'message': 'Logged out'})

@app.route('/api/editor/restart-bot', methods=['POST'])
@require_auth
def restart_bot():
    """Restart the Telegram bot container"""
    try:
        import subprocess
        
        # Try to restart the bot container
        result = subprocess.run(
            ['docker', 'restart', 'echoes_of_the_ashes_bot'],
            capture_output=True,
            text=True,
            timeout=30
        )
        
        if result.returncode == 0:
            return jsonify({
                'success': True, 
                'message': 'Bot restarted successfully'
            })
        else:
            return jsonify({
                'success': False, 
                'error': f'Docker restart failed: {result.stderr}'
            }), 500
            
    except subprocess.TimeoutExpired:
        return jsonify({
            'success': False, 
            'error': 'Restart command timed out'
        }), 500
    except FileNotFoundError:
        return jsonify({
            'success': False, 
            'error': 'Docker command not found. Is Docker installed?'
        }), 500
    except Exception as e:
        return jsonify({
            'success': False, 
            'error': f'Unexpected error: {str(e)}'
        }), 500

@app.route('/api/editor/bot-logs', methods=['GET'])
@require_auth
def get_bot_logs():
    """Get logs from the Telegram bot container"""
    try:
        import subprocess
        
        # Get number of lines from query parameter (default 100)
        lines = request.args.get('lines', '100')
        try:
            lines = int(lines)
            if lines < 1 or lines > 1000:
                lines = 100
        except ValueError:
            lines = 100
        
        # Get logs from the bot container
        result = subprocess.run(
            ['docker', 'logs', 'echoes_of_the_ashes_bot', '--tail', str(lines)],
            capture_output=True,
            text=True,
            timeout=10
        )
        
        # Combine stdout and stderr (Docker logs can use both)
        logs = result.stdout + result.stderr
        
        return jsonify({
            'success': True,
            'logs': logs,
            'lines': lines
        })
            
    except subprocess.TimeoutExpired:
        return jsonify({
            'success': False, 
            'error': 'Logs command timed out'
        }), 500
    except FileNotFoundError:
        return jsonify({
            'success': False, 
            'error': 'Docker command not found. Is Docker installed?'
        }), 500
    except Exception as e:
        return jsonify({
            'success': False, 
            'error': f'Unexpected error: {str(e)}'
        }), 500

@app.route('/api/check-auth', methods=['GET'])
def check_auth():
    """Check if user is authenticated"""
    return jsonify({'authenticated': session.get('authenticated', False)})

# ==================== PUBLIC ROUTES ====================

@app.route('/')
def index():
    """Serve the main map viewer"""
    return send_from_directory(SCRIPT_DIR, 'index.html')

@app.route('/editor')
def editor():
    """Serve the map editor (requires auth)"""
    return send_from_directory(SCRIPT_DIR, 'editor.html')

@app.route('/<path:path>')
def serve_static(path):
    """Serve static files"""
    return send_from_directory(SCRIPT_DIR, path)

@app.route('/images/<path:path>')
def serve_images(path):
    """Serve images from parent directory"""
    return send_from_directory(PARENT_DIR / 'images', path)

@app.route('/api/map-data', methods=['GET'])
@app.route('/map_data.json', methods=['GET'])
def get_map_data():
    """Get current map data (exported from world_loader which now loads from JSON)"""
    try:
        from data.world_loader import export_map_data
        map_data = export_map_data()
        return jsonify(map_data)
    except Exception as e:
        return jsonify({'error': str(e)}), 500

# ==================== EDITOR API ROUTES (PROTECTED) ====================

@app.route('/api/editor/locations', methods=['GET'])
@require_auth
def get_locations():
    """Get all locations with full details"""
    try:
        # Load directly from JSON files to get latest data
        locations_data = load_json_file(LOCATIONS_FILE)
        npcs_data = load_json_file(NPCS_FILE)
        
        if not locations_data:
            return jsonify({'locations': []})
        
        locations = []
        for loc_data in locations_data.get('locations', []):
            location_id = loc_data.get('id')
            
            # Get danger config
            danger_config = locations_data.get('danger_config', {}).get(location_id, {})
            
            # Get spawn config
            spawn_config = locations_data.get('spawn_config', {}).get(location_id, [])
            spawn_npcs = []
            
            for spawn_entry in spawn_config:
                npc_id = spawn_entry.get('npc_id')
                weight = spawn_entry.get('weight', 50)
                
                # Get NPC details
                npc_def = npcs_data.get('npcs', {}).get(npc_id, {})
                if npc_def:
                    spawn_npcs.append({
                        'npc_id': npc_id,
                        'name': npc_def.get('name', npc_id),
                        'emoji': npc_def.get('emoji', '👹'),
                        'weight': weight
                    })
            
            # Get connections from locations.json
            exits = []
            for conn in locations_data.get('connections', []):
                if conn.get('from') == location_id:
                    exits.append(conn.get('to'))
            
            locations.append({
                'id': location_id,
                'name': loc_data.get('name', ''),
                'description': loc_data.get('description', ''),
                'image_path': loc_data.get('image_path', ''),
                'x': loc_data.get('x', 0.0),
                'y': loc_data.get('y', 0.0),
                'danger_level': danger_config.get('danger_level', 0),
                'encounter_rate': danger_config.get('encounter_rate', 0.0),
                'wandering_chance': danger_config.get('wandering_chance', 0.0),
                'spawn_npcs': spawn_npcs,
                'exits': exits,
                'interactables_count': len(loc_data.get('interactables', {}))
            })
        
        return jsonify({'locations': locations})
    except Exception as e:
        import traceback
        print(f"Error loading locations: {e}\n{traceback.format_exc()}")
        return jsonify({'error': str(e)}), 500

@app.route('/api/editor/location/<location_id>', methods=['GET'])
@require_auth
def get_location_detail(location_id):
    """Get detailed information about a specific location"""
    try:
        # Load directly from JSON to get latest data
        locations_data = load_json_file(LOCATIONS_FILE)
        npcs_data = load_json_file(NPCS_FILE)
        
        if not locations_data:
            return jsonify({'error': 'Locations file not found'}), 404
        
        # Find the location in the list
        location = None
        for loc in locations_data.get('locations', []):
            if loc.get('id') == location_id:
                location = loc
                break
        
        if not location:
            return jsonify({'error': 'Location not found'}), 404
        
        # Get danger config
        danger_config = locations_data.get('danger_config', {}).get(location_id, {})
        
        # Get spawn config
        spawn_config = locations_data.get('spawn_config', {}).get(location_id, [])
        spawn_npcs = []
        
        for spawn_entry in spawn_config:
            npc_id = spawn_entry.get('npc_id')
            weight = spawn_entry.get('weight', 50)
            
            # Get NPC details
            npc_def = npcs_data.get('npcs', {}).get(npc_id, {})
            if npc_def:
                spawn_npcs.append({
                    'npc_id': npc_id,
                    'name': npc_def.get('name', npc_id),
                    'emoji': npc_def.get('emoji', '👹'),
                    'weight': weight
                })
        
        # Get exits from connections
        exits = {}
        for conn in locations_data.get('connections', []):
            if conn.get('from') == location_id:
                to_id = conn.get('to')
                exits[to_id] = {
                    'stamina_cost': conn.get('stamina_cost', 1),
                    'distance': conn.get('distance', 1.0)
                }
        
        return jsonify({
            'id': location_id,
            'name': location.get('name', ''),
            'description': location.get('description', ''),
            'image_path': location.get('image_path', ''),
            'x': location.get('x', 0.0),
            'y': location.get('y', 0.0),
            'danger_level': danger_config.get('danger_level', 0),
            'encounter_rate': danger_config.get('encounter_rate', 0.0),
            'wandering_chance': danger_config.get('wandering_chance', 0.0),
            'spawn_npcs': spawn_npcs,
            'exits': exits,
            'interactables': location.get('interactables', {})
        })
    except Exception as e:
        import traceback
        print(f"Error loading location detail: {e}\n{traceback.format_exc()}")
        return jsonify({'error': str(e)}), 500

@app.route('/api/editor/location', methods=['POST'])
@require_auth
def update_location():
    """Update or create a location"""
    try:
        data = request.get_json()
        location_id = data.get('id')
        
        print(f"[DEBUG] Received location data: {json.dumps(data, indent=2)}")
        
        # Load or create config
        config = {}
        if LOCATIONS_FILE.exists():
            with open(LOCATIONS_FILE, 'r') as f:
                config = json.load(f)
        
        # Ensure proper structure - locations is a list, others are dicts
        if 'locations' not in config or not isinstance(config['locations'], list):
            config['locations'] = []
        if 'danger_config' not in config or not isinstance(config['danger_config'], dict):
            config['danger_config'] = {}
        if 'spawn_config' not in config or not isinstance(config['spawn_config'], dict):
            config['spawn_config'] = {}
        
        # Find or create location in the list
        location_index = None
        for i, loc in enumerate(config['locations']):
            if loc.get('id') == location_id:
                location_index = i
                break
        
        location_data = {
            'id': location_id,
            'name': data.get('name'),
            'description': data.get('description'),
            'image_path': data.get('image_path'),
            'x': data.get('x'),
            'y': data.get('y'),
            'interactables': data.get('interactables', {})
        }
        
        if location_index is not None:
            # Update existing location
            config['locations'][location_index] = location_data
        else:
            # Add new location
            config['locations'].append(location_data)
        
        # Update danger config
        config['danger_config'][location_id] = {
            'danger_level': data.get('danger_level', 0),
            'encounter_rate': data.get('encounter_rate', 0.0),
            'wandering_chance': data.get('wandering_chance', 0.0)
        }
        
        # Update spawn config
        spawn_npcs = data.get('spawn_npcs', [])
        print(f"[DEBUG] spawn_npcs type: {type(spawn_npcs)}, value: {spawn_npcs}")
        
        if spawn_npcs and isinstance(spawn_npcs, list):
            config['spawn_config'][location_id] = [
                {'npc_id': npc.get('npc_id', npc) if isinstance(npc, dict) else npc, 
                 'weight': npc.get('weight', 50) if isinstance(npc, dict) else 50}
                for npc in spawn_npcs
            ]
        else:
            config['spawn_config'][location_id] = []
        
        # Save config
        with open(LOCATIONS_FILE, 'w') as f:
            json.dump(config, f, indent=2)
        
        return jsonify({'success': True, 'message': 'Location updated successfully'})
    except Exception as e:
        import traceback
        error_msg = f"[ERROR] Failed to save location: {e}\n{traceback.format_exc()}"
        print(error_msg, flush=True)
        # Also write to a file for debugging
        try:
            with open('/tmp/location_save_error.txt', 'w') as f:
                f.write(error_msg)
                f.write(f"\n\nReceived data: {json.dumps(data, indent=2)}")
        except:
            pass
        return jsonify({'error': str(e)}), 500

@app.route('/api/editor/location/<location_id>', methods=['DELETE'])
@require_auth
def delete_location(location_id):
    """Delete a location from config"""
    try:
        if not LOCATIONS_FILE.exists():
            return jsonify({'error': 'No config file found'}), 404
        
        with open(LOCATIONS_FILE, 'r') as f:
            config = json.load(f)
        
        # Remove location from locations array
        if 'locations' in config:
            config['locations'] = [loc for loc in config['locations'] if loc.get('id') != location_id]
        
        # Remove from connections array (both from and to)
        if 'connections' in config:
            config['connections'] = [
                conn for conn in config['connections'] 
                if conn.get('from') != location_id and conn.get('to') != location_id
            ]
        
        # Remove from danger_config
        if 'danger_config' in config and location_id in config['danger_config']:
            del config['danger_config'][location_id]
        
        # Remove from spawn_config
        if 'spawn_config' in config and location_id in config['spawn_config']:
            del config['spawn_config'][location_id]
        
        with open(LOCATIONS_FILE, 'w') as f:
            json.dump(config, f, indent=2)
        
        return jsonify({'success': True, 'message': 'Location deleted successfully'})
    except Exception as e:
        return jsonify({'error': str(e)}), 500

@app.route('/api/editor/available-npcs', methods=['GET'])
@require_auth
def get_available_npcs():
    """Get list of all available NPCs for spawning"""
    try:
        # Load directly from JSON to get latest data
        npcs_data = load_json_file(NPCS_FILE)
        
        if not npcs_data:
            return jsonify({'npcs': []})
        
        npcs = []
        for npc_id, npc_def in npcs_data.get('npcs', {}).items():
            npcs.append({
                'id': npc_id,
                'name': npc_def.get('name', npc_id),
                'emoji': npc_def.get('emoji', '👹'),
                'hp_range': [npc_def.get('hp_min', 10), npc_def.get('hp_max', 20)],
                'damage_range': [npc_def.get('damage_min', 1), npc_def.get('damage_max', 5)],
                'xp_reward': npc_def.get('xp_reward', 10)
            })
        
        return jsonify({'npcs': npcs})
    except Exception as e:
        import traceback
        print(f"Error loading available NPCs: {e}\n{traceback.format_exc()}")
        return jsonify({'error': str(e)}), 500

@app.route('/api/editor/upload-image', methods=['POST'])
@require_auth
def upload_image():
    """Upload a location image"""
    try:
        if 'image' not in request.files:
            return jsonify({'error': 'No image file provided'}), 400
        
        file = request.files['image']
        if file.filename == '':
            return jsonify({'error': 'No selected file'}), 400
        
        if file and allowed_file(file.filename):
            filename = secure_filename(file.filename)
            filepath = UPLOAD_FOLDER / filename
            file.save(str(filepath))
            
            # Return relative path
            relative_path = f'images/locations/{filename}'
            return jsonify({
                'success': True,
                'image_path': relative_path,
                'message': f'Image uploaded successfully: {filename}'
            })
        else:
            return jsonify({'error': 'Invalid file type'}), 400
    except Exception as e:
        return jsonify({'error': str(e)}), 500

@app.route('/api/editor/export-config', methods=['GET'])
@require_auth
def export_config():
    """Export current config as downloadable JSON"""
    try:
        if LOCATIONS_FILE.exists():
            with open(LOCATIONS_FILE, 'r') as f:
                config = json.load(f)
            return jsonify(config)
        else:
            return jsonify({'locations': {}, 'danger_config': {}, 'spawn_config': {}})
    except Exception as e:
        return jsonify({'error': str(e)}), 500

@app.route('/api/editor/import-config', methods=['POST'])
@require_auth
def import_config():
    """Import config from JSON"""
    try:
        config = request.get_json()
        with open(LOCATIONS_FILE, 'w') as f:
            json.dump(config, f, indent=2)
        return jsonify({'success': True, 'message': 'Configuration imported successfully'})
    except Exception as e:
        return jsonify({'error': str(e)}), 500

@app.route('/api/editor/connections', methods=['GET'])
@require_auth
def get_connections():
    """Get all connections between locations"""
    try:
        import math
        
        # Load directly from JSON file to get latest data
        locations_data = load_json_file(LOCATIONS_FILE)
        if not locations_data:
            return jsonify({'connections': []})
        
        # Build location lookup for calculating distances
        locations_by_id = {}
        for loc in locations_data.get('locations', []):
            locations_by_id[loc['id']] = loc
        
        connections = []
        for conn in locations_data.get('connections', []):
            from_id = conn.get('from')
            to_id = conn.get('to')
            direction = conn.get('direction')
            
            from_loc = locations_by_id.get(from_id)
            to_loc = locations_by_id.get(to_id)
            
            if from_loc and to_loc:
                distance = math.sqrt((to_loc.get('x', 0) - from_loc.get('x', 0))**2 + 
                                   (to_loc.get('y', 0) - from_loc.get('y', 0))**2)
                connections.append({
                    'from': from_id,
                    'to': to_id,
                    'direction': direction,
                    'distance': round(distance, 2)
                })
        
        return jsonify({'connections': connections})
    except Exception as e:
        import traceback
        print(f"Error loading connections: {e}\n{traceback.format_exc()}")
        return jsonify({'error': str(e)}), 500

@app.route('/api/editor/connection', methods=['POST'])
@require_auth
def add_connection():
    """Add a connection between two locations"""
    try:
        data = request.get_json()
        from_id = data.get('from')
        to_id = data.get('to')
        direction = data.get('direction')
        
        # Load or create config
        config = {}
        if LOCATIONS_FILE.exists():
            with open(LOCATIONS_FILE, 'r') as f:
                config = json.load(f)
        
        if 'connections' not in config:
            config['connections'] = []
        
        # Add connection
        config['connections'].append({
            'from': from_id,
            'to': to_id,
            'direction': direction
        })
        
        with open(LOCATIONS_FILE, 'w') as f:
            json.dump(config, f, indent=2)
        
        return jsonify({'success': True, 'message': 'Connection added successfully'})
    except Exception as e:
        return jsonify({'error': str(e)}), 500

@app.route('/api/editor/connection', methods=['DELETE'])
@require_auth
def delete_connection():
    """Delete a connection between two locations"""
    try:
        data = request.get_json()
        from_id = data.get('from')
        to_id = data.get('to')
        
        if not LOCATIONS_FILE.exists():
            return jsonify({'error': 'No config file found'}), 404
        
        with open(LOCATIONS_FILE, 'r') as f:
            config = json.load(f)
        
        if 'connections' in config:
            config['connections'] = [
                conn for conn in config['connections']
                if not (conn['from'] == from_id and conn['to'] == to_id)
            ]
        
        with open(LOCATIONS_FILE, 'w') as f:
            json.dump(config, f, indent=2)
        
        return jsonify({'success': True, 'message': 'Connection deleted successfully'})
    except Exception as e:
        return jsonify({'error': str(e)}), 500

# ==================== NPC MANAGEMENT ====================

@app.route('/api/editor/npcs', methods=['GET'])
@require_auth
def get_npcs():
    """Get all NPCs"""
    try:
        npcs_data = load_json_file(NPCS_FILE)
        if not npcs_data:
            return jsonify({'npcs': []})
        
        npcs = []
        for npc_id, npc_def in npcs_data.get('npcs', {}).items():
            npcs.append({
                'id': npc_id,
                'name': npc_def.get('name', npc_id),
                'emoji': npc_def.get('emoji', '👹'),
                'hp_min': npc_def.get('hp_min', 10),
                'hp_max': npc_def.get('hp_max', 20),
                'damage_min': npc_def.get('damage_min', 1),
                'damage_max': npc_def.get('damage_max', 5),
                'xp_reward': npc_def.get('xp_reward', 10),
                'defense': npc_def.get('defense', 0),
                'description': npc_def.get('description', ''),
                'image_url': npc_def.get('image_url', ''),
                'loot_table': npc_def.get('loot_table', []),
                'corpse_loot': npc_def.get('corpse_loot', [])
            })
        
        return jsonify({'npcs': npcs})
    except Exception as e:
        return jsonify({'error': str(e)}), 500

@app.route('/api/editor/npc', methods=['POST'])
@require_auth
def save_npc():
    """Save or update an NPC"""
    try:
        npc_data = request.get_json()
        npc_id = npc_data.get('id')
        
        # Load current NPCs
        npcs_data = load_json_file(NPCS_FILE) or {'npcs': {}, 'danger_levels': {}, 'spawn_tables': {}}
        
        if 'npcs' not in npcs_data:
            npcs_data['npcs'] = {}
        
        # Update NPC
        npcs_data['npcs'][npc_id] = {
            'name': npc_data.get('name'),
            'emoji': npc_data.get('emoji'),
            'hp_min': npc_data.get('hp_min'),
            'hp_max': npc_data.get('hp_max'),
            'damage_min': npc_data.get('damage_min'),
            'damage_max': npc_data.get('damage_max'),
            'xp_reward': npc_data.get('xp_reward'),
            'defense': npc_data.get('defense', 0),
            'description': npc_data.get('description', ''),
            'image_url': npc_data.get('image_url', ''),
            'loot_table': npc_data.get('loot_table', []),
            'corpse_loot': npc_data.get('corpse_loot', [])
        }
        
        # Save back
        if save_json_file(NPCS_FILE, npcs_data):
            return jsonify({'success': True, 'message': 'NPC saved successfully'})
        else:
            return jsonify({'error': 'Failed to save NPC'}), 500
    except Exception as e:
        return jsonify({'error': str(e)}), 500

@app.route('/api/editor/npc/<npc_id>', methods=['DELETE'])
@require_auth
def delete_npc(npc_id):
    """Delete an NPC"""
    try:
        npcs_data = load_json_file(NPCS_FILE)
        if not npcs_data or 'npcs' not in npcs_data:
            return jsonify({'error': 'NPCs file not found'}), 404
        
        if npc_id in npcs_data['npcs']:
            del npcs_data['npcs'][npc_id]
        
        if save_json_file(NPCS_FILE, npcs_data):
            return jsonify({'success': True, 'message': 'NPC deleted successfully'})
        else:
            return jsonify({'error': 'Failed to delete NPC'}), 500
    except Exception as e:
        return jsonify({'error': str(e)}), 500

# ==================== ITEM MANAGEMENT ====================

@app.route('/api/editor/items', methods=['GET'])
@require_auth
def get_items():
    """Get all items"""
    try:
        items_data = load_json_file(ITEMS_FILE)
        if not items_data:
            return jsonify({'items': []})
        
        items = []
        for item_id, item_def in items_data.get('items', {}).items():
            items.append({
                'id': item_id,
                'name': item_def.get('name', item_id),
                'emoji': item_def.get('emoji', ''),
                'type': item_def.get('type', 'resource'),
                'weight': item_def.get('weight', 0.1),
                'volume': item_def.get('volume', 0.1),
                'description': item_def.get('description', ''),
                'stackable': item_def.get('stackable', True),
                'hp_restore': item_def.get('hp_restore', 0),
                'stamina_restore': item_def.get('stamina_restore', 0),
                'damage': item_def.get('damage', 0),
                'defense': item_def.get('defense', 0),
                'capacity_weight': item_def.get('capacity_weight', 0),
                'capacity_volume': item_def.get('capacity_volume', 0)
            })
        
        return jsonify({'items': items})
    except Exception as e:
        return jsonify({'error': str(e)}), 500

@app.route('/api/editor/item', methods=['POST'])
@require_auth
def save_item():
    """Save or update an item"""
    try:
        item_data = request.get_json()
        item_id = item_data.get('id')
        
        # Load current items
        items_data = load_json_file(ITEMS_FILE) or {'items': {}}
        
        if 'items' not in items_data:
            items_data['items'] = {}
        
        # Build item dict with required fields
        item_entry = {
            'name': item_data.get('name'),
            'type': item_data.get('type'),
            'weight': item_data.get('weight'),
            'volume': item_data.get('volume'),
        }
        
        # Add optional fields only if they have values
        if item_data.get('emoji'):
            item_entry['emoji'] = item_data.get('emoji')
        
        if item_data.get('description'):
            item_entry['description'] = item_data.get('description')
        
        # stackable defaults to True, only include if explicitly set
        stackable = item_data.get('stackable', True)
        if not stackable:
            item_entry['stackable'] = False
        
        # Only include numeric properties if they're non-zero
        if item_data.get('hp_restore', 0) != 0:
            item_entry['hp_restore'] = item_data.get('hp_restore')
        
        if item_data.get('stamina_restore', 0) != 0:
            item_entry['stamina_restore'] = item_data.get('stamina_restore')
        
        if item_data.get('damage', 0) != 0:
            item_entry['damage'] = item_data.get('damage')
        
        if item_data.get('defense', 0) != 0:
            item_entry['defense'] = item_data.get('defense')
        
        if item_data.get('capacity_weight', 0) != 0:
            item_entry['capacity_weight'] = item_data.get('capacity_weight')
        
        if item_data.get('capacity_volume', 0) != 0:
            item_entry['capacity_volume'] = item_data.get('capacity_volume')
        
        # Update item
        items_data['items'][item_id] = item_entry
        
        # Save back
        if save_json_file(ITEMS_FILE, items_data):
            return jsonify({'success': True, 'message': 'Item saved successfully'})
        else:
            return jsonify({'error': 'Failed to save item'}), 500
    except Exception as e:
        return jsonify({'error': str(e)}), 500

@app.route('/api/editor/item/<item_id>', methods=['DELETE'])
@require_auth
def delete_item(item_id):
    """Delete an item"""
    try:
        items_data = load_json_file(ITEMS_FILE)
        if not items_data or 'items' not in items_data:
            return jsonify({'error': 'Items file not found'}), 404
        
        if item_id in items_data['items']:
            del items_data['items'][item_id]
        
        if save_json_file(ITEMS_FILE, items_data):
            return jsonify({'success': True, 'message': 'Item deleted successfully'})
        else:
            return jsonify({'error': 'Failed to delete item'}), 500
    except Exception as e:
        return jsonify({'error': str(e)}), 500

# ==================== INTERACTABLES ENDPOINTS ====================

@app.route('/api/editor/interactables', methods=['GET'])
@require_auth
def get_interactables():
    """Get all interactable templates"""
    try:
        interactables_data = load_json_file(INTERACTABLES_FILE)
        if not interactables_data:
            return jsonify({'interactables': []})
        
        interactables = []
        for inter_id, inter_def in interactables_data.get('interactables', {}).items():
            interactables.append({
                'id': inter_id,
                'name': inter_def.get('name', inter_id),
                'description': inter_def.get('description', ''),
                'image_path': inter_def.get('image_path', ''),
                'actions': inter_def.get('actions', {})
            })
        
        return jsonify({'interactables': interactables})
    except Exception as e:
        return jsonify({'error': str(e)}), 500

@app.route('/api/editor/interactable', methods=['POST'])
@require_auth
def save_interactable():
    """Save or update an interactable template"""
    try:
        inter_data = request.get_json()
        inter_id = inter_data.get('id')
        
        # Load current interactables
        interactables_data = load_json_file(INTERACTABLES_FILE) or {'interactables': {}}
        
        if 'interactables' not in interactables_data:
            interactables_data['interactables'] = {}
        
        # Update interactable
        interactables_data['interactables'][inter_id] = {
            'id': inter_id,
            'name': inter_data.get('name'),
            'description': inter_data.get('description', ''),
            'image_path': inter_data.get('image_path', ''),
            'actions': inter_data.get('actions', {})
        }
        
        # Save back
        if save_json_file(INTERACTABLES_FILE, interactables_data):
            return jsonify({'success': True, 'message': 'Interactable saved successfully'})
        else:
            return jsonify({'error': 'Failed to save interactable'}), 500
    except Exception as e:
        return jsonify({'error': str(e)}), 500

@app.route('/api/editor/interactable/<inter_id>', methods=['DELETE'])
@require_auth
def delete_interactable(inter_id):
    """Delete an interactable template"""
    try:
        interactables_data = load_json_file(INTERACTABLES_FILE)
        if not interactables_data or 'interactables' not in interactables_data:
            return jsonify({'error': 'Interactables file not found'}), 404
        
        if inter_id in interactables_data['interactables']:
            del interactables_data['interactables'][inter_id]
        
        if save_json_file(INTERACTABLES_FILE, interactables_data):
            return jsonify({'success': True, 'message': 'Interactable deleted successfully'})
        else:
            return jsonify({'error': 'Failed to delete interactable'}), 500
    except Exception as e:
        return jsonify({'error': str(e)}), 500

@app.route('/api/editor/live-stats', methods=['GET'])
@require_auth
def get_live_stats():
    """Get real-time player locations and enemy counts"""
    # Try to import database module - may not be available in map container
    sys.path.insert(0, str(PARENT_DIR))
    
    try:
        from bot import database
    except ImportError:
        # Bot module not available in this container
        # Return empty stats - this is expected in map-only container
        return jsonify({
            'players_by_location': {},
            'enemies_by_location': {}
        })
    
    import asyncio
    from sqlalchemy import text
    
    # Run async queries
    async def get_stats():
        players_by_location = {}
        enemies_by_location = {}
        
        # Get all players and their locations
        try:
            async with database.engine.connect() as conn:
                # Get player counts per location
                result = await conn.execute(text(
                    "SELECT location_id, COUNT(*) as count FROM players GROUP BY location_id"
                ))
                rows = result.fetchall()
                for row in rows:
                    players_by_location[row[0]] = row[1]
                
                # Get wandering enemy counts per location
                result = await conn.execute(text(
                    "SELECT location_id, COUNT(*) as count FROM wandering_enemies WHERE despawn_timestamp > EXTRACT(EPOCH FROM CURRENT_TIMESTAMP) GROUP BY location_id"
                ))
                rows = result.fetchall()
                for row in rows:
                    enemies_by_location[row[0]] = row[1]
        except Exception as e:
            print(f"[LIVE-STATS] Database query error: {e}", flush=True)
            import traceback
            traceback.print_exc()
        
        return players_by_location, enemies_by_location
    
    # Run the async function
    try:
        players, enemies = asyncio.run(get_stats())
        #print(f"[LIVE-STATS] Success! Players: {players}, Enemies: {enemies}", flush=True)
    except Exception as e:
        #print(f"[LIVE-STATS] asyncio.run error: {e}", flush=True)
        import traceback
        traceback.print_exc()
        return jsonify({'error': str(e), 'players_by_location': {}, 'enemies_by_location': {}}), 200
    
    return jsonify({
        'players_by_location': players,
        'enemies_by_location': enemies
    })

# Export to JSON endpoint removed to prevent accidental data loss
# The editor now works directly with JSON files

@app.route('/api/editor/export-python', methods=['GET'])
@require_auth
def export_python():
    """Export configuration as Python code to update world_loader.py"""
    try:
        if not LOCATIONS_FILE.exists():
            return jsonify({'error': 'No configuration to export'}), 404
        
        with open(LOCATIONS_FILE, 'r') as f:
            config = json.load(f)
        
        # Generate Python code
        python_code = generate_world_loader_code(config)
        
        return jsonify({
            'success': True,
            'python_code': python_code,
            'message': 'Python code generated. Review and manually update world_loader.py'
        })
    except Exception as e:
        return jsonify({'error': str(e)}), 500

def generate_world_loader_code(config):
    """Generate Python code from config"""
    code_lines = ["# Generated configuration updates\n", "# REVIEW CAREFULLY before applying!\n\n"]
    
    # Generate location updates
    if 'locations' in config:
        code_lines.append("# ===== LOCATION UPDATES =====\n")
        for loc_id, loc_data in config['locations'].items():
            code_lines.append(f"\n# Update {loc_id}\n")
            code_lines.append(f"{loc_id}.name = {repr(loc_data['name'])}\n")
            code_lines.append(f"{loc_id}.description = {repr(loc_data['description'])}\n")
            code_lines.append(f"{loc_id}.x = {loc_data['x']}\n")
            code_lines.append(f"{loc_id}.y = {loc_data['y']}\n")
            if loc_data.get('image_path'):
                code_lines.append(f"{loc_id}.image_path = {repr(loc_data['image_path'])}\n")
    
    # Generate danger config
    if 'danger_config' in config:
        code_lines.append("\n# ===== DANGER CONFIG (for npcs.py) =====\n")
        code_lines.append("LOCATION_DANGER = {\n")
        for loc_id, danger_data in config['danger_config'].items():
            code_lines.append(f"    {repr(loc_id)}: ({danger_data['danger_level']}, {danger_data['encounter_rate']}, {danger_data['wandering_chance']}),\n")
        code_lines.append("}\n")
    
    # Generate spawn config
    if 'spawn_config' in config:
        code_lines.append("\n# ===== SPAWN CONFIG (for npcs.py) =====\n")
        code_lines.append("LOCATION_SPAWNS = {\n")
        for loc_id, spawns in config['spawn_config'].items():
            code_lines.append(f"    {repr(loc_id)}: [\n")
            for spawn in spawns:
                code_lines.append(f"        ({repr(spawn['npc_id'])}, {spawn['weight']}),\n")
            code_lines.append("    ],\n")
        code_lines.append("}\n")
    
    # Generate connections
    if 'connections' in config:
        code_lines.append("\n# ===== CONNECTIONS =====\n")
        for conn in config['connections']:
            code_lines.append(f"{conn['from']}.add_exit({repr(conn['direction'])}, {repr(conn['to'])})\n")
    
    return ''.join(code_lines)

if __name__ == '__main__':
    port = int(os.environ.get('PORT', 8080))
    print(f"""
╔════════════════════════════════════════════╗
║   Echoes of the Ashes - Map Server        ║
║              WITH EDITOR                   ║
╚════════════════════════════════════════════╝

🗺️  Map Viewer: http://localhost:{port}
✏️  Map Editor:  http://localhost:{port}/editor
🔐  Password:    {ADMIN_PASSWORD}

⚠️  IMPORTANT: Set EDITOR_PASSWORD environment variable in production!
""")
    app.run(host='0.0.0.0', port=port, debug=False)
