import os
import uuid
from typing import Optional, Dict, Any
from idg_rcon_ext import execute_rcon

DEFAULT_SERVERS = [
    {
        "id": "srv_1",
        "name": "OFFICIAL.INDUNGI.PRO [MIX 1]",
        "ip": "209.38.247.243",
        "port": 27015,
        "game": "cs16",
        "env_rcon_key": "RCON_27015"
    },
    {
        "id": "srv_2",
        "name": "DOPE.INDUNGI.PRO [MIX 2]",
        "ip": "209.38.247.243",
        "port": 27016,
        "game": "cs16",
        "env_rcon_key": "RCON_27016"
    },
    {
        "id": "srv_3",
        "name": "MIX.INDUNGI.PRO [WAR / CUP]",
        "ip": "209.38.247.243",
        "port": 27017,
        "game": "cs16",
        "env_rcon_key": "RCON_27017"
    },
    {
        "id": "srv_cs2",
        "name": "CS2.INDUNGI.PRO [5v5 MATCH]",
        "ip": "209.38.247.243",
        "port": 27018,
        "game": "cs2",
        "env_rcon_key": "RCON_27018"
    }
]

class TournamentServerPool:
    def __init__(self, db):
        self.db = db

    def _get_rcon_password(self, server: Dict[str, Any]) -> str:
        env_key = server.get("env_rcon_key")
        if env_key and os.getenv(env_key):
            return os.getenv(env_key)
        # Check global fallback
        return os.getenv("RCON_PASSWORD", server.get("rcon_password", ""))

    async def get_available_server(self, game: str = "cs16") -> Optional[Dict[str, Any]]:
        """Finds an unallocated server for the requested game."""
        # 1. Fetch servers from DB if exists
        db_servers = await self.db.idg_servers.find({"game": game}).to_list(20)
        servers = db_servers if db_servers else [s for s in DEFAULT_SERVERS if s["game"] == game]

        # 2. Check active reservations in DB
        active_reservations = await self.db.idg_server_reservations.find({}).to_list(100)
        busy_addresses = {f"{r['server_ip']}:{r['server_port']}" for r in active_reservations}

        for s in servers:
            ip = s.get("ip") or s.get("address", "").split(":")[0]
            port = int(s.get("port") or s.get("address", "").split(":")[1] if ":" in str(s.get("address", "")) else 27015)
            addr = f"{ip}:{port}"
            if addr not in busy_addresses:
                return {
                    "id": s.get("id"),
                    "name": s.get("name"),
                    "ip": ip,
                    "port": port,
                    "game": game,
                    "rcon_password": self._get_rcon_password(s)
                }
        return None

    async def assign_server(self, tournament_id: str, match_id: str, game: str = "cs16", map_name: str = "de_dust2") -> Optional[Dict[str, Any]]:
        """
        Reserves a dedicated game server for a tournament match, sets match password and loads map via RCON.
        """
        server = await self.get_available_server(game)
        if not server:
            # All dedicated servers are currently in use, fall back to virtual assignment
            default_srv = [s for s in DEFAULT_SERVERS if s["game"] == game]
            server = default_srv[0] if default_srv else DEFAULT_SERVERS[0]
            server["rcon_password"] = self._get_rcon_password(server)

        # Generate unique secure match password
        match_short = match_id.split("_")[-1] if "_" in match_id else uuid.uuid4().hex[:4]
        match_password = f"ind_{match_short}"

        # Execute live RCON to prepare the game server
        rcon_pw = server.get("rcon_password")
        if rcon_pw:
            # 1. Set match password
            await execute_rcon(server["ip"], server["port"], rcon_pw, f'sv_password "{match_password}"', game=game)
            # 2. Set map
            await execute_rcon(server["ip"], server["port"], rcon_pw, f"changelevel {map_name}", game=game)
            # 3. Restart game
            await execute_rcon(server["ip"], server["port"], rcon_pw, "mp_restartgame 1", game=game)

        # Save reservation in DB
        res_doc = {
            "tournament_id": tournament_id,
            "match_id": match_id,
            "server_id": server.get("id"),
            "server_name": server.get("name"),
            "server_ip": server["ip"],
            "server_port": server["port"],
            "server_password": match_password,
            "game": game,
            "assigned_at": os.getenv("NOW_ISO", "")
        }
        await self.db.idg_server_reservations.update_one(
            {"match_id": match_id},
            {"$set": res_doc},
            upsert=True
        )

        return {
            "server_ip": server["ip"],
            "server_port": server["port"],
            "server_name": server.get("name"),
            "server_password": match_password,
            "connect_url": f"steam://connect/{server['ip']}:{server['port']}/{match_password}"
        }

    async def release_server(self, match_id: str):
        """Unlocks the game server and clears password via RCON upon match conclusion."""
        res = await self.db.idg_server_reservations.find_one({"match_id": match_id})
        if res:
            server_ip = res.get("server_ip")
            server_port = res.get("server_port")
            game = res.get("game", "cs16")
            
            # Find RCON password
            matching = [s for s in DEFAULT_SERVERS if s["ip"] == server_ip and s["port"] == server_port]
            rcon_pw = self._get_rcon_password(matching[0]) if matching else os.getenv("RCON_PASSWORD", "")
            
            if rcon_pw:
                # Remove password
                await execute_rcon(server_ip, server_port, rcon_pw, 'sv_password ""', game=game)
                
            await self.db.idg_server_reservations.delete_one({"match_id": match_id})
