"""
Echoes of the Ashes - Main FastAPI Application
Streamlined and modular architecture for easy maintenance
"""
from fastapi import FastAPI, WebSocket, WebSocketDisconnect, Depends, HTTPException
from fastapi.middleware.cors import CORSMiddleware
from fastapi.staticfiles import StaticFiles
from fastapi.security import HTTPAuthorizationCredentials
from contextlib import asynccontextmanager
from pathlib import Path
from datetime import datetime
import logging

# Import core modules
from .core.config import CORS_ORIGINS, IMAGES_DIR, API_INTERNAL_KEY
from .core.websockets import manager
from .core.security import get_current_user, decode_token, security, verify_internal_key

# Import database and game data
from . import database as db
from .world_loader import load_world, World, Location
from .items import ItemsManager
from . import background_tasks
from .redis_manager import redis_manager

# Import all routers
from .routers import (
    auth,
    characters,
    game_routes,
    combat,
    equipment,
    crafting,
    loot,
    statistics,
    statistics,
    admin,
    quests,
    trade,
    npcs
)

# Configure logging
logging.basicConfig(
    level=logging.INFO,
    format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
)
logger = logging.getLogger(__name__)

# Load game data
print("🔄 Loading game world...")
WORLD: World = load_world()
LOCATIONS = WORLD.locations
ITEMS_MANAGER = ItemsManager()
print(f"✅ Game world ready: {len(LOCATIONS)} locations, {len(ITEMS_MANAGER.items)} items")


@asynccontextmanager
async def lifespan(app: FastAPI):
    """Application lifespan manager for startup/shutdown"""
    # Startup
    await db.init_db()
    print("✅ Database initialized")
    
    # Connect to Redis
    await redis_manager.connect()
    print("✅ Redis connected")
    
    # Inject Redis manager into ConnectionManager
    manager.set_redis_manager(redis_manager)
    
    # Subscribe to all location channels + global broadcast
    location_channels = [f"location:{loc_id}" for loc_id in LOCATIONS.keys()]
    await redis_manager.subscribe_to_channels(location_channels + ['game:broadcast'])
    print(f"✅ Subscribed to {len(location_channels)} location channels")
    
    # Register this worker
    await redis_manager.register_worker()
    print(f"✅ Worker registered: {redis_manager.worker_id}")
    
    # Start Redis message listener (background task)
    redis_manager.start_listener(manager.handle_redis_message)
    print("✅ Redis listener started")
    
    # Start background tasks (distributed via Redis locks)
    tasks = await background_tasks.start_background_tasks(manager, LOCATIONS, NPCS_DATA)
    if tasks:
        print(f"✅ Started {len(tasks)} background tasks in this worker")
    else:
        print("⏭️  Background tasks running in another worker")

    # APPLY GLOBAL QUEST UNLOCKS
    print("🔓 Applying global quest unlocks...")
    try:
        completed_quests = await db.get_completed_global_quests()
        print(f"  - Found {len(completed_quests)} completed global quests")
        for quest_id in completed_quests:
            # Unlock locations
            for loc in LOCATIONS.values():
                if loc.unlocked_by == quest_id:
                    loc.locked = False
                    print(f"  - Unlocked location: {loc.id}")
                
                # Unlock interactables
                for inter in loc.interactables:
                    if inter.unlocked_by == quest_id:
                        inter.locked = False
                        print(f"  - Unlocked interactable: {inter.id} in {loc.id}")
    except Exception as e:
        print(f"❌ Failed to apply global quest unlocks: {e}")
    
    yield
    
    # Shutdown
    await background_tasks.stop_background_tasks(tasks)
    
    # Unregister worker
    await redis_manager.unregister_worker()
    print(f"🔌 Worker unregistered: {redis_manager.worker_id}")
    
    # Disconnect from Redis
    await redis_manager.disconnect()
    print("✅ Redis disconnected")


# Initialize FastAPI app
app = FastAPI(
    title="Echoes of the Ashes API",
    version="2.0.0",
    description="Post-apocalyptic survival RPG - Modular Architecture",
    lifespan=lifespan
)

# CORS middleware
app.add_middleware(
    CORSMiddleware,
    allow_origins=CORS_ORIGINS,
    allow_credentials=True,
    allow_methods=["*"],
    allow_headers=["*"],
)

# Mount static files for images
if IMAGES_DIR.exists():
    app.mount("/images", StaticFiles(directory=str(IMAGES_DIR)), name="images")
    print(f"✅ Mounted images directory: {IMAGES_DIR}")
else:
    print(f"⚠️  Images directory not found: {IMAGES_DIR}")

# Initialize routers with game data dependencies
# Load Quests and NPCs Data at startup
QUESTS_DATA = {}
NPCS_DATA = {}

try:
    print("🔄 Loading quests and NPCs...")
    quests_path = Path("./gamedata/quests.json")
    npcs_path = Path("./gamedata/static_npcs.json")
    
    import json
    if quests_path.exists():
        with open(quests_path, "r") as f:
            q_data = json.load(f)
            QUESTS_DATA = q_data.get("quests", {})
            print(f"✅ Loaded {len(QUESTS_DATA)} quests")
    
    if npcs_path.exists():
        with open(npcs_path, "r") as f:
            n_data = json.load(f)
            NPCS_DATA = n_data.get("static_npcs", {})
            print(f"✅ Loaded {len(NPCS_DATA)} static NPCs")
            
    # Load Enemies / Other NPCs
    enemies_path = Path("./gamedata/npcs.json")
    if enemies_path.exists():
        with open(enemies_path, "r") as f:
            e_data = json.load(f)
            enemies = e_data.get("npcs", {})
            # Merge into NPCS_DATA
            NPCS_DATA.update(enemies)
            print(f"✅ Loaded {len(enemies)} enemies/NPCs")
            
except Exception as e:
    print(f"❌ Error loading game data: {e}")

# Initialize routers with game data dependencies
game_routes.init_router_dependencies(LOCATIONS, ITEMS_MANAGER, WORLD, redis_manager)
combat.init_router_dependencies(LOCATIONS, ITEMS_MANAGER, WORLD, redis_manager, QUESTS_DATA)
equipment.init_router_dependencies(LOCATIONS, ITEMS_MANAGER, WORLD, redis_manager)
crafting.init_router_dependencies(LOCATIONS, ITEMS_MANAGER, WORLD)
loot.init_router_dependencies(LOCATIONS, ITEMS_MANAGER, WORLD)
statistics.init_router_dependencies(LOCATIONS, ITEMS_MANAGER, WORLD)
admin.init_router_dependencies(LOCATIONS, ITEMS_MANAGER, WORLD, IMAGES_DIR)
quests.init_router_dependencies(ITEMS_MANAGER, QUESTS_DATA, NPCS_DATA, LOCATIONS)
trade.init_router_dependencies(ITEMS_MANAGER, NPCS_DATA)
npcs.init_router_dependencies()

# Include all routers
app.include_router(auth.router)
app.include_router(characters.router)
app.include_router(game_routes.router)
app.include_router(combat.router)
app.include_router(equipment.router)
app.include_router(crafting.router)
app.include_router(loot.router)
app.include_router(statistics.router)
app.include_router(admin.router)
app.include_router(quests.router)
app.include_router(trade.router)
app.include_router(npcs.router)

print("✅ All routers registered")


@app.get("/health")
async def health_check():
    """Health check endpoint for load balancers"""
    return {"status": "ok", "version": "2.0.0"}


@app.websocket("/ws/game/{token}")
async def websocket_endpoint(websocket: WebSocket, token: str):
    """
    WebSocket endpoint for real-time game updates.
    Clients connect with their JWT token in the path.
    """
    
    try:
        # Decode and validate token
        payload = decode_token(token)
        character_id = payload.get("character_id")
        
        if not character_id:
            await websocket.close(code=1008, reason="No character selected")
            return
        
        # Get character data
        character = await db.get_player_by_id(character_id)
        if not character:
            await websocket.close(code=1008, reason="Character not found")
            return
        
        player_id = character['id']
        username = character['name']
        location_id = character['location_id']
        
        # Connect WebSocket
        await manager.connect(websocket, player_id, username)
        
        # Register in Redis
        if redis_manager:
            await redis_manager.set_player_session(player_id, {
                'username': username,
                'location_id': location_id,
                'hp': character.get('hp'),
                'max_hp': character.get('max_hp'),
                'stamina': character.get('stamina'),
                'max_stamina': character.get('max_stamina'),
                'level': character.get('level', 1),
                'xp': character.get('xp', 0),
                'websocket_connected': 'true'
            })
            
            # Add player to location registry
            await redis_manager.add_player_to_location(player_id, location_id)

            # Increment connected player count
            await redis_manager.increment_connected_player(player_id)
            
            # Broadcast new player count
            count = await redis_manager.get_connected_player_count()
            await redis_manager.publish_global_broadcast({
                "type": "player_count_update",
                "data": { "count": count }
            })
        
        logger.info(f"WebSocket connected: {username} (ID: {player_id})")

        
        # Keep connection alive
        while True:
            try:
                data_text = await websocket.receive_text()
                try:
                    data_json = json.loads(data_text)
                    if data_json.get("type") == "ack":
                        logger.debug(f"ACK received from {username} for msg {data_json.get('reply_to')}")
                    else:
                        logger.debug(f"Received from {username}: {data_text}")
                except:
                    logger.debug(f"Received from {username}: {data_text}")
            except WebSocketDisconnect:
                break
            except Exception as e:
                logger.error(f"WebSocket error for {username}: {e}")
                break
    
    except HTTPException as e:
        await websocket.close(code=1008, reason=e.detail)
        return
    except Exception as e:
        logger.error(f"WebSocket connection error: {e}")
        await websocket.close(code=1011, reason="Internal error")
        return
    finally:
        # Cleanup on disconnect
        try:
            await manager.disconnect(player_id, websocket)
            
            if location_id and redis_manager:
                await redis_manager.remove_player_from_location(player_id, location_id)
                
                # Decrement connected player count
                await redis_manager.decrement_connected_player(player_id)
                
                # Broadcast new player count
                count = await redis_manager.get_connected_player_count()
                await redis_manager.publish_global_broadcast({
                    "type": "player_count_update",
                    "data": { "count": count }
                })
            
            logger.info(f"WebSocket disconnected: {username}")
        except:
            pass


print("\n" + "="*60)
print("✅ Echoes of the Ashes API - Ready")
print(f"📊 Total Routers: 9 (auth, characters, game, combat, equipment, crafting, loot, statistics, admin)")
print(f"🌍 Locations: {len(LOCATIONS)}")
print(f"📦 Items: {len(ITEMS_MANAGER.items)}")
print("="*60 + "\n")
