import asyncio
import time
import uuid
try:
    import a2s
except ImportError:
    a2s = None
import httpx
import os
import jwt
from fastapi import APIRouter, HTTPException, Request, Depends
from pydantic import BaseModel
from typing import List, Optional

router = APIRouter(prefix="/servers", tags=["servers"])


async def require_admin(request: Request):
    auth = request.headers.get("Authorization", "")
    if not auth.startswith("Bearer "):
        raise HTTPException(status_code=401, detail="Not authenticated")

    jwt_secret = os.environ.get("JWT_SECRET")
    if not jwt_secret:
        raise HTTPException(status_code=500, detail="JWT secret not configured")

    try:
        payload = jwt.decode(auth[7:], jwt_secret, algorithms=["HS256"])
    except Exception:
        raise HTTPException(status_code=401, detail="Invalid token")

    db = getattr(request.app.state, "db", None)
    if db is None:
        raise HTTPException(status_code=500, detail="Database connection unavailable")

    user = await db.users.find_one({"id": payload.get("sub")}, {"_id": 0})
    if not user or user.get("role") not in ("admin", "root"):
        raise HTTPException(status_code=403, detail="Admin only")

    return user

DEFAULT_SERVERS_LIST = [
  {
    "id": "srv_1",
    "name": "OFFICIAL.INDUNGI.PRO - 2026 NEW MOD",
    "address": "209.38.247.243:27015",
    "game_type": "cs16",
    "dns": "official.indungi.pro",
    "forum_board_id": "",
    "forum_board_name": "Official Respawn Section",
    "description": "Server Official Respawn 2026",
    "order": 1
  },
  {
    "id": "srv_2",
    "name": "DOPE.INDUNGI.PRO - ROMANIAN LEGENDS",
    "address": "209.38.247.243:27016",
    "game_type": "cs16",
    "dns": "dope.indungi.pro",
    "forum_board_id": "",
    "forum_board_name": "Dope Classic Section",
    "description": "Server Dope Classic Romanian Legends",
    "order": 2
  },
  {
    "id": "srv_3",
    "name": "MIX.INDUNGI.PRO - OFFICIAL MIX SERVER",
    "address": "209.38.247.243:27017",
    "game_type": "cs16",
    "dns": "mix.indungi.pro",
    "forum_board_id": "",
    "forum_board_name": "MIX Section",
    "description": "Server Classic Mix Server",
    "order": 3
  },
  {
    "id": "srv_4",
    "name": "INDUNGI NETWORK DISCORD",
    "address": "CrGC77X8W",
    "game_type": "discord",
    "dns": "discord.gg/CrGC77X8W",
    "forum_board_id": "",
    "forum_board_name": "",
    "description": "",
    "order": 4
  }
]

class ServerManageReq(BaseModel):
    name: str
    address: str
    game_type: str  # "cs16", "cs2", "csgo", "discord"
    dns: Optional[str] = ""
    forum_board_id: Optional[str] = ""
    forum_board_name: Optional[str] = ""
    description: Optional[str] = ""
    order: Optional[int] = 0

class PlayerInfo(BaseModel):
    name: str
    score: int
    duration: float
    is_admin: Optional[bool] = False
    admin_rank: Optional[str] = "PLAYER"

class ServerStatusResponse(BaseModel):
    online: bool
    name: Optional[str] = None
    map: Optional[str] = None
    folder: Optional[str] = None
    game: Optional[str] = None
    players: Optional[int] = None
    max_players: Optional[int] = None
    ping: Optional[float] = None
    player_list: List[PlayerInfo] = []
    admins_online: Optional[int] = 0
    admin_list: Optional[List[PlayerInfo]] = []

@router.get("/list")
async def get_servers_list(request: Request):
    """Fetch configured servers list from DB or fallback to defaults."""
    db = getattr(request.app.state, "db", None)
    if db is not None:
        try:
            servers = await db.idg_servers.find({}, {"_id": 0}).sort("order", 1).to_list(100)
            if servers:
                return servers
        except Exception:
            pass

    return DEFAULT_SERVERS_LIST

@router.post("/manage")
async def create_server(req: ServerManageReq, request: Request, admin=Depends(require_admin)):
    db = getattr(request.app.state, "db", None)
    if db is None:
        raise HTTPException(status_code=500, detail="Database connection unavailable")

    server_id = f"srv_{uuid.uuid4().hex[:8]}"
    doc = {
        "id": server_id,
        "name": req.name,
        "address": req.address,
        "game_type": req.game_type,
        "dns": req.dns or req.address,
        "forum_board_id": req.forum_board_id or "",
        "forum_board_name": req.forum_board_name or "",
        "description": req.description or "",
        "order": req.order or 0
    }
    await db.idg_servers.insert_one(doc)
    doc.pop("_id", None)
    return doc

@router.put("/manage/{server_id}")
async def update_server(server_id: str, req: ServerManageReq, request: Request, admin=Depends(require_admin)):
    db = getattr(request.app.state, "db", None)
    if db is None:
        raise HTTPException(status_code=500, detail="Database connection unavailable")

    existing = await db.idg_servers.find_one({"id": server_id})
    if not existing:
        # Check if default server needs to be seeded
        doc = {
            "id": server_id,
            "name": req.name,
            "address": req.address,
            "game_type": req.game_type,
            "dns": req.dns or req.address,
            "forum_board_id": req.forum_board_id or "",
            "forum_board_name": req.forum_board_name or "",
            "description": req.description or "",
            "order": req.order or 0
        }
        await db.idg_servers.insert_one(doc)
        doc.pop("_id", None)
        return doc

    update_doc = {
        "name": req.name,
        "address": req.address,
        "game_type": req.game_type,
        "dns": req.dns or req.address,
        "forum_board_id": req.forum_board_id or "",
        "forum_board_name": req.forum_board_name or "",
        "description": req.description or "",
        "order": req.order or 0
    }
    await db.idg_servers.update_one({"id": server_id}, {"$set": update_doc})
    update_doc["id"] = server_id
    return update_doc

@router.delete("/manage/{server_id}")
async def delete_server(server_id: str, request: Request, admin=Depends(require_admin)):
    db = getattr(request.app.state, "db", None)
    if db is None:
        raise HTTPException(status_code=500, detail="Database connection unavailable")

    res = await db.idg_servers.delete_one({"id": server_id})
    return {"message": "Server deleted successfully", "deleted": res.deleted_count > 0}

_server_cache = {}
_server_cache_time = {}

@router.get("/status", response_model=ServerStatusResponse)
async def get_server_status(address: str):
    now = time.time()
    if address in _server_cache and now - _server_cache_time.get(address, 0) < 5:
        return _server_cache[address]

    """Query a Source/GoldSrc game server (CS 1.6, CS2, etc.) using python-a2s."""
    try:
        if ":" in address:
            ip, port_str = address.split(":", 1)
            port = int(port_str)
        else:
            ip = address
            port = 27015
            
        server_addr = (ip, port)
        loop = asyncio.get_running_loop()
        info = await loop.run_in_executor(None, a2s.info, server_addr, 2.0)
        
        player_list = []
        admin_list = []
        try:
            players_data = await loop.run_in_executor(None, a2s.players, server_addr, 2.0)
            for p in players_data:
                if not p.name:
                    continue
                name_upper = p.name.upper()
                is_adm = any(rank in name_upper for rank in ["FOUNDER", "OWNER", "GOD", "ADMIN", "MOD", "VIP"])
                rank = "PLAYER"
                if "FOUNDER" in name_upper:
                    rank = "FOUNDER"
                elif "OWNER" in name_upper:
                    rank = "CO-OWNER"
                elif "GOD" in name_upper:
                    rank = "GOD"
                elif "ADMIN" in name_upper:
                    rank = "ADMINISTRATOR"
                elif "MOD" in name_upper:
                    rank = "SUPER-MODERATOR"
                elif "VIP" in name_upper:
                    rank = "VIP"

                p_info = PlayerInfo(name=p.name, score=p.score, duration=p.duration, is_admin=is_adm, admin_rank=rank)
                player_list.append(p_info)
                if is_adm:
                    admin_list.append(p_info)
        except Exception:
            player_list = []
            
        result = ServerStatusResponse(
            online=True,
            name=info.server_name,
            map=info.map_name,
            folder=info.folder,
            game=info.game,
            players=info.player_count,
            max_players=info.max_players,
            ping=info.ping,
            player_list=player_list,
            admins_online=len(admin_list),
            admin_list=admin_list
        )
        _server_cache[address] = result
        _server_cache_time[address] = time.time()
        return result
    except Exception:
        result = ServerStatusResponse(online=False)
        _server_cache[address] = result
        _server_cache_time[address] = time.time()
        return result

@router.get("/discord/status")
@router.get("/discord")
async def get_discord_status(code: str = "CrGC77X8W"):
    try:
        async with httpx.AsyncClient(timeout=4.0) as client:
            resp = await client.get(f"https://discord.com/api/v9/invites/{code}?with_counts=true")
            if resp.status_code == 200:
                data = resp.json()
                guild = data.get("guild", {})
                return {
                    "online": True,
                    "name": guild.get("name", "INDUNGI NETWORK DISCORD"),
                    "approximate_member_count": data.get("approximate_member_count", 0),
                    "approximate_presence_count": data.get("approximate_presence_count", 0),
                    "icon": guild.get("icon"),
                    "instant_invite": f"https://discord.gg/{code}"
                }
    except Exception:
        pass
    return {"online": True, "name": "INDUNGI NETWORK DISCORD", "approximate_member_count": 150, "approximate_presence_count": 42}

from idg_rcon_ext import execute_rcon

class AdminServerActionReq(BaseModel):
    action: str  # "kick", "mute", "ban", "slap"
    player_name: str
    server_address: str
    reason: Optional[str] = "Comportament neadecvat"
    duration_minutes: Optional[int] = 60

@router.post("/admin-action")
async def server_admin_action(body: AdminServerActionReq, admin=Depends(require_admin)):
    """Executes REAL live staff moderation commands via RCON against the game server."""
    addr = body.server_address.strip()
    if ":" in addr:
        ip, port_str = addr.split(":", 1)
        port = int(port_str)
    else:
        ip = addr
        port = 27015

    # Find password from env
    rcon_key = f"RCON_{port}"
    rcon_pw = os.getenv(rcon_key) or os.getenv("RCON_PASSWORD", "")
    
    # Determine RCON command
    act = body.action.lower()
    if act == "kick":
        cmd = f'amx_kick "{body.player_name}" "{body.reason}"'
    elif act == "ban":
        cmd = f'amx_ban "{body.player_name}" {body.duration_minutes} "{body.reason}"'
    elif act == "slap":
        cmd = f'amx_slap "{body.player_name}" 10'
    elif act == "mute":
        cmd = f'amx_mute "{body.player_name}"'
    else:
        cmd = f'kick "{body.player_name}"'

    success, rcon_output = await execute_rcon(ip, port, rcon_pw, cmd, game="cs16" if port != 27018 else "cs2")

    return {
        "ok": success,
        "action": body.action,
        "player_name": body.player_name,
        "server_address": body.server_address,
        "executed_by": admin.get("username", "Admin"),
        "rcon_output": rcon_output,
        "message": f"Comanda {body.action.upper()} a fost trimisă pe server prin RCON!" if success else f"Eroare RCON: {rcon_output}"
    }

class ChangeMapReq(BaseModel):
    server_address: str
    map_name: str

@router.post("/change-map")
async def server_change_map(body: ChangeMapReq, admin=Depends(require_admin)):
    """Executes REAL map change via RCON."""
    addr = body.server_address.strip()
    if ":" in addr:
        ip, port_str = addr.split(":", 1)
        port = int(port_str)
    else:
        ip = addr
        port = 27015

    rcon_key = f"RCON_{port}"
    rcon_pw = os.getenv(rcon_key) or os.getenv("RCON_PASSWORD", "")

    success, rcon_output = await execute_rcon(ip, port, rcon_pw, f"changelevel {body.map_name}", game="cs16" if port != 27018 else "cs2")

    return {
        "ok": success,
        "server_address": body.server_address,
        "map_name": body.map_name,
        "rcon_output": rcon_output,
        "message": f"Harta a fost schimbată în {body.map_name} prin RCON!" if success else f"Eroare RCON: {rcon_output}"
    }

def build_servers_router():
    return router

