from __future__ import annotations

import asyncio
import base64
import json
import sys
from pathlib import Path
from typing import Optional

import click
from rich.console import Console
from rich.table import Table
from rich.panel import Panel

from .java_ping import mcping as java_ping, PingResult
from .bedrock_ping import mcping_bedrock, BedrockPingResult
from .colors import to_rich, strip_all, to_legacy


console = Console()


@click.command()
@click.argument("host")
@click.option(
    "-p", "--port", "port", default=None, type=int,
    help="Port (Java: default 25565, Bedrock: default 19132)",
)
@click.option(
    "-j", "--java", "edition", flag_value="java", default=True,
    help="Java Edition server (default)",
)
@click.option(
    "-b", "--bedrock", "edition", flag_value="bedrock",
    help="Bedrock Edition server",
)
@click.option(
    "--timeout", default=10.0, type=float,
    help="Connection timeout in seconds (default: 10)",
)
@click.option(
    "--players/--no-players", "show_players", default=True,
    help="Show online player list (default: show)",
)
@click.option(
    "--player-limit", default=5, type=int,
    help="Max players to show in list (default: 5)",
)
@click.option(
    "--no-srv", "resolve_srv", flag_value=False, default=True,
    help="Disable SRV record resolution (default: enabled)",
)
@click.option(
    "--json", "output_json", is_flag=True,
    help="Output pure JSON format",
)
@click.option(
    "--color/--no-color", "force_color", default=None,
    help="Force enable/disable color output",
)
def main(
    host: str,
    port: Optional[int],
    edition: str,
    timeout: float,
    show_players: bool,
    player_limit: int,
    resolve_srv: bool,
    output_json: bool,
    force_color: Optional[bool],
) -> None:
    if port is None:
        port = 19132 if edition == "bedrock" else 25565

    if output_json:
        force_color = False

    async def _run() -> None:
        if edition == "bedrock":
            result = await mcping_bedrock(host, port, timeout, resolve_srv)
            if output_json:
                _print_json_bedrock(result)
            else:
                _print_rich_bedrock(result, show_players, player_limit, force_color)
        else:
            result = await java_ping(host, port, timeout, resolve_srv=resolve_srv)
            if output_json:
                _print_json_java(result)
            else:
                _print_rich_java(result, show_players, player_limit, force_color)

    asyncio.run(_run())


def _format_latency(ms: float) -> str:
    if ms < 1:
        return f"{ms:.2f}ms"
    if ms < 100:
        return f"{ms:.0f}ms"
    return f"{ms:.0f}ms"


def _build_motd_text(motd_str: str) -> str:
    return strip_all(motd_str)


def _save_favicon(host: str, favicon: str) -> str:
    try:
        header, data = favicon.split(",", 1)
        image_data = base64.b64decode(data)
    except Exception:
        return favicon

    suffix = ".png"
    if "image/svg" in header:
        suffix = ".svg"
    elif "image/gif" in header:
        suffix = ".gif"

    cache_dir = Path.home() / ".mcping" / "icons"
    cache_dir.mkdir(parents=True, exist_ok=True)
    safe_host = host.replace(".", "_").replace(":", "_")
    icon_path = cache_dir / f"{safe_host}{suffix}"

    try:
        with open(icon_path, "wb") as f:
            f.write(image_data)
        return str(icon_path)
    except Exception:
        return favicon


def _print_json_java(result: PingResult) -> None:
    output = {
        "success": result.success,
        "error": result.error,
        "host": result.host,
        "port": result.port,
        "ip": result.ip,
        "resolved_host": result.resolved_host,
        "resolved_port": result.resolved_port,
        "srv_resolved": result.srv_resolved,
        "version": {
            "name": result.version_name.replace(" / ", " - "),
            "protocol": result.protocol,
        },
        "players": {
            "online": result.players_online,
            "max": result.players_max,
        },
        "motd": result.motd,
        "motd_raw": _build_motd_text(result.motd),
        "player_list": result.player_list,
        "latency_ms": result.latency_ms,
        "favicon": result.favicon,
    }
    console.print(json.dumps(output, ensure_ascii=False, indent=2))


def _print_json_bedrock(result: BedrockPingResult) -> None:
    output = {
        "success": result.success,
        "error": result.error,
        "host": result.host,
        "port": result.port,
        "ip": result.ip,
        "resolved_host": result.resolved_host,
        "resolved_port": result.resolved_port,
        "srv_resolved": result.srv_resolved,
        "edition": result.edition,
        "version": {
            "name": result.version_name.replace(" / ", " - "),
            "protocol": result.protocol,
        },
        "players": {
            "online": result.players_online,
            "max": result.players_max,
        },
        "motd": result.motd,
        "motd_raw": _build_motd_text(result.motd),
        "sub_motd": result.sub_motd,
        "gamemode": result.gamemode,
        "latency_ms": result.latency_ms,
    }
    console.print(json.dumps(output, ensure_ascii=False, indent=2))


def _print_rich_java(
    result: PingResult,
    show_players: bool,
    player_limit: int,
    force_color: Optional[bool],
) -> None:
    from rich import box

    no_color = force_color is False or (force_color is None and not console.color_system)

    if not result.success:
        panel = Panel(
            f"[bold red]Connection failed[/bold red]\n{result.error}",
            title="[red]MCPing[/red]",
            border_style="red",
            box=box.ROUNDED,
        )
        console.print(panel)
        sys.exit(1)

    if no_color:
        motd_display = strip_all(result.motd)
    else:
        motd_display = to_rich(result.motd)

    motd_text = str(motd_display) if no_color else motd_display
    title_parts = [f"[bold cyan]{result.resolved_host or result.host}[/bold cyan]"]
    if result.srv_resolved:
        title_parts.append(f"[dim](SRV -> {result.resolved_host}:{result.resolved_port})[/dim]")
    else:
        title_parts.append(f"[dim]:{result.port}[/dim]")
    title_str = "".join(title_parts)

    motd_panel = Panel(
        motd_text,
        title=title_str,
        border_style="cyan",
        box=box.ROUNDED,
    )
    console.print(motd_panel)

    table = Table(box=box.SIMPLE, show_header=False)
    table.add_column("key", style="bold cyan", no_wrap=True)
    table.add_column("value", no_wrap=False)

    table.add_row("Version", result.version_name.replace(" / ", " - "))
    table.add_row("Protocol", str(result.protocol))
    table.add_row("Latency", f"[yellow]{_format_latency(result.latency_ms)}[/yellow]")
    table.add_row("Players", f"{result.players_online} / {result.players_max}")
    if result.favicon:
        icon_path = _save_favicon(result.host, result.favicon)
        table.add_row("Icon", f"[green]Fetched[/green] {icon_path}")
    else:
        table.add_row("Icon", "[dim]None[/dim]")
    if result.ip:
        table.add_row("IP", result.ip)

    console.print(table)

    if show_players and result.player_list:
        player_table = Table(
            title="[bold cyan]Players[/bold cyan]",
            box=box.SIMPLE,
            show_header=True,
            header_style="bold cyan",
        )
        player_table.add_column("#", justify="right", style="dim")
        player_table.add_column("Player Name")

        display_players = result.player_list[:player_limit]
        for i, player in enumerate(display_players, 1):
            player_table.add_row(str(i), player)

        if len(result.player_list) > player_limit:
            remaining = len(result.player_list) - player_limit
            player_table.add_row(
                f"[dim]... (+{remaining} more)[/dim]", "[dim](use --player-limit to show more)[/dim]"
            )

        console.print(player_table)


def _print_rich_bedrock(
    result: BedrockPingResult,
    show_players: bool,
    player_limit: int,
    force_color: Optional[bool],
) -> None:
    from rich import box

    if not result.success:
        panel = Panel(
            f"[bold red]Connection failed[/bold red]\n{result.error}",
            title="[red]MCPing (Bedrock)[/red]",
            border_style="red",
            box=box.ROUNDED,
        )
        console.print(panel)
        sys.exit(1)

    title_parts = [f"[bold cyan]{result.resolved_host or result.host}[/bold cyan]"]
    if result.srv_resolved:
        title_parts.append(f"[dim](SRV -> {result.resolved_host}:{result.resolved_port})[/dim]")
    else:
        title_parts.append(f"[dim]:{result.port}[/dim]")
    title_str = "".join(title_parts)

    no_color = force_color is False or (force_color is None and not console.color_system)
    if no_color:
        motd_display = strip_all(result.motd)
    else:
        motd_display = to_rich(result.motd)

    motd_text = str(motd_display) if no_color else motd_display

    motd_panel = Panel(
        motd_text,
        title=title_str,
        border_style="cyan",
        box=box.ROUNDED,
    )
    console.print(motd_panel)

    table = Table(box=box.SIMPLE, show_header=False)
    table.add_column("key", style="bold cyan", no_wrap=True)
    table.add_column("value", no_wrap=False)

    table.add_row("Version", f"{result.edition} / {result.version_name}")
    table.add_row("Protocol", str(result.protocol))
    table.add_row("Latency", f"[yellow]{_format_latency(result.latency_ms)}[/yellow]")
    table.add_row("Players", f"{result.players_online} / {result.players_max}")
    if result.sub_motd:
        table.add_row("Sub MOTD", result.sub_motd)
    if result.gamemode:
        table.add_row("Gamemode", result.gamemode)
    if result.ip:
        table.add_row("IP", result.ip)

    console.print(table)


if __name__ == "__main__":
    main()
