import asyncio
import logging
import json
import random
from aiohttp import web

logger = logging.getLogger("gamble_bot.web_server")

# Global registries for connection and events
minecraft_ws = None
pending_deposits = {}  # code -> {username: str, amount: float}
pending_registrations = {}  # username.lower() -> {discord_id: int, code: str}
payout_futures = {}    # tx_id -> asyncio.Future

info_futures = {}      # req_id -> asyncio.Future

class WebServer:
    def __init__(self, bot):
        self.bot = bot
        self.app = web.Application()
        self.app.router.add_get('/ws', self.websocket_handler)
        self.runner = None

    async def start(self):
        """Starts the local web server on port 8000."""
        self.runner = web.AppRunner(self.app)
        await self.runner.setup()
        site = web.TCPSite(self.runner, '127.0.0.1', 8000)
        await site.start()
        logger.info("Local Web Server started on http://127.0.0.1:8000/ws")

    async def stop(self):
        """Stops the web server."""
        if self.runner:
            await self.runner.cleanup()
            logger.info("Local Web Server stopped.")

    async def websocket_handler(self, request):
        """Handles the WebSocket connection with the Minecraft Bot."""
        global minecraft_ws
        ws = web.WebSocketResponse()
        await ws.prepare(request)

        minecraft_ws = ws
        logger.info("Minecraft client bot connected to WebSocket bridge.")

        try:
            async for msg in ws:
                if msg.type == web.WSMsgType.TEXT:
                    data = json.loads(msg.data)
                    msg_type = data.get("type")
                    
                    if msg_type == "auto_deposit":
                        # Received automatic payment notification from Minecraft Bot
                        username = data.get("username")
                        amount = float(data.get("amount"))
                        
                        from database import db
                        import config
                        
                        # Find discord ID linked to this Minecraft name
                        query = "SELECT discord_id FROM users WHERE LOWER(game_username) = LOWER(?)" if config.USE_SQLITE else \
                                "SELECT discord_id FROM users WHERE LOWER(game_username) = LOWER($1)"
                        user_row = await db.fetchrow(query, username)
                        
                        if user_row:
                            discord_id = user_row["discord_id"]
                            try:
                                # Credit balance atomically
                                new_balance = await db.update_balance(
                                    discord_id, amount, "deposit", 
                                    f"Oyun içi otomatik yükleme (Minecraft: {username})"
                                )
                                
                                # Send success WebSocket back to Minecraft bot
                                response = {
                                    "type": "auto_deposit_result",
                                    "username": username,
                                    "amount": amount,
                                    "success": True,
                                    "new_balance": new_balance
                                }
                                await ws.send_str(json.dumps(response))
                                
                                # Send transaction logs
                                discord_user = self.bot.get_user(discord_id) or await self.bot.fetch_user(discord_id)
                                from utils.logger import log_transaction
                                await log_transaction(
                                    bot=self.bot,
                                    user=discord_user,
                                    tx_type="deposit",
                                    amount=amount,
                                    status="completed",
                                    details=f"Oyun içi otomatik yükleme. Oyun Adı: {username}"
                                )
                                
                                # Try to notify the user via DM
                                try:
                                    import discord
                                    from datetime import datetime
                                    dm_embed = discord.Embed(
                                        title="💰 Otomatik Bakiye Yüklendi!",
                                        description="Oyun içinden gönderdiğiniz tutar tespit edilerek hesabınıza aktarıldı.",
                                        color=discord.Color.green(),
                                        timestamp=datetime.utcnow()
                                    )
                                    dm_embed.add_field(name="Minecraft Kullanıcı Adınız", value=f"`{username}`", inline=True)
                                    from utils.security import format_money_string
                                    dm_embed.add_field(name="Yüklenen Tutar", value=f"💰 **+{format_money_string(amount)}**", inline=True)
                                    dm_embed.add_field(name="Yeni Bakiyeniz", value=f"💰 **{format_money_string(new_balance)}**", inline=True)
                                    await discord_user.send(embed=dm_embed)
                                except Exception as dm_err:
                                    logger.warning(f"Failed to send deposit success DM: {dm_err}")
                                
                                # Send public announcement in public_log_channel (bakiye-islemleri)
                                pub_channel_id = getattr(config, "PUBLIC_LOG_CHANNEL_ID", None)
                                if pub_channel_id:
                                    try:
                                        import discord
                                        pub_channel = self.bot.get_channel(pub_channel_id) or await self.bot.fetch_channel(pub_channel_id)
                                        if pub_channel:
                                            pub_embed = discord.Embed(
                                                title="📥 Bakiye Yüklendi!",
                                                description=f"🎉 {discord_user.mention} (**{username}**) oyunda bota **{amount:,.2f}** birim ödeme göndererek hesabına bakiye yükledi!",
                                                color=discord.Color.green(),
                                                timestamp=discord.utils.utcnow()
                                            )
                                            pub_embed.set_thumbnail(url=discord_user.display_avatar.url)
                                            await pub_channel.send(embed=pub_embed)
                                    except Exception as pub_err:
                                        logger.error(f"Failed to send public deposit announcement: {pub_err}")
                                    
                            except Exception as e:
                                logger.error(f"Error processing auto deposit in DB: {e}")
                                response = {
                                    "type": "auto_deposit_result",
                                    "username": username,
                                    "amount": amount,
                                    "success": False,
                                    "reason": "database_error"
                                }
                                await ws.send_str(json.dumps(response))
                        else:
                            # User is not linked to a Discord ID
                            logger.warning(f"Auto deposit failed: Minecraft username '{username}' is not linked to any Discord account.")
                            response = {
                                "type": "auto_deposit_result",
                                "username": username,
                                "amount": amount,
                                "success": False,
                                "reason": "user_not_linked"
                            }
                            await ws.send_str(json.dumps(response))

                    elif msg_type == "verify_code":
                        username = data.get("username")
                        code = data.get("code")
                        
                        user_key = username.lower()
                        if user_key in pending_registrations:
                            reg_data = pending_registrations[user_key]
                            import time
                            now = time.time()
                            
                            # 1. Check 5-minute expiration (300 seconds)
                            if now - reg_data.get("created_at", 0) > 300:
                                del pending_registrations[user_key]
                                logger.warning(f"Registration code for '{username}' expired (> 5 minutes).")
                                response = {
                                    "type": "verify_result",
                                    "username": username,
                                    "success": False,
                                    "reason": "code_expired"
                                }
                                await ws.send_str(json.dumps(response))
                                
                                try:
                                    discord_user = self.bot.get_user(reg_data["discord_id"]) or await self.bot.fetch_user(reg_data["discord_id"])
                                    if discord_user:
                                        await discord_user.send("⏱️ **Eşleştirme Kodu Süresi Doldu:** Ürettiğiniz 6 haneli kod 5 dakika içinde kullanılmadığı için güvenlik amacıyla iptal edildi. Lütfen Discord'dan yeni bir kod alınız.")
                                except Exception:
                                    pass
                            
                            # 2. Check code match
                            elif str(reg_data.get("code", "")).strip() == str(code).strip():
                                discord_id = reg_data["discord_id"]
                                
                                from database import db
                                try:
                                    # Complete registration in DB
                                    await db.register_user(discord_id, username)
                                    
                                    # Send success WebSocket reply
                                    response = {
                                        "type": "verify_result",
                                        "username": username,
                                        "success": True
                                    }
                                    await ws.send_str(json.dumps(response))
                                    
                                    # Remove from pending list (single-use)
                                    del pending_registrations[user_key]
                                    logger.info(f"Successfully linked Discord user {discord_id} to Minecraft account {username}")
                                    
                                    # Send success message to Discord user via DM
                                    try:
                                        import discord
                                        discord_user = self.bot.get_user(discord_id) or await self.bot.fetch_user(discord_id)
                                        dm_embed = discord.Embed(
                                            title="✅ Hesap Başarıyla Eşleştirildi!",
                                            description=f"Discord hesabınız artık `{username}` Minecraft karakteri ile eşleşti.",
                                            color=discord.Color.green(),
                                            timestamp=discord.utils.utcnow()
                                        )
                                        await discord_user.send(embed=dm_embed)
                                    except Exception as dm_err:
                                        logger.warning(f"Failed to send verification success DM: {dm_err}")
                                        
                                except Exception as db_err:
                                    logger.error(f"Error registering user in DB: {db_err}")
                                    response = {
                                        "type": "verify_result",
                                        "username": username,
                                        "success": False,
                                        "reason": "database_error"
                                    }
                                    await ws.send_str(json.dumps(response))
                            else:
                                # Wrong code entered -> Increment attempts
                                reg_data["attempts"] = reg_data.get("attempts", 0) + 1
                                if reg_data["attempts"] >= 3:
                                    del pending_registrations[user_key]
                                    logger.warning(f"Registration code for '{username}' revoked due to 3 failed attempts.")
                                    response = {
                                        "type": "verify_result",
                                        "username": username,
                                        "success": False,
                                        "reason": "too_many_attempts"
                                    }
                                    await ws.send_str(json.dumps(response))
                                    
                                    try:
                                        discord_user = self.bot.get_user(reg_data["discord_id"]) or await self.bot.fetch_user(reg_data["discord_id"])
                                        if discord_user:
                                            await discord_user.send("🚨 **Güvenlik Uyarısı:** 3 kez üst üste hatalı kod girildiği için eşleştirme kodu iptal edildi. Lütfen Discord'dan yeni bir kod alınız.")
                                    except Exception:
                                        pass
                                else:
                                    response = {
                                        "type": "verify_result",
                                        "username": username,
                                        "success": False,
                                        "reason": "invalid_code",
                                        "remaining_attempts": 3 - reg_data["attempts"]
                                    }
                                    await ws.send_str(json.dumps(response))
                        else:
                            # Not found or already expired/consumed
                            response = {
                                "type": "verify_result",
                                "username": username,
                                "success": False,
                                "reason": "invalid_code"
                            }
                            await ws.send_str(json.dumps(response))

                    elif msg_type == "payout_result":
                        # Received payout completion response
                        tx_id = data.get("tx_id")
                        success = data.get("success")
                        reason = data.get("reason", "")
                        
                        if tx_id in payout_futures:
                            payout_futures[tx_id].set_result((success, reason))

                    elif msg_type == "bot_info_response":
                        req_id = data.get("req_id")
                        if req_id in info_futures:
                            info_futures[req_id].set_result(data)

                    elif msg_type == "bot_status":
                        # Realtime status update from Minecraft client bot
                        is_conn = bool(data.get("connected"))
                        mc_user = data.get("username", "KolicGambleBot")
                        mc_srv = data.get("server", "kolichub.net")
                        mc_reason = data.get("reason", "")
                        asyncio.create_task(update_bot_status_display(self.bot, is_conn, mc_user, mc_srv, mc_reason))
                            
                elif msg.type == web.WSMsgType.ERROR:
                    logger.error(f"WebSocket connection closed with error: {ws.exception()}")
        finally:
            minecraft_ws = None
            logger.warning("Minecraft client bot disconnected from WebSocket bridge.")
            asyncio.create_task(update_bot_status_display(self.bot, False, "KolicGambleBot", "kolichub.net", "Bot köprü bağlantısı kesildi"))
            # Fail all pending payouts since connection is lost
            for tx_id, fut in list(payout_futures.items()):
                if not fut.done():
                    fut.set_result((False, "Minecraft bot disconnected."))
            for req_id, fut in list(info_futures.items()):
                if not fut.done():
                    fut.set_result({"success": False, "is_online": False, "reason": "Minecraft bot disconnected."})
        
        return ws


_last_status_cache = {
    "is_online": None,
    "reason": None,
    "last_update": 0
}

async def update_bot_status_display(bot, is_online: bool, username: str = "KolicGambleBot", server_ip: str = "kolichub.net", reason: str = ""):
    """Updates the dedicated Discord bot status channel and embed only when state changes, avoiding rate limits."""
    global _last_status_cache
    import time
    import config
    import discord
    from datetime import datetime

    now = time.time()
    
    # If state hasn't changed and last update was less than 5 minutes ago, skip API call completely!
    if _last_status_cache["is_online"] == is_online and _last_status_cache["reason"] == reason and (now - _last_status_cache["last_update"]) < 300:
        return

    channel_id = getattr(config, "BOT_STATUS_CHANNEL_ID", None)
    if not channel_id:
        return

    try:
        channel = bot.get_channel(channel_id) or await bot.fetch_channel(channel_id)
    except Exception:
        return

    if not channel:
        return

    # Update cache
    _last_status_cache["is_online"] = is_online
    _last_status_cache["reason"] = reason
    _last_status_cache["last_update"] = now

    # Update channel name safely (subject to Discord 2 edits / 10 min rate limits)
    try:
        new_name = "🟢・bot-aktif" if is_online else "🔴・bot-kapali"
        if channel.name != new_name:
            await channel.edit(name=new_name, reason="Minecraft bot canlı durum güncellemesi")
    except Exception as e:
        logger.debug(f"Channel rename rate limit or error: {e}")

    # Build status embed
    if is_online:
        embed = discord.Embed(
            title="🟢 Minecraft Botu Aktif & Çevrimiçi",
            description=(
                f"✅ **`{username}`** başarıyla Minecraft sunucusuna bağlı!\n\n"
                f"🎮 **Sunucu IP:** `{server_ip}`\n"
                f"🤖 **Bot Adı:** `{username}`\n"
                f"📥 **Para Yatırma:** `🟢 Aktif` (`/pay {username} <miktar>`)\n"
                f"📤 **Para Çekme:** `🟢 Aktif` (Anında transfer)\n"
                f"⚡ **Sistem Durumu:** Tüm oyun içi transferler canlı olarak işleniyor."
            ),
            color=discord.Color.green(),
            timestamp=datetime.utcnow()
        )
        embed.set_footer(text="Canlı Sistem Durumu • KolicGambleBot")
    else:
        embed = discord.Embed(
            title="🔴 Minecraft Botu Çevrimdışı (Kapalı)",
            description=(
                f"⚠️ **`{username}`** şu anda Minecraft sunucusuna bağlı değil.\n\n"
                f"🎮 **Sunucu IP:** `{server_ip}`\n"
                f"🛑 **Para Yatırma / Çekme:** `🔴 Geçici Olarak Beklemede`\n"
                f"💡 **Bilgi:** *Bot sunucuya tekrar bağlandığı an sistem otomatik olarak yeşile dönecektir.*\n"
                + (f"📌 **Sebep:** `{reason}`\n" if reason else "")
            ),
            color=discord.Color.red(),
            timestamp=datetime.utcnow()
        )
        embed.set_footer(text="Canlı Sistem Durumu • KolicGambleBot")

    # Find existing status message in channel or send a new one
    try:
        status_msg = None
        async for msg in channel.history(limit=10):
            if msg.author == bot.user:
                status_msg = msg
                break
        
        if status_msg:
            await status_msg.edit(embed=embed)
        else:
            await channel.send(embed=embed)
            
        logger.info(f"📡 [BOT STATUS] Discord paneli güncellendi -> {'🟢 AKTİF' if is_online else '🔴 KAPALI'}")
    except Exception as e:
        logger.warning(f"Status embed update warning: {e}")


# Helper function for cogs to execute a payout via the Minecraft Client bot
async def send_minecraft_payout(tx_id: int, username: str, amount: float) -> tuple[bool, str]:
    """
    Sends a WebSocket packet to the Minecraft bot to execute the in-game command.
    Returns (success: bool, reason: str)
    """
    global minecraft_ws
    if not minecraft_ws or minecraft_ws.closed:
        return False, "Minecraft botu çevrimdışı (bağlantı yok)."

    # Create a future to wait for response
    loop = asyncio.get_running_loop()
    fut = loop.create_future()
    payout_futures[tx_id] = fut

    # Send command request
    payload = {
        "type": "execute_payout",
        "tx_id": tx_id,
        "username": username,
        "amount": amount
    }
    
    try:
        await minecraft_ws.send_str(json.dumps(payload))
        # Wait for the result with a 15 second timeout
        success, reason = await asyncio.wait_for(fut, timeout=15.0)
        return success, reason
    except asyncio.TimeoutError:
        return False, "Minecraft botundan zaman aşımı (yanıt vermedi)."
    except Exception as e:
        logger.error(f"Error in send_minecraft_payout: {e}")
        return False, str(e)
    finally:
        if tx_id in payout_futures:
            del payout_futures[tx_id]


async def send_minecraft_chat(message: str) -> bool:
    """Sends a chat message or command through the in-game Minecraft bot."""
    global minecraft_ws
    if not minecraft_ws or minecraft_ws.closed:
        return False
    try:
        await minecraft_ws.send_str(json.dumps({"type": "remote_chat", "message": message}))
        return True
    except Exception as e:
        logger.error(f"Error in send_minecraft_chat: {e}")
        return False


async def send_minecraft_reconnect() -> bool:
    """Requests the Minecraft bot to disconnect and reconnect to the Minecraft server."""
    global minecraft_ws
    if not minecraft_ws or minecraft_ws.closed:
        return False
    try:
        await minecraft_ws.send_str(json.dumps({"type": "remote_reconnect"}))
        return True
    except Exception as e:
        logger.error(f"Error in send_minecraft_reconnect: {e}")
        return False


async def get_minecraft_info() -> dict:
    """Requests live player position, health, food, and server info from the in-game bot."""
    global minecraft_ws
    if not minecraft_ws or minecraft_ws.closed:
        return {"success": False, "is_online": False, "reason": "Bot WebSocket köprüsüne bağlı değil."}
    
    import random
    req_id = f"info_{random.randint(100000, 999999)}"
    loop = asyncio.get_running_loop()
    fut = loop.create_future()
    info_futures[req_id] = fut
    
    try:
        await minecraft_ws.send_str(json.dumps({"type": "get_bot_info", "req_id": req_id}))
        result = await asyncio.wait_for(fut, timeout=5.0)
        return result
    except asyncio.TimeoutError:
        return {"success": False, "is_online": False, "reason": "Bot bilgi yanıtı zaman aşımına uğradı."}
    except Exception as e:
        return {"success": False, "is_online": False, "reason": str(e)}
    finally:
        if req_id in info_futures:
            del info_futures[req_id]
