# server_manager.py

import json
import os
import asyncio
from typing import Dict, List, Optional, Set
from dataclasses import dataclass, asdict, fields as dataclass_fields
from datetime import datetime, timedelta
import discord
from apscheduler.schedulers.asyncio import AsyncIOScheduler
from apscheduler.triggers.interval import IntervalTrigger
from apscheduler.triggers.cron import CronTrigger
from apscheduler.triggers.date import DateTrigger
import random
from utils.logger import get_logger

logger = get_logger('server')

@dataclass
class ServerConfig:
    """Configuration for a single Discord server"""
    guild_id: int
    guild_name: str
    autonomous_channel_id: int
    autonomous_channel_name: str
    bedtime_channel_ids: List[int]
    enabled_features: Set[str]  # autonomous, bedtime, monday_video, etc.
    autonomous_interval_minutes: int = 15
    conversation_detection_interval_minutes: int = 3
    bedtime_hour: int = 21
    bedtime_minute: int = 0
    bedtime_hour_end: int = 21  # End of bedtime range (default 11PM)
    bedtime_minute_end: int = 59  # End of bedtime range (default 11:59PM)
    monday_video_hour: int = 4
    monday_video_minute: int = 30
    # Per-server mood tracking
    current_mood_name: str = "neutral"
    current_mood_description: str = ""
    previous_mood_name: str = "neutral"
    is_sleeping: bool = False
    sleepy_responses_left: Optional[int] = None
    angry_wakeup_timer: Optional[float] = None  # TODO: implement angry-wakeup mechanic or remove field
    forced_angry_until: Optional[str] = None  # ISO format datetime string, or None
    just_woken_up: bool = False
    
    def to_dict(self):
        return asdict(self)
    
    @classmethod
    def from_dict(cls, data: dict):
        # Convert set back from list, or handle old string format
        if 'enabled_features' in data:
            if isinstance(data['enabled_features'], list):
                data['enabled_features'] = set(data['enabled_features'])
            elif isinstance(data['enabled_features'], str):
                # Handle old string format like "{'bedtime', 'monday_video', 'autonomous'}"
                try:
                    # Remove the outer braces and split by comma
                    features_str = data['enabled_features'].strip('{}')
                    features_list = [f.strip().strip("'\"") for f in features_str.split(',') if f.strip()]
                    data['enabled_features'] = set(features_list)
                except Exception as e:
                    logger.warning(f"Failed to parse enabled_features string '{data['enabled_features']}': {e}")
                    # Fallback to default features
                    data['enabled_features'] = {"autonomous", "bedtime", "monday_video"}
        # Strip any keys that aren't valid dataclass fields (forward-compat safety)
        valid_fields = {f.name for f in dataclass_fields(cls)}
        data = {k: v for k, v in data.items() if k in valid_fields}
        return cls(**data)

class ServerManager:
    """Manages multiple Discord servers with independent configurations"""
    
    def __init__(self, config_file: str = "memory/servers_config.json"):
        self.config_file = config_file
        self.servers: Dict[int, ServerConfig] = {}
        self.schedulers: Dict[int, AsyncIOScheduler] = {}
        self._wakeup_tasks: Dict[int, asyncio.Task] = {}  # guild_id -> delayed wakeup task
        self.load_config()
    
    def load_config(self):
        """Load server configurations from file"""
        if os.path.exists(self.config_file):
            try:
                with open(self.config_file, "r", encoding="utf-8") as f:
                    data = json.load(f)
                    for guild_id_str, server_data in data.items():
                        guild_id = int(guild_id_str)
                        self.servers[guild_id] = ServerConfig.from_dict(server_data)
                        logger.info(f"Loaded config for server: {server_data['guild_name']} (ID: {guild_id})")
                
                # After loading, check if we need to repair the config
                self.repair_config()
            except Exception as e:
                logger.error(f"Failed to load server config: {e}")
                logger.info("Starting with zero servers — add servers via the API or dashboard")
        else:
            logger.info("No servers_config.json found — starting with zero servers")
    
    def repair_config(self):
        """Repair corrupted configuration data and save it back"""
        try:
            needs_repair = False
            for server in self.servers.values():
                # Check if enabled_features is a string (corrupted)
                if isinstance(server.enabled_features, str):
                    needs_repair = True
                    logger.info(f"Repairing corrupted enabled_features for server: {server.guild_name}")
                    # Re-parse the features
                    try:
                        features_str = server.enabled_features.strip('{}')
                        features_list = [f.strip().strip("'\"") for f in features_str.split(',') if f.strip()]
                        server.enabled_features = set(features_list)
                    except Exception as e:
                        logger.warning(f"Failed to repair enabled_features for {server.guild_name}: {e}")
                        server.enabled_features = {"autonomous", "bedtime", "monday_video"}
            
            if needs_repair:
                logger.info("Saving repaired configuration...")
                self.save_config()
        except Exception as e:
            logger.error(f"Failed to repair config: {e}")
    
    def save_config(self):
        """Save server configurations to file"""
        try:
            os.makedirs(os.path.dirname(self.config_file), exist_ok=True)
            config_data = {}
            for guild_id, server in self.servers.items():
                # Convert the server config to dict, but handle sets properly
                server_dict = server.to_dict()
                # Convert set to list for JSON serialization
                if 'enabled_features' in server_dict and isinstance(server_dict['enabled_features'], set):
                    server_dict['enabled_features'] = list(server_dict['enabled_features'])
                config_data[str(guild_id)] = server_dict
            
            with open(self.config_file, "w", encoding="utf-8") as f:
                json.dump(config_data, f, indent=2)
        except Exception as e:
            logger.error(f"Failed to save server config: {e}")
    
    def add_server(self, guild_id: int, guild_name: str, autonomous_channel_id: int, 
                   autonomous_channel_name: str, bedtime_channel_ids: List[int] = None,
                   enabled_features: Set[str] = None) -> bool:
        """Add a new server configuration"""
        if guild_id in self.servers:
            logger.info(f"Server {guild_id} already exists")
            return False
        
        if bedtime_channel_ids is None:
            bedtime_channel_ids = [autonomous_channel_id]
        
        if enabled_features is None:
            enabled_features = {"autonomous", "bedtime", "monday_video"}
        
        server = ServerConfig(
            guild_id=guild_id,
            guild_name=guild_name,
            autonomous_channel_id=autonomous_channel_id,
            autonomous_channel_name=autonomous_channel_name,
            bedtime_channel_ids=bedtime_channel_ids,
            enabled_features=enabled_features
        )
        
        self.servers[guild_id] = server
        self.save_config()
        logger.info(f"Added new server: {guild_name} (ID: {guild_id})")
        return True
    
    def remove_server(self, guild_id: int) -> bool:
        """Remove a server configuration"""
        if guild_id not in self.servers:
            return False
        
        server_name = self.servers[guild_id].guild_name
        del self.servers[guild_id]
        
        # Stop and remove scheduler
        if guild_id in self.schedulers:
            self.schedulers[guild_id].shutdown()
            del self.schedulers[guild_id]
        
        self.save_config()
        logger.info(f"Removed server: {server_name} (ID: {guild_id})")
        return True
    
    def get_server_config(self, guild_id: int) -> Optional[ServerConfig]:
        """Get configuration for a specific server"""
        return self.servers.get(guild_id)
    
    def get_all_servers(self) -> List[ServerConfig]:
        """Get all server configurations"""
        return list(self.servers.values())
    
    def update_server_config(self, guild_id: int, **kwargs) -> bool:
        """Update configuration for a specific server"""
        if guild_id not in self.servers:
            return False
        
        server = self.servers[guild_id]
        for key, value in kwargs.items():
            if hasattr(server, key):
                setattr(server, key, value)
        
        self.save_config()
        logger.info(f"Updated config for server: {server.guild_name}")
        return True
    
    # ========== Mood Management Methods ==========
    def get_server_mood(self, guild_id: int) -> tuple[str, str]:
        """Get current mood name and description for a server"""
        if guild_id not in self.servers:
            return "neutral", ""
        
        server = self.servers[guild_id]
        return server.current_mood_name, server.current_mood_description
    
    def set_server_mood(self, guild_id: int, mood_name: str, mood_description: str = None):
        """Set mood for a specific server.
        
        Also handles:
        - Syncing is_sleeping state (fix #4: sleep/mood desync)
        - Notifying the autonomous engine (fix #9: engine mood desync)
        """
        if guild_id not in self.servers:
            return False
        
        server = self.servers[guild_id]
        server.previous_mood_name = server.current_mood_name
        server.current_mood_name = mood_name
        
        if mood_description:
            server.current_mood_description = mood_description
        else:
            # Load mood description if not provided
            try:
                from utils.moods import load_mood_description
                server.current_mood_description = load_mood_description(mood_name)
            except Exception as e:
                logger.error(f"Failed to load mood description for {mood_name}: {e}")
                server.current_mood_description = f"I'm feeling {mood_name} today."
        
        # Fix #4: Keep is_sleeping in sync with mood
        # If mood changes away from 'asleep', clear sleeping state
        if mood_name != "asleep" and server.is_sleeping:
            server.is_sleeping = False
            self.cancel_wakeup_task(guild_id)
            logger.info(f"Cleared sleep state for server {server.guild_name} (mood changed to {mood_name})")
        
        self.save_config()
        logger.info(f"Server {server.guild_name} mood changed to: {mood_name}")
        logger.debug(f"Mood description: {server.current_mood_description[:100]}{'...' if len(server.current_mood_description) > 100 else ''}")
        
        # Fix #9: Always notify autonomous engine of mood change
        try:
            from utils.autonomous import on_mood_change
            on_mood_change(guild_id, mood_name)
        except Exception as e:
            logger.error(f"Failed to notify autonomous engine of mood change to {mood_name}: {e}")
        
        return True
    
    def get_server_sleep_state(self, guild_id: int) -> bool:
        """Get sleep state for a specific server"""
        if guild_id not in self.servers:
            return False
        return self.servers[guild_id].is_sleeping
    
    def set_server_sleep_state(self, guild_id: int, sleeping: bool):
        """Set sleep state for a specific server"""
        if guild_id not in self.servers:
            return False
        
        server = self.servers[guild_id]
        server.is_sleeping = sleeping
        
        # If waking up, cancel any pending delayed wakeup task
        if not sleeping:
            self.cancel_wakeup_task(guild_id)
        
        self.save_config()
        return True
    
    def schedule_wakeup_task(self, guild_id: int, delay_seconds: int = 3600):
        """Schedule a delayed wakeup task for a server, cancelling any existing one first.
        
        Args:
            guild_id: The server to schedule wakeup for
            delay_seconds: How long to sleep before waking (default 1 hour)
        """
        # Cancel any existing wakeup task for this server
        self.cancel_wakeup_task(guild_id)
        
        import globals as _globals
        
        async def _delayed_wakeup():
            try:
                await asyncio.sleep(delay_seconds)
                # Check if we're still asleep (might have been woken manually)
                server = self.servers.get(guild_id)
                if server and server.is_sleeping:
                    self.set_server_sleep_state(guild_id, False)
                    self.set_server_mood(guild_id, "neutral")
                    
                    # Update nickname
                    try:
                        from utils.moods import update_server_nickname
                        await update_server_nickname(guild_id)
                    except Exception as e:
                        logger.error(f"Failed to update nickname on wake-up: {e}")
                    
                    logger.info(f"Server {guild_id} woke up from auto-sleep after {delay_seconds}s")
                else:
                    logger.debug(f"Wakeup task for {guild_id} completed but server already awake, skipping")
            except asyncio.CancelledError:
                logger.debug(f"Wakeup task for server {guild_id} was cancelled")
            finally:
                # Clean up our reference
                self._wakeup_tasks.pop(guild_id, None)
        
        task = _globals.client.loop.create_task(_delayed_wakeup())
        self._wakeup_tasks[guild_id] = task
        logger.info(f"Scheduled auto-wake for server {guild_id} in {delay_seconds}s")
        return task
    
    def cancel_wakeup_task(self, guild_id: int):
        """Cancel a pending wakeup task for a server, if any."""
        task = self._wakeup_tasks.pop(guild_id, None)
        if task and not task.done():
            task.cancel()
            logger.info(f"Cancelled pending wakeup task for server {guild_id}")
    
    def get_server_mood_state(self, guild_id: int) -> dict:
        """Get complete mood state for a specific server"""
        if guild_id not in self.servers:
            return {}
        
        server = self.servers[guild_id]
        return {
            "current_mood_name": server.current_mood_name,
            "current_mood_description": server.current_mood_description,
            "previous_mood_name": server.previous_mood_name,
            "is_sleeping": server.is_sleeping,
            "sleepy_responses_left": server.sleepy_responses_left,
            "forced_angry_until": server.forced_angry_until,
            "just_woken_up": server.just_woken_up
        }
    
    def set_server_mood_state(self, guild_id: int, **kwargs):
        """Set multiple mood state properties for a server"""
        if guild_id not in self.servers:
            return False
        
        server = self.servers[guild_id]
        for key, value in kwargs.items():
            if hasattr(server, key):
                setattr(server, key, value)
        
        self.save_config()
        return True
    
    def setup_server_scheduler(self, guild_id: int, client: discord.Client):
        """Setup independent scheduler for a specific server"""
        if guild_id not in self.servers:
            logger.warning(f"Cannot setup scheduler for unknown server: {guild_id}")
            return
        
        server_config = self.servers[guild_id]
        
        # Create new scheduler for this server
        scheduler = AsyncIOScheduler()
        
        # Add autonomous speaking job
        if "autonomous" in server_config.enabled_features:
            scheduler.add_job(
                self._run_autonomous_for_server,
                IntervalTrigger(minutes=server_config.autonomous_interval_minutes),
                args=[guild_id, client],
                id=f"autonomous_{guild_id}"
            )
        
        # Add autonomous reaction job (parallel to speaking, runs every 20 minutes)
        if "autonomous" in server_config.enabled_features:
            scheduler.add_job(
                self._run_autonomous_reaction_for_server,
                IntervalTrigger(minutes=20),
                args=[guild_id, client],
                id=f"autonomous_reaction_{guild_id}"
            )
        
        # Note: Conversation detection is now handled by V2 system via message events
        # No need for separate scheduler job
        
        # Add Monday video job
        if "monday_video" in server_config.enabled_features:
            scheduler.add_job(
                self._send_monday_video_for_server,
                CronTrigger(day_of_week='mon', hour=server_config.monday_video_hour, minute=server_config.monday_video_minute),
                args=[guild_id, client],
                id=f"monday_video_{guild_id}"
            )
        
        # Add bedtime reminder job
        if "bedtime" in server_config.enabled_features:
            logger.info(f"Setting up bedtime scheduler for server {server_config.guild_name}")
            logger.debug(f"   Random time range: {server_config.bedtime_hour:02d}:{server_config.bedtime_minute:02d} - {server_config.bedtime_hour_end:02d}:{server_config.bedtime_minute_end:02d}")
            scheduler.add_job(
                self._schedule_random_bedtime_for_server,
                CronTrigger(hour=server_config.bedtime_hour, minute=server_config.bedtime_minute),
                args=[guild_id, client],
                id=f"bedtime_schedule_{guild_id}"
            )
        
        # Add mood rotation job (every hour)
        scheduler.add_job(
            self._rotate_server_mood,
            IntervalTrigger(hours=1),
            args=[guild_id, client],
            id=f"mood_rotation_{guild_id}"
        )
        
        self.schedulers[guild_id] = scheduler
        scheduler.start()
        logger.info(f"Started scheduler for server: {server_config.guild_name}")
    
    def start_all_schedulers(self, client: discord.Client):
        """Start schedulers for all servers"""
        logger.info("Starting all server schedulers...")
        
        for guild_id in self.servers:
            self.setup_server_scheduler(guild_id, client)
        
        # Start DM mood rotation scheduler
        self.setup_dm_mood_scheduler(client)
        # Start Figurine DM scheduler
        self.setup_figurine_updates_scheduler(client)
        
        logger.info(f"Started {len(self.servers)} server schedulers + DM mood scheduler")
    
    def update_server_bedtime_job(self, guild_id: int, client: discord.Client):
        """Update just the bedtime job for a specific server without restarting all schedulers"""
        server_config = self.servers.get(guild_id)
        if not server_config:
            logger.warning(f"No server config found for guild {guild_id}")
            return False
            
        scheduler = self.schedulers.get(guild_id)
        if not scheduler:
            logger.warning(f"No scheduler found for guild {guild_id}")
            return False
            
        # Remove existing bedtime job if it exists
        bedtime_job_id = f"bedtime_schedule_{guild_id}"
        try:
            scheduler.remove_job(bedtime_job_id)
            logger.info(f"Removed old bedtime job for server {guild_id}")
        except Exception as e:
            logger.debug(f"No existing bedtime job to remove for server {guild_id}: {e}")
            
        # Add new bedtime job with updated configuration
        if "bedtime" in server_config.enabled_features:
            logger.info(f"Updating bedtime scheduler for server {server_config.guild_name}")
            logger.debug(f"   New random time range: {server_config.bedtime_hour:02d}:{server_config.bedtime_minute:02d} - {server_config.bedtime_hour_end:02d}:{server_config.bedtime_minute_end:02d}")
            scheduler.add_job(
                self._schedule_random_bedtime_for_server,
                CronTrigger(hour=server_config.bedtime_hour, minute=server_config.bedtime_minute),
                args=[guild_id, client],
                id=bedtime_job_id
            )
            logger.info(f"Updated bedtime job for server {server_config.guild_name}")
            return True
        else:
            logger.info(f"Bedtime feature not enabled for server {guild_id}")
            return True
    
    def setup_dm_mood_scheduler(self, client: discord.Client):
        """Setup DM mood rotation scheduler"""
        try:
            from utils.moods import rotate_dm_mood
            
            # Create DM mood rotation job (every 2 hours)
            scheduler = AsyncIOScheduler()
            scheduler.add_job(
                rotate_dm_mood,
                IntervalTrigger(hours=2),
                id="dm_mood_rotation"
            )
            
            scheduler.start()
            self.schedulers["dm_mood"] = scheduler
            logger.info("DM mood rotation scheduler started (every 2 hours)")
            
        except Exception as e:
            logger.error(f"Failed to setup DM mood scheduler: {e}")

    def _enqueue_figurine_send(self, client: discord.Client):
        """Enqueue the figurine DM send task in the client's loop."""
        try:
            from utils.figurine_notifier import send_figurine_dm_to_all_subscribers
            if client.loop and client.loop.is_running():
                client.loop.create_task(send_figurine_dm_to_all_subscribers(client))
                logger.debug("Figurine DM send task queued")
            else:
                logger.warning("Client loop not available for figurine DM send")
        except Exception as e:
            logger.error(f"Error enqueuing figurine DM: {e}")

    def _schedule_one_figurine_send_today(self, scheduler: AsyncIOScheduler, client: discord.Client):
        """Schedule one figurine DM send at a random non-evening time today (or tomorrow if time passed)."""
        now = datetime.now()
        # Define non-evening hours: 08:00-17:59
        random_hour = random.randint(8, 17)
        random_minute = random.randint(0, 59)
        target_time = now.replace(hour=random_hour, minute=random_minute, second=0, microsecond=0)
        if target_time <= now:
            target_time = target_time + timedelta(days=1)
        logger.info(f"Scheduling figurine DM at {target_time.strftime('%Y-%m-%d %H:%M')} (random non-evening)")
        scheduler.add_job(
            self._enqueue_figurine_send,
            DateTrigger(run_date=target_time),
            args=[client],
            id=f"figurine_dm_{int(target_time.timestamp())}",
            replace_existing=False
        )

    def setup_figurine_updates_scheduler(self, client: discord.Client):
        """Create a daily scheduler that schedules one random non-evening figurine DM send per day."""
        try:
            scheduler = AsyncIOScheduler()
            # Every day at 07:30, schedule today's random send (will roll to tomorrow if time passed)
            scheduler.add_job(
                self._schedule_one_figurine_send_today,
                CronTrigger(hour=7, minute=30),
                args=[scheduler, client],
                id="figurine_daily_scheduler"
            )
            # Also schedule one immediately on startup for today/tomorrow
            self._schedule_one_figurine_send_today(scheduler, client)
            scheduler.start()
            self.schedulers["figurine_dm"] = scheduler
            logger.info("Figurine updates scheduler started")
        except Exception as e:
            logger.error(f"Failed to setup figurine updates scheduler: {e}")
    
    def stop_all_schedulers(self):
        """Stop all schedulers"""
        logger.info("Stopping all schedulers...")
        
        for scheduler in self.schedulers.values():
            try:
                scheduler.shutdown()
            except Exception as e:
                logger.warning(f"Error stopping scheduler: {e}")
        
        self.schedulers.clear()
        logger.info("All schedulers stopped")
    
    # Implementation of autonomous functions - these integrate with the autonomous system
    def _run_autonomous_for_server(self, guild_id: int, client: discord.Client):
        """Run autonomous behavior for a specific server - called by APScheduler"""
        try:
            # V2: Use the new context-aware autonomous system
            from utils.autonomous import autonomous_tick
            # Create an async task in the client's event loop
            if client.loop and client.loop.is_running():
                client.loop.create_task(autonomous_tick(guild_id))
                logger.debug(f"[V2] Autonomous tick queued for server {guild_id}")
            else:
                logger.warning(f"Client loop not available for autonomous tick in server {guild_id}")
        except Exception as e:
            logger.error(f"Error in autonomous tick for server {guild_id}: {e}")
    
    def _run_autonomous_reaction_for_server(self, guild_id: int, client: discord.Client):
        """Run autonomous reaction for a specific server - called by APScheduler"""
        try:
            # V2: Use the new context-aware reaction system
            from utils.autonomous import autonomous_reaction_tick
            # Create an async task in the client's event loop
            if client.loop and client.loop.is_running():
                client.loop.create_task(autonomous_reaction_tick(guild_id))
                logger.debug(f"[V2] Autonomous reaction queued for server {guild_id}")
            else:
                logger.warning(f"Client loop not available for autonomous reaction in server {guild_id}")
        except Exception as e:
            logger.error(f"Error in autonomous reaction for server {guild_id}: {e}")
    
    def _run_conversation_detection_for_server(self, guild_id: int, client: discord.Client):
        """Run conversation detection for a specific server - called by APScheduler"""
        try:
            from utils.autonomous import miku_detect_and_join_conversation_for_server
            # Create an async task in the client's event loop
            if client.loop and client.loop.is_running():
                client.loop.create_task(miku_detect_and_join_conversation_for_server(guild_id))
                logger.debug(f"Conversation detection queued for server {guild_id}")
            else:
                logger.warning(f"Client loop not available for conversation detection in server {guild_id}")
        except Exception as e:
            logger.error(f"Error in conversation detection for server {guild_id}: {e}")
    
    def _send_monday_video_for_server(self, guild_id: int, client: discord.Client):
        """Send Monday video for a specific server - called by APScheduler"""
        try:
            from utils.scheduled import send_monday_video_for_server
            # Create an async task in the client's event loop
            if client.loop and client.loop.is_running():
                client.loop.create_task(send_monday_video_for_server(guild_id))
                logger.debug(f"Monday video queued for server {guild_id}")
            else:
                logger.warning(f"Client loop not available for Monday video in server {guild_id}")
        except Exception as e:
            logger.error(f"Error in Monday video for server {guild_id}: {e}")
    
    def _schedule_random_bedtime_for_server(self, guild_id: int, client: discord.Client):
        """Schedule bedtime reminder for a specific server at a random time within the configured range"""
        logger.info(f"Bedtime scheduler triggered for server {guild_id} at {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}")
        
        # Get server config to determine the random time range
        server_config = self.servers.get(guild_id)
        if not server_config:
            logger.warning(f"No server config found for guild {guild_id}")
            return
            
        # Calculate random time within the bedtime range
        start_minutes = server_config.bedtime_hour * 60 + server_config.bedtime_minute
        end_minutes = server_config.bedtime_hour_end * 60 + server_config.bedtime_minute_end
        
        logger.debug(f"Bedtime range calculation: {server_config.bedtime_hour:02d}:{server_config.bedtime_minute:02d} ({start_minutes} min) to {server_config.bedtime_hour_end:02d}:{server_config.bedtime_minute_end:02d} ({end_minutes} min)")
        
        # Handle case where end time is next day (e.g., 23:30 to 00:30)
        if end_minutes <= start_minutes:
            end_minutes += 24 * 60  # Add 24 hours
            logger.debug(f"Cross-midnight range detected, adjusted end to {end_minutes} minutes")
            
        random_minutes = random.randint(start_minutes, end_minutes)
        logger.debug(f"Random time selected: {random_minutes} minutes from midnight")
        
        # Convert back to hours and minutes
        random_hour = (random_minutes // 60) % 24
        random_minute = random_minutes % 60
        
        # Calculate delay until the random time
        now = datetime.now()
        target_time = now.replace(hour=random_hour, minute=random_minute, second=0, microsecond=0)
        
        # If the target time has already passed today, schedule for tomorrow
        if target_time <= now:
            target_time += timedelta(days=1)
            
        delay_seconds = (target_time - now).total_seconds()
        
        logger.info(f"Random bedtime for server {server_config.guild_name}: {random_hour:02d}:{random_minute:02d} (in {delay_seconds/60:.1f} minutes)")
        
        # Schedule the actual bedtime reminder
        try:
            from utils.scheduled import send_bedtime_reminder_for_server
            
            def send_bedtime_delayed():
                if client.loop and client.loop.is_running():
                    client.loop.create_task(send_bedtime_reminder_for_server(guild_id, client))
                    logger.info(f"Random bedtime reminder sent for server {guild_id}")
                else:
                    logger.warning(f"Client loop not available for bedtime reminder in server {guild_id}")
            
            # Use the scheduler to schedule the delayed bedtime reminder
            scheduler = self.schedulers.get(guild_id)
            if scheduler:
                scheduler.add_job(
                    send_bedtime_delayed,
                    DateTrigger(run_date=target_time),
                    id=f"bedtime_reminder_{guild_id}_{int(target_time.timestamp())}"
                )
                logger.info(f"Bedtime reminder scheduled for server {guild_id} at {target_time.strftime('%Y-%m-%d %H:%M:%S')}")
            else:
                logger.warning(f"No scheduler found for server {guild_id}")
                
        except Exception as e:
            logger.error(f"Error scheduling bedtime reminder for server {guild_id}: {e}")
    
    def _rotate_server_mood(self, guild_id: int, client: discord.Client):
        """Rotate mood for a specific server - called by APScheduler"""
        try:
            from utils.moods import rotate_server_mood
            # Create an async task in the client's event loop
            if client.loop and client.loop.is_running():
                client.loop.create_task(rotate_server_mood(guild_id))
                logger.debug(f"Mood rotation queued for server {guild_id}")
            else:
                logger.warning(f"Client loop not available for mood rotation in server {guild_id}")
        except Exception as e:
            logger.error(f"Error in mood rotation for server {guild_id}: {e}")

# Global instance
server_manager = ServerManager()
