"""
M-Team MCP Server

Provides MCP tools for interacting with the M-Team (馒头) private torrent tracker:
  - search_torrents    : Search torrents by keyword
  - get_torrent_detail : Get detailed info for a specific torrent
  - download_torrent   : Download a .torrent file by torrent ID

Authentication: MTEAM_API_KEY read from .env file (x-api-key header).
"""

import os
import re

import requests
from dotenv import load_dotenv
from fastmcp import FastMCP

load_dotenv()

API_KEY: str = os.environ.get("MTEAM_API_KEY", "")
BASE_URL: str = os.environ.get("MTEAM_API_BASE", "https://api.m-team.cc/api").rstrip("/")
DOWNLOAD_DIR: str = os.environ.get("MTEAM_DOWNLOAD_DIR", "./seed")

mcp = FastMCP(
    "mcp-server-mteam",
    instructions=(
        "Tools for searching and downloading torrents from M-Team (馒头). "
        "Requires MTEAM_API_KEY to be set in the .env file."
    ),
)


def _headers() -> dict[str, str]:
    if not API_KEY:
        raise RuntimeError(
            "MTEAM_API_KEY is not set. "
            "Please add it to your .env file (see .env_example)."
        )
    return {
        "Content-Type": "application/json",
        "x-api-key": API_KEY,
    }


def _format_size(size_bytes: int | str | None) -> str:
    """Convert byte count to human-readable string."""
    if size_bytes is None:
        return "unknown"
    try:
        n = int(size_bytes)
    except (ValueError, TypeError):
        return str(size_bytes)
    for unit in ("B", "KB", "MB", "GB", "TB"):
        if n < 1024:
            return f"{n:.2f} {unit}"
        n /= 1024
    return f"{n:.2f} PB"


@mcp.tool
def search_torrents(
    keyword: str,
    mode: str = "normal",
    page_number: int = 1,
    page_size: int = 20,
) -> str:
    """Search torrents on M-Team by keyword.

    Args:
        keyword: Search keyword (supports Chinese / English).
        mode: Search mode. One of: normal, adult, movie, music, tvshow,
              waterfall, rss, rankings, all. Defaults to "normal".
        page_number: Page number (1-based). Defaults to 1.
        page_size: Number of results per page (max 100). Defaults to 20.

    Returns:
        A formatted string listing matching torrents with their IDs, names,
        sizes, seeder/leecher counts, labels, ratings, and discount info.
    """
    url = f"{BASE_URL}/torrent/search"
    body = {
        "keyword": keyword,
        "mode": mode,
        "pageNumber": page_number,
        "pageSize": page_size,
    }
    resp = requests.post(url, json=body, headers=_headers(), timeout=30)
    resp.raise_for_status()
    data = resp.json()

    code = data.get("code")
    message = data.get("message", "")
    payload = data.get("data")

    if str(code) != "0" and message.upper() != "SUCCESS":
        return f"API error {code}: {message}"

    if isinstance(payload, dict):
        items: list = (
            payload.get("list")
            or payload.get("data")
            or payload.get("torrents")
            or []
        )
        total = payload.get("total") or payload.get("totalCount") or len(items)
    elif isinstance(payload, list):
        items = payload
        total = len(payload)
    else:
        return f"Unexpected response format:\n{data}"

    if not items:
        return f'No results found for "{keyword}" (mode={mode}).'

    lines = [
        f'Search results for "{keyword}" (mode={mode})',
        f"Total: {total}  |  Page {page_number}, showing {len(items)} items",
        "-" * 60,
    ]
    for item in items:
        tid = item.get("id", "?")
        name = item.get("name") or item.get("title") or "(no name)"
        size = _format_size(item.get("size"))
        labels = ", ".join(item.get("labelsNew") or []) or "-"
        imdb_rating = item.get("imdbRating") or "-"
        douban_rating = item.get("doubanRating") or "-"
        status = item.get("status") or {}
        seeders = status.get("seeders", "?")
        leechers = status.get("leechers", "?")
        discount = status.get("discount", "-")
        lines.append(
            f"[{tid}] {name}\n"
            f"  Size: {size}  Seeders: {seeders}  Leechers: {leechers}\n"
            f"  Labels: {labels}  Discount: {discount}\n"
            f"  IMDB: {imdb_rating}  Douban: {douban_rating}"
        )

    return "\n".join(lines)


@mcp.tool
def get_torrent_detail(torrent_id: str) -> str:
    """Get detailed information for a specific torrent by its ID.

    Args:
        torrent_id: The numeric torrent ID (e.g. "1125330").

    Returns:
        A formatted string with full torrent details including name, size,
        description, ratings, file count, seeder/leecher counts, etc.
    """
    url = f"{BASE_URL}/torrent/detail"
    # detail endpoint accepts form-encoded data (not JSON)
    detail_headers = {k: v for k, v in _headers().items() if k != "Content-Type"}
    resp = requests.post(url, data={"id": torrent_id}, headers=detail_headers, timeout=30)
    resp.raise_for_status()
    data = resp.json()

    code = data.get("code")
    message = data.get("message", "")
    payload = data.get("data")

    if payload is None:
        return f"API error {code}: {message}"

    item = payload if isinstance(payload, dict) else {}

    tid = item.get("id", torrent_id)
    name = item.get("name") or item.get("title") or "(no name)"
    small_descr = item.get("smallDescr") or "-"
    size = _format_size(item.get("size"))
    numfiles = item.get("numfiles", "?")
    labels = ", ".join(item.get("labelsNew") or []) or "-"
    imdb = item.get("imdb") or "-"
    imdb_rating = item.get("imdbRating") or "-"
    douban = item.get("douban") or "-"
    douban_rating = item.get("doubanRating") or "-"
    created = item.get("createdDate", "-")
    status = item.get("status") or {}
    seeders = status.get("seeders", "?")
    leechers = status.get("leechers", "?")
    times_completed = status.get("timesCompleted", "?")
    discount = status.get("discount", "-")
    visible = status.get("visible", True)
    banned = status.get("banned", False)

    lines = [
        f"Torrent Detail: [{tid}]",
        "=" * 60,
        f"Name       : {name}",
        f"Description: {small_descr}",
        f"Size       : {size}  ({numfiles} file(s))",
        f"Labels     : {labels}",
        f"Discount   : {discount}",
        f"Seeders    : {seeders}  Leechers: {leechers}  Completed: {times_completed}",
        f"Created    : {created}",
        f"Visible    : {visible}  Banned: {banned}",
        f"IMDB       : {imdb}  Rating: {imdb_rating}",
        f"Douban     : {douban}  Rating: {douban_rating}",
    ]
    return "\n".join(lines)


@mcp.tool
def download_torrent(torrent_id: str) -> str:
    """Download a .torrent file for the given torrent ID.

    This tool first obtains a temporary download token from the M-Team API,
    then downloads the .torrent file and saves it to the configured download
    directory (default: ./seed/).

    Args:
        torrent_id: The numeric torrent ID (e.g. "1125330").

    Returns:
        The absolute path to the saved .torrent file on success, or an error
        message on failure.
    """
    token_url = f"{BASE_URL}/torrent/genDlToken"
    token_headers = {
        "accept": "*/*",
        "content-type": "application/x-www-form-urlencoded; charset=UTF-8",
        "x-api-key": API_KEY,
    }
    if not API_KEY:
        raise RuntimeError(
            "MTEAM_API_KEY is not set. "
            "Please add it to your .env file (see .env_example)."
        )

    resp = requests.post(
        token_url, headers=token_headers, data={"id": torrent_id}, timeout=15
    )
    resp.raise_for_status()
    token_data = resp.json()

    if token_data.get("message") != "SUCCESS" or not token_data.get("data"):
        return f"Failed to get download token: {token_data}"

    download_url: str = token_data["data"]

    # Fetch torrent name for a meaningful filename; fall back to ID if unavailable
    torrent_name = torrent_id
    try:
        detail_headers = {k: v for k, v in _headers().items() if k != "Content-Type"}
        detail_resp = requests.post(
            f"{BASE_URL}/torrent/detail",
            data={"id": torrent_id},
            headers=detail_headers,
            timeout=15,
        )
        detail_resp.raise_for_status()
        detail_data = detail_resp.json()
        raw_name = (detail_data.get("data") or {}).get("name") or ""
        if raw_name:
            torrent_name = raw_name
    except Exception:
        pass

    # Replace spaces with dots, then strip illegal filesystem characters
    dotted = torrent_name.replace(" ", ".")
    safe_name = re.sub(r'[<>:"/\\|?*\x00-\x1f]', "", dotted).strip(".")
    if not safe_name:
        safe_name = torrent_id
    filename = f"[M-TEAM]{safe_name}.torrent"
    os.makedirs(DOWNLOAD_DIR, exist_ok=True)
    output_path = os.path.abspath(os.path.join(DOWNLOAD_DIR, filename))

    dl_resp = requests.get(download_url, stream=True, timeout=60)
    dl_resp.raise_for_status()
    with open(output_path, "wb") as f:
        for chunk in dl_resp.iter_content(chunk_size=128 * 1024):
            if chunk:
                f.write(chunk)

    return f"Torrent saved to: {output_path}"


def main() -> None:
    """Entry point for the mcp-server-mteam console script."""
    mcp.run()


if __name__ == "__main__":
    main()
