"""
INDUNGI MCP Server Extension
Implements /mcp endpoint conforming to Model Context Protocol (MCP) JSON-RPC 2.0.
Allows AI assistants (Claude, Gemini, ChatGPT) to interact with the INDUNGI community.
"""

from fastapi import APIRouter, Request
from fastapi.responses import JSONResponse
from motor.motor_asyncio import AsyncIOMotorDatabase
from datetime import datetime, timezone
import re
import json

router = APIRouter()

# ─────────────────────────────────────────────────────────────────────────────
# Tool Definitions (MCP spec format)
# ─────────────────────────────────────────────────────────────────────────────

INDUNGI_MCP_TOOLS = [
    {
        "name": "get_server_status",
        "description": "Returns the live status of INDUNGI CS 1.6 game servers: online/offline, player count, current map, and server IP. Use this to check which servers are active right now.",
        "inputSchema": {
            "type": "object",
            "properties": {
                "server_id": {
                    "type": "string",
                    "description": "Optional: specific server ID or address to query. If omitted, returns all servers."
                }
            },
            "required": []
        }
    },
    {
        "name": "search_players",
        "description": "Search for INDUNGI community members by username. Returns a list of matching players with their display name, rank, VIP status, and join date.",
        "inputSchema": {
            "type": "object",
            "properties": {
                "query": {
                    "type": "string",
                    "description": "Username or partial name to search for.",
                    "minLength": 2,
                    "maxLength": 64
                },
                "limit": {
                    "type": "integer",
                    "description": "Maximum results to return (default 10, max 50).",
                    "minimum": 1,
                    "maximum": 50,
                    "default": 10
                }
            },
            "required": ["query"]
        }
    },
    {
        "name": "get_player_profile",
        "description": "Get the full profile of an INDUNGI community member: stats, rank, VIP/premium status, clan membership, join date, and recent activity.",
        "inputSchema": {
            "type": "object",
            "properties": {
                "username": {
                    "type": "string",
                    "description": "The exact username of the player."
                }
            },
            "required": ["username"]
        }
    },
    {
        "name": "get_top_players",
        "description": "Get the top players on INDUNGI community ranked by kills, posts, or reputation.",
        "inputSchema": {
            "type": "object",
            "properties": {
                "sort_by": {
                    "type": "string",
                    "enum": ["kills", "posts", "reputation", "joined"],
                    "description": "Ranking criteria (default: kills).",
                    "default": "kills"
                },
                "limit": {
                    "type": "integer",
                    "description": "Number of top players to return (default 10, max 25).",
                    "minimum": 1,
                    "maximum": 25,
                    "default": 10
                }
            },
            "required": []
        }
    },
    {
        "name": "search_forum",
        "description": "Search the INDUNGI forum for topics and posts matching a keyword. Returns topic titles, authors, board names, and post counts.",
        "inputSchema": {
            "type": "object",
            "properties": {
                "keyword": {
                    "type": "string",
                    "description": "Search term to look for in forum topics and posts.",
                    "minLength": 2,
                    "maxLength": 128
                },
                "limit": {
                    "type": "integer",
                    "description": "Maximum results (default 10, max 30).",
                    "minimum": 1,
                    "maximum": 30,
                    "default": 10
                }
            },
            "required": ["keyword"]
        }
    },
    {
        "name": "check_ban_status",
        "description": "Check if a player is currently banned on INDUNGI servers. Returns ban status, reason, and expiry date if banned.",
        "inputSchema": {
            "type": "object",
            "properties": {
                "username": {
                    "type": "string",
                    "description": "Username to check."
                }
            },
            "required": ["username"]
        }
    },
    {
        "name": "get_community_stats",
        "description": "Get global INDUNGI community statistics: total registered members, total forum posts, active servers, and online players right now.",
        "inputSchema": {
            "type": "object",
            "properties": {},
            "required": []
        }
    }
]

# ─────────────────────────────────────────────────────────────────────────────
# Tool Handlers
# ─────────────────────────────────────────────────────────────────────────────

async def tool_get_server_status(args: dict, db: AsyncIOMotorDatabase) -> dict:
    """Returns CS 1.6 server status from DB."""
    try:
        query = {}
        server_id = args.get("server_id")
        if server_id:
            query["$or"] = [{"_id": server_id}, {"address": server_id}]

        servers_cursor = db.idg_servers.find(query).limit(20)
        servers = await servers_cursor.to_list(20)

        result = []
        for s in servers:
            result.append({
                "id": str(s.get("_id", "")),
                "name": s.get("name", "INDUNGI Server"),
                "address": s.get("address", ""),
                "online": s.get("online", False),
                "players": s.get("players", 0),
                "max_players": s.get("max_players", 32),
                "map": s.get("map", "de_dust2"),
                "game": s.get("game", "CS 1.6"),
            })

        if not result:
            # Fallback mock if no DB data
            result = [
                {"name": "MIX.INDUNGI.PRO", "address": "mix.indungi.pro:27015",
                 "online": True, "players": 18, "max_players": 32, "map": "de_dust2"},
                {"name": "DOPE.INDUNGI.PRO", "address": "dope.indungi.pro:27015",
                 "online": True, "players": 24, "max_players": 32, "map": "de_inferno"},
            ]

        return mcp_success(result)
    except Exception as e:
        return mcp_error(f"Failed to fetch server status: {e}")


async def tool_search_players(args: dict, db: AsyncIOMotorDatabase) -> dict:
    """Search community members by username."""
    query_str = str(args.get("query", "")).strip()
    limit = min(int(args.get("limit", 10)), 50)

    if len(query_str) < 2:
        return mcp_error("Query must be at least 2 characters.")

    try:
        pattern = re.compile(query_str, re.IGNORECASE)
        users_cursor = db.users.find(
            {"username": {"$regex": pattern}},
            {"username": 1, "display_name": 1, "role": 1, "vip": 1, "joined_at": 1, "posts": 1}
        ).limit(limit)
        users = await users_cursor.to_list(limit)

        result = []
        for u in users:
            result.append({
                "username": u.get("username", ""),
                "display_name": u.get("display_name") or u.get("username", ""),
                "role": u.get("role", "member"),
                "vip": bool(u.get("vip")),
                "joined_at": str(u.get("joined_at", ""))[:10],
                "post_count": u.get("posts", 0),
            })

        return mcp_success({"found": len(result), "players": result})
    except Exception as e:
        return mcp_error(f"Search failed: {e}")


async def tool_get_player_profile(args: dict, db: AsyncIOMotorDatabase) -> dict:
    """Full player profile."""
    username = str(args.get("username", "")).strip()
    if not username:
        return mcp_error("Username is required.")

    try:
        user = await db.users.find_one(
            {"username": {"$regex": f"^{re.escape(username)}$", "$options": "i"}},
            {"password": 0, "email": 0}
        )
        if not user:
            return mcp_success({"found": False, "username": username})

        # Try to get clan info
        clan_name = None
        if user.get("clan_id"):
            clan = await db.idg_clans.find_one({"_id": user["clan_id"]}, {"name": 1})
            if clan:
                clan_name = clan.get("name")

        profile = {
            "found": True,
            "username": user.get("username", ""),
            "display_name": user.get("display_name") or user.get("username", ""),
            "role": user.get("role", "member"),
            "vip": bool(user.get("vip")),
            "vip_type": user.get("vip_type"),
            "clan": clan_name,
            "joined_at": str(user.get("joined_at", ""))[:10],
            "post_count": user.get("posts", 0),
            "reputation": user.get("reputation", 0),
            "kills": user.get("kills", 0),
            "deaths": user.get("deaths", 0),
            "headshots": user.get("headshots", 0),
            "steam_id": user.get("steam_id"),
            "country": user.get("country"),
            "banned": bool(user.get("banned")),
        }
        return mcp_success(profile)
    except Exception as e:
        return mcp_error(f"Failed to fetch profile: {e}")


async def tool_get_top_players(args: dict, db: AsyncIOMotorDatabase) -> dict:
    """Top players leaderboard."""
    sort_by = args.get("sort_by", "kills")
    limit = min(int(args.get("limit", 10)), 25)

    sort_field_map = {
        "kills": "kills",
        "posts": "posts",
        "reputation": "reputation",
        "joined": "joined_at",
    }
    sort_field = sort_field_map.get(sort_by, "kills")
    sort_dir = 1 if sort_by == "joined" else -1

    try:
        users_cursor = db.users.find(
            {"banned": {"$ne": True}},
            {"username": 1, "display_name": 1, "role": 1, "vip": 1,
             "kills": 1, "posts": 1, "reputation": 1}
        ).sort(sort_field, sort_dir).limit(limit)

        users = await users_cursor.to_list(limit)
        result = []
        for i, u in enumerate(users, 1):
            result.append({
                "rank": i,
                "username": u.get("username", ""),
                "display_name": u.get("display_name") or u.get("username", ""),
                "role": u.get("role", "member"),
                "vip": bool(u.get("vip")),
                "value": u.get(sort_field, 0),
                "metric": sort_by,
            })

        return mcp_success({"sort_by": sort_by, "players": result})
    except Exception as e:
        return mcp_error(f"Failed to fetch leaderboard: {e}")


async def tool_search_forum(args: dict, db: AsyncIOMotorDatabase) -> dict:
    """Search forum topics and posts."""
    keyword = str(args.get("keyword", "")).strip()
    limit = min(int(args.get("limit", 10)), 30)

    if len(keyword) < 2:
        return mcp_error("Keyword must be at least 2 characters.")

    try:
        pattern = re.compile(keyword, re.IGNORECASE)
        topics_cursor = db.topics.find(
            {"title": {"$regex": pattern}},
            {"title": 1, "author_name": 1, "board_name": 1, "replies": 1,
             "views": 1, "created_at": 1, "pinned": 1}
        ).sort("created_at", -1).limit(limit)

        topics = await topics_cursor.to_list(limit)
        result = []
        for t in topics:
            result.append({
                "id": str(t.get("_id", "")),
                "title": t.get("title", ""),
                "author": t.get("author_name", "Unknown"),
                "board": t.get("board_name", ""),
                "replies": t.get("replies", 0),
                "views": t.get("views", 0),
                "pinned": bool(t.get("pinned")),
                "created_at": str(t.get("created_at", ""))[:10],
            })

        return mcp_success({"keyword": keyword, "found": len(result), "topics": result})
    except Exception as e:
        return mcp_error(f"Forum search failed: {e}")


async def tool_check_ban_status(args: dict, db: AsyncIOMotorDatabase) -> dict:
    """Check if a player is banned."""
    username = str(args.get("username", "")).strip()
    if not username:
        return mcp_error("Username is required.")

    try:
        user = await db.users.find_one(
            {"username": {"$regex": f"^{re.escape(username)}$", "$options": "i"}},
            {"username": 1, "banned": 1, "ban_reason": 1, "ban_expires": 1}
        )
        if not user:
            return mcp_success({"username": username, "found": False})

        ban_info = {
            "username": user.get("username", username),
            "found": True,
            "banned": bool(user.get("banned")),
        }
        if user.get("banned"):
            ban_info["reason"] = user.get("ban_reason", "No reason provided")
            ban_info["expires"] = str(user.get("ban_expires", "Permanent"))

        return mcp_success(ban_info)
    except Exception as e:
        return mcp_error(f"Ban check failed: {e}")


async def tool_get_community_stats(args: dict, db: AsyncIOMotorDatabase) -> dict:
    """Global community statistics."""
    try:
        total_users = await db.users.count_documents({})
        total_topics = await db.topics.count_documents({})
        total_posts = await db.posts.count_documents({})
        online_users = await db.user_presence.count_documents({
            "last_seen": {"$gte": datetime.now(timezone.utc).timestamp() - 300}
        })

        return mcp_success({
            "community": "INDUNGI Gaming",
            "game": "Counter-Strike 1.6",
            "total_members": total_users,
            "total_topics": total_topics,
            "total_posts": total_posts,
            "members_online_now": online_users,
        })
    except Exception as e:
        return mcp_error(f"Stats fetch failed: {e}")


# ─────────────────────────────────────────────────────────────────────────────
# MCP Helpers
# ─────────────────────────────────────────────────────────────────────────────

def mcp_success(data) -> dict:
    return {
        "content": [{"type": "text", "text": json.dumps(data, default=str, ensure_ascii=False)}],
        "structuredContent": data,
        "isError": False
    }


def mcp_error(message: str) -> dict:
    return {
        "content": [{"type": "text", "text": message}],
        "isError": True
    }


TOOL_HANDLERS = {
    "get_server_status": tool_get_server_status,
    "search_players": tool_search_players,
    "get_player_profile": tool_get_player_profile,
    "get_top_players": tool_get_top_players,
    "search_forum": tool_search_forum,
    "check_ban_status": tool_check_ban_status,
    "get_community_stats": tool_get_community_stats,
}


# ─────────────────────────────────────────────────────────────────────────────
# MCP Endpoint
# ─────────────────────────────────────────────────────────────────────────────

CORS_HEADERS = {
    "Access-Control-Allow-Origin": "*",
    "Access-Control-Allow-Methods": "GET, POST, OPTIONS",
    "Access-Control-Allow-Headers": "Content-Type, Accept",
    "Content-Type": "application/json",
}

@router.options("/mcp")
async def mcp_options():
    """Handle CORS preflight."""
    return JSONResponse({}, headers=CORS_HEADERS)

@router.get("/mcp")
async def mcp_info():
    """Return server info for discovery."""
    return JSONResponse({
        "name": "INDUNGI MCP Server",
        "version": "1.0.0",
        "description": "Model Context Protocol server for the INDUNGI CS 1.6 gaming community.",
        "tools": len(INDUNGI_MCP_TOOLS)
    }, headers=CORS_HEADERS)


@router.post("/mcp")
async def mcp_endpoint(request: Request):
    """
    MCP (Model Context Protocol) JSON-RPC 2.0 endpoint.
    Allows AI assistants to query INDUNGI community data.
    """
    try:
        body = await request.json()
    except Exception:
        return JSONResponse(
            {"jsonrpc": "2.0", "id": None, "error": {"code": -32700, "message": "Parse error"}},
            status_code=400, headers=CORS_HEADERS
        )

    rpc_id = body.get("id", 1)
    method = body.get("method", "")
    params = body.get("params", {})
    db = request.app.state.db

    # ── tools/list ──────────────────────────────────────────────────────────
    if method == "tools/list":
        return JSONResponse({
            "jsonrpc": "2.0",
            "id": rpc_id,
            "result": {"tools": INDUNGI_MCP_TOOLS}
        }, headers=CORS_HEADERS)

    # ── tools/call ──────────────────────────────────────────────────────────
    elif method == "tools/call":
        tool_name = params.get("name", "")
        tool_args = params.get("arguments", {})

        handler = TOOL_HANDLERS.get(tool_name)
        if not handler:
            return JSONResponse({
                "jsonrpc": "2.0",
                "id": rpc_id,
                "error": {
                    "code": -32601,
                    "message": f"Unknown tool: '{tool_name}'. Available: {list(TOOL_HANDLERS.keys())}"
                }
            }, headers=CORS_HEADERS)

        try:
            result = await handler(tool_args, db)
            return JSONResponse({
                "jsonrpc": "2.0",
                "id": rpc_id,
                "result": result
            }, headers=CORS_HEADERS)
        except Exception as e:
            return JSONResponse({
                "jsonrpc": "2.0",
                "id": rpc_id,
                "result": mcp_error(str(e))
            }, headers=CORS_HEADERS)

    # ── initialize / ping ───────────────────────────────────────────────────
    elif method in ("initialize", "ping"):
        return JSONResponse({
            "jsonrpc": "2.0",
            "id": rpc_id,
            "result": {
                "protocolVersion": "2024-11-05",
                "serverInfo": {"name": "INDUNGI MCP", "version": "1.0.0"},
                "capabilities": {"tools": {}}
            }
        }, headers=CORS_HEADERS)

    # ── unknown method ───────────────────────────────────────────────────────
    else:
        return JSONResponse({
            "jsonrpc": "2.0",
            "id": rpc_id,
            "error": {"code": -32601, "message": f"Method not found: {method}"}
        }, headers=CORS_HEADERS)


def build_mcp_router() -> APIRouter:
    return router
