from fastapi import APIRouter, Depends, HTTPException, Body
from typing import Dict, List, Any, Optional
import time
import json
import logging
from ..core.websockets import manager
from ..core.security import get_current_user
from .. import database as db
from .. import game_logic
from ..items import ItemsManager
from ..services.helpers import get_locale_string

router = APIRouter(
    prefix="/api/quests",
    tags=["quests"],
    responses={404: {"description": "Not found"}},
)

# Request Models
class HistoryParams:
    page: int = 1
    page_size: int = 20

logger = logging.getLogger(__name__)

# Dependencies
QUESTS_DATA = {}
NPCS_DATA = {}
LOCATIONS_DATA = {}

def init_router_dependencies(items_manager: ItemsManager, quests_data=None, npcs_data=None, locations_data=None):
    global ITEMS_MANAGER, QUESTS_DATA, NPCS_DATA, LOCATIONS_DATA
    ITEMS_MANAGER = items_manager
    if quests_data:
        QUESTS_DATA = quests_data
    if npcs_data:
        NPCS_DATA = npcs_data
    if locations_data:
        LOCATIONS_DATA = locations_data

@router.get("/history")
async def get_quest_history_endpoint(
    page: int = 1, 
    limit: int = 20, 
    current_user: dict = Depends(get_current_user)
):
    """Get completed quest history with pagination"""
    character_id = current_user['id']
    history = await db.get_quest_history(character_id, page=page, page_size=limit)
    
    # Enrich with quest definitions
    enriched_data = []
    for entry in history['data']:
        quest_def = QUESTS_DATA.get(entry['quest_id'])
        if quest_def:
             # Merge entry data with quest def
             item = dict(entry)
             item['title'] = quest_def.get('title')
             item['description'] = quest_def.get('description')
             item['type'] = quest_def.get('type')
             item['objectives'] = quest_def.get('objectives') # Fix: Copy objectives
             
             # Enrich with giver info
             if quest_def.get('giver_id'):
                 giver = NPCS_DATA.get(quest_def['giver_id'])
                 if giver:
                     item['giver_name'] = giver.get('name')
                     item['giver_image'] = giver.get('image')
                     
                     # Get Location Name
                     if giver.get('location_id'):
                         loc = LOCATIONS_DATA.get(giver['location_id'])
                         if loc:
                             item['giver_location_name'] = loc.name
                         else:
                             item['giver_location_name'] = giver['location_id']

             enriched_data.append(item)
        else:
             # Fallback if quest def removed?
             enriched_data.append(entry)

    # 2nd pass: Enrich objectives and rewards for all items in enriched_data
    final_data = []
    for q_data in enriched_data:
        # ENRICH OBJECTIVES WITH NAMES
        if 'objectives' in q_data:
            enriched_objs = []
            for obj in q_data['objectives']:
                new_obj = dict(obj)
                target = obj.get('target')
                if obj.get('type') == 'kill_count':
                    npc = NPCS_DATA.get(target)
                    if npc:
                         new_obj['target_name'] = npc.get('name')
                    else:
                         logger.warning(f"NPC not found for target: {target}")
                elif obj.get('type') == 'item_delivery':
                    item = ITEMS_MANAGER.get_item(target)
                    if item:
                         new_obj['target_name'] = item.name
                    else:
                         logger.warning(f"Item not found for target: {target}")
                enriched_objs.append(new_obj)
            q_data['objectives'] = enriched_objs

        # ENRICH REWARDS WITH NAMES
        # For history, rewards might be stored in 'rewards' json.
        if 'rewards' in q_data and 'items' in q_data['rewards']:
            enriched_items = {}
            for item_id, qty in q_data['rewards']['items'].items():
                item = ITEMS_MANAGER.get_item(item_id)
                name = item.name if item else item_id
                enriched_items[item_id] = {'qty': qty, 'name': name}
            
            q_data['reward_items_details'] = enriched_items
        
        final_data.append(q_data)

    history['data'] = final_data
    return history


@router.get("/active")
async def get_active_quests(current_user: dict = Depends(get_current_user)):
    """Get all active quests for the character"""
    character_id = current_user['id']
    quests = await db.get_character_quests(character_id)
    
    result = []
    for q in quests:
        quest_def = QUESTS_DATA.get(q['quest_id'])
        if not quest_def:
            continue
            
        # Enrich with static data
        q_data = dict(q)
        q_data['start_at'] = q['started_at'] # Consistency
        q_data.update(quest_def)
        
        # Calculate cooldown status for repeatable quests
        if quest_def.get('repeatable') and q['cooldown_expires_at']:
            if time.time() < q['cooldown_expires_at']:
                q_data['on_cooldown'] = True
                q_data['cooldown_remaining'] = int(q['cooldown_expires_at'] - time.time())
            else:
                q_data['on_cooldown'] = False
        
        # Global Quest Progress
        if quest_def.get('type') == 'global':
            g_quest = await db.get_global_quest(q['quest_id'])
            if g_quest:
                q_data['global_progress'] = g_quest.get('global_progress', {})
                q_data['global_is_completed'] = g_quest.get('is_completed', False)
        
        # Enrich with giver info
        if quest_def.get('giver_id'):
            giver = NPCS_DATA.get(quest_def['giver_id'])
            if giver:
                q_data['giver_name'] = giver.get('name')
                q_data['giver_image'] = giver.get('image')
                
                # Get Location Name
                if giver.get('location_id'):
                    loc = LOCATIONS_DATA.get(giver['location_id'])
                    if loc:
                        q_data['giver_location_name'] = loc.name
                    else:
                        q_data['giver_location_name'] = giver['location_id']

        # ENRICH OBJECTIVES WITH NAMES
        if 'objectives' in q_data:
            enriched_objs = []
            for obj in q_data['objectives']:
                new_obj = dict(obj)
                target = obj.get('target')
                if obj.get('type') == 'kill_count':
                    npc = NPCS_DATA.get(target)
                    if npc:
                         new_obj['target_name'] = npc.get('name')
                elif obj.get('type') == 'item_delivery':
                    item = ITEMS_MANAGER.get_item(target)
                    if item:
                         new_obj['target_name'] = item.name
                enriched_objs.append(new_obj)
            q_data['objectives'] = enriched_objs

        # ENRICH REWARDS WITH NAMES
        if 'rewards' in q_data and 'items' in q_data['rewards']:
            enriched_items = {}
            for item_id, qty in q_data['rewards']['items'].items():
                item = ITEMS_MANAGER.get_item(item_id)
                name = item.name if item else item_id
                enriched_items[item_id] = {'qty': qty, 'name': name}
            
            # Store back in a way frontend can use, or just replace items dict?
            # Frontend currently iterates entries of items.
            # Let's add a new field 'reward_items_details'
            q_data['reward_items_details'] = enriched_items

        result.append(q_data)
        
    return result

@router.get("/available")
async def get_available_quests(current_user: dict = Depends(get_current_user)):
    """Get quests available to be started at current location"""
    character_id = current_user['id']
    location_id = current_user['location_id']
    
    # 1. Identify NPCs at this location
    local_npcs = [
        npc_id for npc_id, npc in NPCS_DATA.items() 
        if npc.get('location_id') == location_id
    ]
    
    if not local_npcs:
        return []
        
    # 2. Get quests offered by these NPCs
    potential_quests = []
    for q_id, q_def in QUESTS_DATA.items():
        if q_def.get('giver_id') in local_npcs:
            potential_quests.append(q_def)
            
    # 3. Filter out active/completed non-repeatable quests
    # We need to check DB state
    available = []
    
    # Bulk fetch might be better but loop is fine for now
    for q_def in potential_quests:
        q_id = q_def['quest_id']
        existing = await db.get_character_quest(character_id, q_id)
        
        if not existing:
            # Never started -> Available
            available.append(q_def)
        else:
            # Exists
            if existing['status'] == 'active':
                continue # Already active
                
            if existing['status'] == 'completed':
                if q_def.get('repeatable'):
                    # Check cooldown
                    expires = existing.get('cooldown_expires_at')
                    if not expires or time.time() >= expires:
                        available.append(q_def)
                else:
                    continue # Completed and not repeatable
            
            if existing['status'] == 'failed':
                available.append(q_def) # Can retry?
                
    return available

@router.post("/accept/{quest_id}")
async def accept_quest(quest_id: str, current_user: dict = Depends(get_current_user)):
    """Accept a quest"""
    character_id = current_user['id']
    quest_def = QUESTS_DATA.get(quest_id)
    
    if not quest_def:
        raise HTTPException(status_code=404, detail="Quest not found")
    
    # Check if repeatable & cooldown
    existing = await db.get_character_quest(character_id, quest_id)
    if existing:
        if not quest_def.get('repeatable'):
             raise HTTPException(status_code=400, detail="Quest already completed or active")
        
        # Check cooldown
        if existing.get('cooldown_expires_at') and time.time() < existing['cooldown_expires_at']:
            remaining = int(existing['cooldown_expires_at'] - time.time())
            raise HTTPException(status_code=400, detail=f"Quest on cooldown for {remaining}s")
            
        if existing['status'] == 'active':
             raise HTTPException(status_code=400, detail="Quest already active")
             
    # Accept quest
    await db.accept_quest(character_id, quest_id)
    
    # Return updated quest data for frontend
    updated_q_data = dict(quest_def)
    updated_q_data['status'] = 'active'
    updated_q_data['start_at'] = int(time.time())
    updated_q_data['progress'] = {} # New quest
    
    return {"success": True, "message": "Quest accepted", "quest": updated_q_data}

@router.post("/hand_in/{quest_id}")
async def hand_in_quest(quest_id: str, current_user: dict = Depends(get_current_user)):
    """
    Hand in items or check completion for a quest.
    Automatically deducts items from inventory for delivery objectives.
    """
    character_id = current_user['id']
    quest_def = QUESTS_DATA.get(quest_id)
    
    if not quest_def:
        raise HTTPException(status_code=404, detail="Quest not found")
        
    quest_record = await db.get_character_quest(character_id, quest_id)
    if not quest_record or quest_record['status'] != 'active':
        raise HTTPException(status_code=400, detail="Quest not active")
        
    current_progress = quest_record.get('progress') or {}
    objectives = quest_def.get('objectives', [])
    
    updated_progress = current_progress.copy()
    items_deducted = []
    all_completed = True
    
    # Iterate objectives
    for obj in objectives:
        obj_type = obj['type']
        target = obj['target']
        required_count = obj['count']
        
        current_count = current_progress.get(target, 0)
        
        if current_count >= required_count:
            continue # Already done
            
        if obj_type == 'item_delivery':
            # Check inventory
            inventory = await db.get_inventory(character_id)
            inv_item = next((i for i in inventory if i['item_id'] == target), None)
            
            if inv_item:
                available = inv_item['quantity']
                needed = required_count - current_count # Personal needed (to match max count)
                
                # GLOBAL CAP CHECK
                is_global = quest_def.get('type') == 'global'
                if is_global:
                     global_quest = await db.get_global_quest(quest_id)
                     global_prog = global_quest.get('global_progress', {}) if global_quest else {}
                     global_current_val = global_prog.get(target, 0)
                     global_remaining = max(0, required_count - global_current_val)
                     
                     # Cap needed by global remaining
                     needed = min(needed, global_remaining)
                
                to_take = min(available, needed)
                
                if to_take > 0:
                    # Remove from inventory
                    await db.remove_item_from_inventory(character_id, target, to_take)
                    
                    # Update progress
                    new_count = current_count + to_take
                    updated_progress[target] = new_count
                    items_deducted.append(f"{target} x{to_take}")
                    
                    # Global Quest Logic
                    if is_global:
                        # Re-fetch or use existing? We need to be careful with race conditions slightly, 
                        # but safe enough for now to just update.
                        # We already fetched 'global_prog' above.
                        
                        # Add contribution
                        new_global = global_current_val + to_take
                        global_prog[target] = new_global
                        await db.update_global_quest(quest_id, global_prog)
                        
                        # Check for global completion
                        is_global_complete = True
                        for obj in objectives:
                            t = obj['target']
                            req = obj['count']
                            # Check cached updated prog
                            if global_prog.get(t, 0) < req:
                                is_global_complete = False
                                break
                        
                        if is_global_complete:
                             # Finish global quest!
                             await finish_global_quest(quest_id, quest_def)
                             
                             # RETURN IMMEDIATELY to prevent double rewards/deletion logic
                             # We construct a success response here.
                             return {
                                "success": True,
                                "message": "Global Quest Completed!",
                                "is_completed": True,
                                "items_deducted": items_deducted,
                                "rewards": ["See Global Rewards"], # Placeholders, real rewards via finish_global_quest/websocket
                                "completion_text": quest_def.get("completion_text", "Global Quest Finished!"),
                                "quest_update": {
                                    **quest_def,
                                    "quest_id": quest_id,
                                    "status": "completed",
                                    "progress": updated_progress,
                                    "on_cooldown": quest_def.get('repeatable'),
                                }
                             }
                        else:
                             # Prevent individual completion if global is not done
                             all_completed = False
                    
                    if new_count < required_count:
                        all_completed = False
                else:
                    all_completed = False
            else:
                all_completed = False
        
        elif obj_type == 'kill_count':
            # Check if kill count is met (updated via other events usually)
            if current_count < required_count:
                all_completed = False

    # WEIGHT CHECK FOR REWARDS
    rewards_msg = []
    if all_completed:
        rewards = quest_def.get('rewards', {})
        reward_weight = 0.0
        
        if 'items' in rewards:
            for item_id, qty in rewards['items'].items():
                 item_def = ITEMS_MANAGER.get_item(item_id)
                 if item_def:
                     reward_weight += item_def.weight * qty
        
        # Calculate current weight
        # Calculate current weight and capacity
        from ..services.helpers import calculate_player_capacity
        inventory = await db.get_inventory(character_id)
        current_weight, capacity, _, _ = await calculate_player_capacity(inventory, ITEMS_MANAGER)
        
        if current_weight + reward_weight > capacity:
             # Rollback? The items for delivery were already removed above!
             # Ideally we should check weight BEFORE deducting delivery items.
             # converting this to a "check before action" logic is hard because delivery logic is stateful.
             # However, delivery items REDUCE weight. So we are likely safe unless rewards are heavier than delivered items.
             # BUT, if we error here, we technically leave the quest in "partially delivered" state, which is fine.
             # The user can just clear inventory and try again.
             
             raise HTTPException(status_code=400, detail=f"Not enough inventory space for rewards! (Overweight by {current_weight + reward_weight - capacity:.1f})")

        # Give Rewards
        # XP
        if 'xp' in rewards:
            xp_gained = rewards['xp']
            new_xp = current_user['xp'] + xp_gained
            await db.update_player(character_id, xp=new_xp)
            rewards_msg.append(f"{xp_gained} XP")
            
            # Check for level up
            try:
                level_up_result = await game_logic.check_and_apply_level_up(character_id)
                if level_up_result and level_up_result.get('leveled_up'):
                    new_level = level_up_result['new_level']
                    stats_gained = level_up_result['levels_gained']
                    rewards_msg.append(f"Level Up! (Lvl {new_level}) +{stats_gained} Stat Points")
            except Exception as e:
                logger.error(f"Failed to check level up in quest hand-in: {e}")
            
        # Items
        if 'items' in rewards:
            for item_id, qty in rewards['items'].items():
                await db.add_item_to_inventory(character_id, item_id, qty)
                # Resolve name
                idev = ITEMS_MANAGER.get_item(item_id)
                name = idev.name if idev else item_id
                rewards_msg.append(f"{name} x{qty}")
        
        # Set cooldown if repeatable
        if quest_def.get('repeatable'):
             cooldown_hours = quest_def.get('cooldown_hours', 24)
             expires = time.time() + (cooldown_hours * 3600)
             await db.set_quest_cooldown(character_id, quest_id, expires)
             
        # LOG HISTORY
        await db.log_quest_completion(
            character_id=character_id,
            quest_id=quest_id,
            started_at=quest_record.get('started_at') or time.time(),
            rewards=quest_def.get('rewards', {})
        )
        
        # REMOVE FROM ACTIVE QUESTS (DELETE)
        await db.delete_character_quest(character_id, quest_id)
        
        status = "completed"
        
    else:
        # Not completed, just update progress
        status = "active"
        await db.update_quest_progress(character_id, quest_id, updated_progress, status)
              
    # ENRICH OBJECTIVES FOR RESPONSE
    enriched_objs = []
    for obj in objectives:
        new_obj = dict(obj)
        target = obj.get('target')
        
        # Add current progress
        new_obj['current'] = updated_progress.get(target, 0)
        
        # Add names
        if obj.get('type') == 'kill_count':
            npc = NPCS_DATA.get(target)
            if npc:
                    new_obj['target_name'] = npc.get('name')
        elif obj.get('type') == 'item_delivery':
            item = ITEMS_MANAGER.get_item(target)
            if item:
                    new_obj['target_name'] = item.name
        enriched_objs.append(new_obj)

    response = {
        "success": True,
        "progress": updated_progress,
        "is_completed": all_completed,
        "items_deducted": items_deducted,
        "message": "Progress updated",
        "quest_update": {
            **quest_def,
            "quest_id": quest_id,
            "status": status,
            "progress": updated_progress,
            "objectives": enriched_objs,
            "on_cooldown": all_completed and quest_def.get('repeatable'),
            # other fields as needed
        }
    }
    
    if all_completed:
        response["message"] = "Quest Completed!"
        response["rewards"] = rewards_msg
        response["completion_text"] = quest_def.get("completion_text", {})
        
    return response

# Also exposing global quest state
@router.get("/global/{quest_id}")
async def get_global_quest_progress(quest_id: str):
    quest = await db.get_global_quest(quest_id)
    if not quest:
        return {"progress": {}}
    return quest


async def finish_global_quest(quest_id: str, quest_def: Dict):
    """
    Handle global quest completion:
    1. Mark global quest as completed
    2. Unlock content (In-Memory)
    3. Distribute rewards to all participants
    4. Broadcast completion
    """
    logger.info(f"🌍 Finishing Global Quest: {quest_id}")
    
    # 1. Mark as completed in DB
    await db.mark_global_quest_completed(quest_id)
    
    # 2. Unlock content (In-Memory)
    unlocks = []
    
    # Unlock Locations
    for loc in LOCATIONS_DATA.values():
        if loc.unlocked_by == quest_id:
            loc.locked = False
            unlocks.append({"type": "location", "name": get_locale_string(loc.name, 'en'), "id": loc.id})
            
        # Unlock interactables
        for inter in loc.interactables:
            if inter.unlocked_by == quest_id:
                inter.locked = False
                unlocks.append({"type": "interactable", "name": get_locale_string(inter.name, 'en'), "location": loc.id})

    # 3. Distribute Rewards to participants
    participants = await db.get_all_quest_participants(quest_id)
    total_xp_pool = quest_def.get('rewards', {}).get('xp', 0)
    
    total_required = 0
    for obj in quest_def.get('objectives', []):
        total_required += obj.get('count', 0)
        
    for p in participants:
        # Calculate user contribution
        user_progress = p.get('progress', {})
        user_contribution = 0
        for obj in quest_def.get('objectives', []):
            target = obj['target']
            user_contribution += user_progress.get(target, 0)
            
        if user_contribution > 0 and total_required > 0:
            percentage = user_contribution / total_required
            xp_reward = int(total_xp_pool * percentage)
            
            if xp_reward > 0:
                # Give XP
                char = await db.get_player_by_id(p['character_id'])
                if char:
                    new_xp = char['xp'] + xp_reward
                    await db.update_player(p['character_id'], xp=new_xp)
                    
                    # Mark as completed (delete from active) and log history
                    await db.delete_character_quest(p['character_id'], quest_id)
                    await db.log_quest_completion(
                         character_id=p['character_id'],
                         quest_id=quest_id,
                         started_at=p['started_at'],
                         rewards={"xp": xp_reward, "note": f"Contribution: {percentage*100:.1f}%"}
                    )

    # 4. Broadcast
    await manager.broadcast({
        "type": "global_quest_completed",
        "quest_id": quest_id,
        "title": get_locale_string(quest_def.get('title', 'Global Quest'), 'en'),
        "outcome": {
            "unlocks": unlocks
        }
    })
