# autonomous.py (V2)
"""
Enhanced autonomous system that uses the autonomous_engine for true autonomy.
Integrates with legacy autonomous functions from autonomous_v1_legacy.py
"""

import asyncio
import time
from utils.autonomous_engine import autonomous_engine
from server_manager import server_manager
import globals
from utils.logger import get_logger
from utils.task_tracker import create_tracked_task

logger = get_logger('autonomous')

# Rate limiting: Track last action time per server to prevent rapid-fire
# Seeded from persisted engine data so restarts don't bypass cooldowns
_last_action_execution = dict(autonomous_engine.server_last_action)
_MIN_ACTION_INTERVAL = 30  # Minimum 30 seconds between autonomous actions

# Per-guild locks to prevent race conditions from near-simultaneous messages
_action_locks: dict = {}  # guild_id -> asyncio.Lock

def _get_action_lock(guild_id: int) -> asyncio.Lock:
    """Get or create an asyncio.Lock for a guild."""
    if guild_id not in _action_locks:
        _action_locks[guild_id] = asyncio.Lock()
    return _action_locks[guild_id]

# Pause state for voice sessions
_autonomous_paused = False


def pause_autonomous_system():
    """Pause autonomous message generation (called during voice sessions)"""
    global _autonomous_paused
    _autonomous_paused = True
    logger.info("Autonomous system paused")


def resume_autonomous_system():
    """Resume autonomous message generation (called after voice sessions)"""
    global _autonomous_paused
    _autonomous_paused = False
    logger.info("Autonomous system resumed")


async def autonomous_tick_v2(guild_id: int):
    """
    New autonomous tick that uses context-aware decision making.
    Replaces the random 10% chance with intelligent decision.
    """
    # Check if autonomous is paused (voice session)
    if _autonomous_paused:
        logger.debug(f"[V2] Autonomous system paused (voice session active)")
        return
    
    # Rate limiting check
    now = time.time()
    if guild_id in _last_action_execution:
        time_since_last = now - _last_action_execution[guild_id]
        if time_since_last < _MIN_ACTION_INTERVAL:
            logger.debug(f"[V2] Rate limit: Only {time_since_last:.0f}s since last action (need {_MIN_ACTION_INTERVAL}s)")
            return
    
    # Ask the engine if Miku should act (with optional debug logging)
    action_type = autonomous_engine.should_take_action(guild_id, debug=globals.AUTONOMOUS_DEBUG)
    
    if action_type is None:
        # Engine decided not to act
        return
    
    logger.info(f"[V2] Autonomous engine decided to: {action_type} for server {guild_id}")
    
    # Execute the action using legacy functions
    from utils.autonomous_v1_legacy import (
        miku_say_something_general_for_server,
        miku_engage_random_user_for_server,
        share_miku_tweet_for_server,
        miku_detect_and_join_conversation_for_server
    )
    from utils.profile_picture_manager import profile_picture_manager
    
    try:
        if action_type == "general":
            await miku_say_something_general_for_server(guild_id)
        elif action_type == "engage_user":
            await miku_engage_random_user_for_server(guild_id)
        elif action_type == "share_tweet":
            await share_miku_tweet_for_server(guild_id)
        elif action_type == "join_conversation":
            await miku_detect_and_join_conversation_for_server(guild_id)
        elif action_type == "change_profile_picture":
            # Get current mood for this server
            mood, _ = server_manager.get_server_mood(guild_id)
            logger.info(f"[V2] Changing profile picture (mood: {mood})")
            result = await profile_picture_manager.change_profile_picture(mood=mood, debug=True)
            if result["success"]:
                logger.info(f"Profile picture changed successfully!")
            else:
                logger.warning(f"Profile picture change failed: {result.get('error')}")
        
        # Record that action was taken
        autonomous_engine.record_action(guild_id)
        
        # Update rate limiter
        _last_action_execution[guild_id] = time.time()
        
        # Check for bipolar argument trigger (only if bipolar mode is active)
        try:
            from utils.bipolar_mode import maybe_trigger_argument, is_bipolar_mode
            if is_bipolar_mode():
                server_config = server_manager.servers.get(guild_id)
                if server_config and server_config.autonomous_channel_id:
                    channel = globals.client.get_channel(server_config.autonomous_channel_id)
                    if channel:
                        await maybe_trigger_argument(channel, globals.client, "Triggered after an autonomous action")
        except Exception as bipolar_err:
            logger.warning(f"Bipolar check error: {bipolar_err}")
        
    except Exception as e:
        logger.error(f"Error executing autonomous action: {e}")


async def autonomous_reaction_tick_v2(guild_id: int):
    """
    Scheduled check for reacting to older messages.
    This runs less frequently (e.g., every 20 minutes) and picks from recent messages.
    """
    # Ask the engine if Miku should react (scheduled check)
    should_react = autonomous_engine.should_react_to_message(guild_id, message_age_seconds=600)  # Check 10 min old msgs
    
    if not should_react:
        return
    
    logger.debug(f"[V2] Scheduled reaction check triggered for server {guild_id}")
    
    try:
        from utils.autonomous_v1_legacy import miku_autonomous_reaction_for_server
        # Don't pass force_message - let it pick a random recent message
        await miku_autonomous_reaction_for_server(guild_id, force_message=None)
        
        # Record action
        autonomous_engine.record_action(guild_id)
        
    except Exception as e:
        logger.error(f"Error executing scheduled reaction: {e}")


def on_message_event(message):
    """
    Hook for bot.py to call on every message.
    Updates context without LLM calls.
    
    ONLY processes messages from the configured autonomous channel.
    """
    if not message.guild:
        return  # DMs don't use this system
    
    guild_id = message.guild.id
    
    # Get server config to check if this is the autonomous channel
    server_config = server_manager.get_server_config(guild_id)
    if not server_config:
        return  # No config for this server
    
    # CRITICAL: Only process messages from the autonomous channel
    if message.channel.id != server_config.autonomous_channel_id:
        return  # Ignore messages from other channels
    
    # Track the message
    autonomous_engine.track_message(guild_id, author_is_bot=message.author.bot)
    
    # Check if we should act (async, non-blocking)
    if not message.author.bot:  # Only check for human messages
        create_tracked_task(_check_and_act(guild_id), task_name="autonomous_check_act")
        
        # Also check if we should react to this specific message
        create_tracked_task(_check_and_react(guild_id, message), task_name="autonomous_check_react")


async def _check_and_react(guild_id: int, message):
    """
    Check if Miku should react to a new message with an emoji.
    Called for each new message in real-time.
    """
    if _autonomous_paused:
        return

    # Calculate message age
    from datetime import datetime, timezone
    message_age = (datetime.now(timezone.utc) - message.created_at).total_seconds()
    
    # Ask engine if we should react
    should_react = autonomous_engine.should_react_to_message(guild_id, message_age)
    
    if should_react:
        logger.info(f"[V2] Real-time reaction triggered for message from {message.author.display_name}")
        from utils.autonomous_v1_legacy import miku_autonomous_reaction_for_server
        await miku_autonomous_reaction_for_server(guild_id, force_message=message)
        
        # Record action (reactions count as actions for cooldown purposes)
        autonomous_engine.record_action(guild_id)


async def _check_and_act(guild_id: int):
    """
    Internal function to check if action should be taken.
    Called after each message, but engine makes smart decision.
    
    IMPORTANT: Pass triggered_by_message=True so the engine knows to respond
    to the message instead of saying something random/general.
    
    Uses per-guild lock to prevent race conditions from near-simultaneous messages.
    """
    async with _get_action_lock(guild_id):
        if _autonomous_paused:
            return

        # Rate limiting check
        now = time.time()
        if guild_id in _last_action_execution:
            time_since_last = now - _last_action_execution[guild_id]
            if time_since_last < _MIN_ACTION_INTERVAL:
                return
        
        action_type = autonomous_engine.should_take_action(guild_id, triggered_by_message=True)
        
        if action_type:
            logger.info(f"[V2] Message triggered autonomous action: {action_type}")
            
            # Execute the action directly (don't call autonomous_tick_v2 which would check again)
            from utils.autonomous_v1_legacy import (
                miku_say_something_general_for_server,
                miku_engage_random_user_for_server,
                share_miku_tweet_for_server,
                miku_detect_and_join_conversation_for_server
            )
            from utils.profile_picture_manager import profile_picture_manager
            
            try:
                if action_type == "general":
                    await miku_say_something_general_for_server(guild_id)
                elif action_type == "engage_user":
                    await miku_engage_random_user_for_server(guild_id)
                elif action_type == "share_tweet":
                    await share_miku_tweet_for_server(guild_id)
                elif action_type == "join_conversation":
                    await miku_detect_and_join_conversation_for_server(guild_id)
                elif action_type == "change_profile_picture":
                    # Get current mood for this server
                    mood, _ = server_manager.get_server_mood(guild_id)
                    logger.info(f"[V2] Changing profile picture (mood: {mood})")
                    result = await profile_picture_manager.change_profile_picture(mood=mood, debug=True)
                    if result["success"]:
                        logger.info(f"Profile picture changed successfully!")
                    else:
                        logger.warning(f"Profile picture change failed: {result.get('error')}")
                
                # Record that action was taken
                autonomous_engine.record_action(guild_id)
                
                # Update rate limiter
                _last_action_execution[guild_id] = time.time()
                
                # Check for bipolar argument trigger (only if bipolar mode is active)
                try:
                    from utils.bipolar_mode import maybe_trigger_argument, is_bipolar_mode
                    if is_bipolar_mode():
                        server_config = server_manager.servers.get(guild_id)
                        if server_config and server_config.autonomous_channel_id:
                            channel = globals.client.get_channel(server_config.autonomous_channel_id)
                            if channel:
                                await maybe_trigger_argument(channel, globals.client, "Triggered after message-based action")
                except Exception as bipolar_err:
                    logger.warning(f"Bipolar check error: {bipolar_err}")
                
            except Exception as e:
                logger.error(f"Error executing message-triggered action: {e}")


def on_presence_update(member, before, after):
    """
    Hook for presence updates (status changes, activities).
    
    Args:
        member: The Member object (from 'after' in discord.py event)
        before: Member object with old state
        after: Member object with new state
    """
    # Ignore bot users (including music bots that spam activity updates)
    if member.bot:
        return
    
    guild_id = member.guild.id
    
    # Track status changes
    if before.status != after.status:
        autonomous_engine.track_user_event(guild_id, "status_changed")
        logger.debug(f"[V2] {member.display_name} status changed: {before.status} → {after.status}")
    
    # Track activity changes
    if before.activities != after.activities:
        # Check for new activities
        before_activity_names = {a.name for a in before.activities if hasattr(a, 'name')}
        after_activity_names = {a.name for a in after.activities if hasattr(a, 'name')}
        
        new_activities = after_activity_names - before_activity_names
        
        for activity_name in new_activities:
            autonomous_engine.track_user_event(
                guild_id, 
                "activity_started", 
                {"activity_name": activity_name}
            )
            logger.debug(f"[V2] {member.display_name} started activity: {activity_name}")


def on_member_join(member):
    """Hook for member join events"""
    # Ignore bot users
    if member.bot:
        return
    
    guild_id = member.guild.id
    autonomous_engine.track_user_event(guild_id, "user_joined")


def on_mood_change(guild_id: int, new_mood: str):
    """Hook for mood changes"""
    autonomous_engine.update_mood(guild_id, new_mood)


async def periodic_decay_task():
    """
    Background task that decays event counters and saves context.
    Run this every 15 minutes.
    """
    task_start_time = time.time()
    iteration_count = 0
    
    while True:
        await asyncio.sleep(900)  # 15 minutes
        
        iteration_count += 1
        
        # Use list() to safely iterate even if dict changes
        guild_ids = list(server_manager.servers.keys())
        
        for guild_id in guild_ids:
            try:
                autonomous_engine.decay_events(guild_id)
            except Exception as e:
                logger.warning(f"Error decaying events for guild {guild_id}: {e}")
        
        # Save context to disk periodically
        try:
            autonomous_engine.save_context()
        except Exception as e:
            logger.error(f"Error saving autonomous context: {e}")
        
        uptime_hours = (time.time() - task_start_time) / 3600
        logger.debug(f"[V2] Decay task completed (iteration #{iteration_count}, uptime: {uptime_hours:.1f}h)")
        logger.debug(f"   └─ Processed {len(guild_ids)} servers")


def initialize_v2_system(client):
    """
    Initialize the V2 autonomous system.
    Call this from bot.py on startup.
    """
    logger.debug("Initializing Autonomous V2 System...")
    
    # Initialize mood states for all servers
    for guild_id, server_config in server_manager.servers.items():
        autonomous_engine.update_mood(guild_id, server_config.current_mood_name)
    
    # Start decay task
    client.loop.create_task(periodic_decay_task())
    
    logger.info("Autonomous V2 System initialized")


# ========== Legacy Function Wrappers ==========
# These maintain compatibility with old code that imports from autonomous.py

from utils.autonomous_v1_legacy import (
    load_last_sent_tweets,
    save_last_sent_tweets,
    setup_autonomous_speaking,
    # Server-specific functions
    miku_autonomous_tick_for_server,
    miku_say_something_general_for_server,
    miku_engage_random_user_for_server,
    miku_detect_and_join_conversation_for_server,
    share_miku_tweet_for_server,
    miku_autonomous_reaction_for_server,
    miku_autonomous_reaction_for_dm,
    handle_custom_prompt_for_server,
    # Legacy global functions (for API compatibility)
    miku_autonomous_tick,
    miku_say_something_general,
    miku_engage_random_user,
    miku_detect_and_join_conversation,
    share_miku_tweet,
    handle_custom_prompt,
    miku_autonomous_reaction,
)

# Alias the V2 tick as the main autonomous tick
autonomous_tick = autonomous_tick_v2
autonomous_reaction_tick = autonomous_reaction_tick_v2
