import asyncio
import logging
import signal
import os
import time

from dotenv import load_dotenv
from telegram import Update
from telegram.ext import Application, CommandHandler, CallbackQueryHandler

from bot import database, handlers

# Enable logging
logging.basicConfig(
    format="%(asctime)s - %(name)s - %(levelname)s - %(message)s", level=logging.INFO
)
# Quieten down the HTTPX logger, which is very verbose
logging.getLogger("httpx").setLevel(logging.WARNING)
logger = logging.getLogger(__name__)

# A global event to signal shutdown
shutdown_event = asyncio.Event()

def signal_handler(sig, frame):
    """Gracefully handle shutdown signals."""
    logger.info("Shutdown signal received. Shutting down gracefully...")
    shutdown_event.set()

async def decay_dropped_items():
    """A background task that periodically cleans up old dropped items."""
    while not shutdown_event.is_set():
        try:
            # Wait for 5 minutes before the next cleanup
            await asyncio.wait_for(shutdown_event.wait(), timeout=300)
        except asyncio.TimeoutError:
            start_time = time.time()
            logger.info("Running item decay task...")
            
            # Set decay time to 1 hour (3600 seconds)
            decay_seconds = 3600
            timestamp_limit = int(time.time()) - decay_seconds
            items_removed = await database.remove_expired_dropped_items(timestamp_limit)
            
            elapsed = time.time() - start_time
            if items_removed > 0:
                logger.info(f"Decayed and removed {items_removed} old items in {elapsed:.2f}s")

async def regenerate_stamina():
    """A background task that periodically regenerates stamina for all players."""
    while not shutdown_event.is_set():
        try:
            # Wait for 5 minutes before the next regeneration cycle
            await asyncio.wait_for(shutdown_event.wait(), timeout=300)
        except asyncio.TimeoutError:
            start_time = time.time()
            logger.info("Running stamina regeneration...")
            
            players_updated = await database.regenerate_all_players_stamina()
            
            elapsed = time.time() - start_time
            if players_updated > 0:
                logger.info(f"Regenerated stamina for {players_updated} players in {elapsed:.2f}s")
            
            # Alert if regeneration is taking too long (potential scaling issue)
            if elapsed > 5.0:
                logger.warning(f"⚠️ Stamina regeneration took {elapsed:.2f}s (threshold: 5s) - check database load!")

async def check_combat_timers():
    """A background task that checks for idle combat turns and auto-attacks."""
    while not shutdown_event.is_set():
        try:
            # Wait for 30 seconds before next check
            await asyncio.wait_for(shutdown_event.wait(), timeout=30)
        except asyncio.TimeoutError:
            start_time = time.time()
            # Check for combats idle for more than 5 minutes (300 seconds)
            idle_threshold = time.time() - 300
            idle_combats = await database.get_all_idle_combats(idle_threshold)
            
            if idle_combats:
                logger.info(f"Processing {len(idle_combats)} idle combats...")
            
            for combat in idle_combats:
                try:
                    from bot import combat as combat_logic
                    # Force end player's turn and let NPC attack
                    if combat['turn'] == 'player':
                        logger.info(f"Player {combat['player_id']} idle in combat - auto-ending turn")
                        await database.update_combat(combat['player_id'], {
                            'turn': 'npc',
                            'turn_started_at': time.time()
                        })
                        # NPC attacks
                        await combat_logic.npc_attack(combat['player_id'])
                except Exception as e:
                    logger.error(f"Error processing idle combat: {e}")
            
            # Log performance for monitoring
            if idle_combats:
                elapsed = time.time() - start_time
                logger.info(f"Processed {len(idle_combats)} idle combats in {elapsed:.2f}s")
                
                # Warn if taking too long (potential scaling issue)
                if elapsed > 10.0:
                    logger.warning(f"⚠️ Combat timer check took {elapsed:.2f}s (threshold: 10s) - consider batching!")

async def decay_corpses():
    """A background task that removes old corpses."""
    while not shutdown_event.is_set():
        try:
            # Wait for 10 minutes before next cleanup
            await asyncio.wait_for(shutdown_event.wait(), timeout=600)
        except asyncio.TimeoutError:
            logger.info("Running corpse decay...")
            # Player corpses decay after 24 hours
            player_corpse_limit = time.time() - (24 * 3600)
            player_corpses_removed = await database.remove_expired_player_corpses(player_corpse_limit)
            
            # NPC corpses decay after 2 hours
            npc_corpse_limit = time.time() - (2 * 3600)
            npc_corpses_removed = await database.remove_expired_npc_corpses(npc_corpse_limit)
            
            if player_corpses_removed > 0 or npc_corpses_removed > 0:
                logger.info(f"Decayed {player_corpses_removed} player corpses and {npc_corpses_removed} NPC corpses.")

async def main() -> None:
    """Start the bot and wait for a shutdown signal."""
    load_dotenv()
    TOKEN = os.getenv("TELEGRAM_TOKEN")

    if not TOKEN or TOKEN == "YOUR_TELEGRAM_BOT_TOKEN_HERE":
        logger.error("TELEGRAM_TOKEN is not set! Please edit your .env file.")
        return

    await database.create_tables()

    application = Application.builder().token(TOKEN).build()

    application.add_handler(CommandHandler("start", handlers.start))
    application.add_handler(CommandHandler("map", handlers.export_map))
    application.add_handler(CommandHandler("spawns", handlers.spawn_stats))
    application.add_handler(CallbackQueryHandler(handlers.button_handler))

    async with application:
        await application.start()
        await application.updater.start_polling(allowed_updates=Update.ALL_TYPES)
        logger.info("Bot is running and polling for updates...")
        
        # Start the spawn manager
        from bot import spawn_manager
        await spawn_manager.start_spawn_manager()
        
        # Start the background tasks
        decay_task = asyncio.create_task(decay_dropped_items())
        stamina_task = asyncio.create_task(regenerate_stamina())
        combat_timer_task = asyncio.create_task(check_combat_timers())
        corpse_decay_task = asyncio.create_task(decay_corpses())

        await shutdown_event.wait()

        await application.updater.stop()
        await application.stop()
        
        # Ensure the background tasks are also cancelled on shutdown
        decay_task.cancel()
        stamina_task.cancel()
        combat_timer_task.cancel()
        corpse_decay_task.cancel()
        logger.info("Bot has been shut down.")


if __name__ == "__main__":
    signal.signal(signal.SIGINT, signal_handler)
    signal.signal(signal.SIGTERM, signal_handler)

    try:
        asyncio.run(main())
    except (KeyboardInterrupt, SystemExit):
        logger.info("Main function interrupted.")
