"""Chisel 后端 - 基于 HTTP/WebSocket 的加密隧道"""
from __future__ import annotations

import gzip
import json
import os
import platform
import subprocess
import sys
import urllib.request
from pathlib import Path

from rich import print


def _get_chisel_path():
    """获取 chisel 二进制路径，优先 PATH，其次 ~/.maque/bin/"""
    import shutil

    exe_name = "chisel.exe" if sys.platform == "win32" else "chisel"
    found = shutil.which(exe_name)
    if found:
        return found

    local = Path.home() / ".maque" / "bin" / exe_name
    if local.exists():
        return str(local)
    return None


def _download_chisel(use_mirror: bool = None):
    """从 GitHub 下载 chisel 二进制，返回路径或 None

    Args:
        use_mirror: True=强制镜像, False=强制直连, None=先直连再 fallback 镜像
    """
    system = platform.system().lower()
    machine = platform.machine().lower()

    os_map = {"linux": "linux", "darwin": "darwin", "windows": "windows"}
    arch_map = {
        "x86_64": "amd64",
        "amd64": "amd64",
        "aarch64": "arm64",
        "arm64": "arm64",
    }

    os_name = os_map.get(system)
    arch = arch_map.get(machine)

    if not os_name or not arch:
        print(f"[red]不支持的平台: {system}/{machine}[/red]")
        print("请手动下载: https://github.com/jpillora/chisel/releases")
        return None

    print("[dim]正在获取最新版本...[/dim]")
    api_url = "https://api.github.com/repos/jpillora/chisel/releases/latest"
    try:
        req = urllib.request.Request(api_url, headers={"User-Agent": "maque"})
        with urllib.request.urlopen(req, timeout=15) as resp:
            release_info = json.loads(resp.read())
        version = release_info["tag_name"].lstrip("v")
    except Exception as e:
        print(f"[red]获取版本信息失败: {e}[/red]")
        return None

    ext = "zip" if os_name == "windows" else "gz"
    filename = f"chisel_{version}_{os_name}_{arch}.{ext}"
    raw_url = f"https://github.com/jpillora/chisel/releases/download/v{version}/{filename}"

    mirror_url = None
    try:
        from maque.git import convert_to_mirror_url

        mirror_url = convert_to_mirror_url(raw_url)
        if mirror_url == raw_url:
            mirror_url = None
    except ImportError:
        pass

    if use_mirror is True and mirror_url:
        urls = [mirror_url]
    elif use_mirror is False:
        urls = [raw_url]
    else:
        urls = [raw_url]
        if mirror_url:
            urls.append(mirror_url)

    bin_dir = Path.home() / ".maque" / "bin"
    bin_dir.mkdir(parents=True, exist_ok=True)

    exe_name = "chisel.exe" if system == "windows" else "chisel"
    chisel_path = bin_dir / exe_name

    print(f"[blue]下载 chisel v{version} ({os_name}/{arch})...[/blue]")

    for i, url in enumerate(urls):
        if i > 0:
            print("[yellow]直连失败，尝试镜像...[/yellow]")
        print(f"[dim]{url}[/dim]")

        try:
            req = urllib.request.Request(url, headers={"User-Agent": "maque"})
            with urllib.request.urlopen(req, timeout=60) as resp:
                data = resp.read()

            if os_name == "windows":
                import io
                import zipfile

                with zipfile.ZipFile(io.BytesIO(data)) as zf:
                    exe_names = [n for n in zf.namelist() if n.endswith("chisel.exe")]
                    if not exe_names:
                        print("[red]zip 中未找到 chisel.exe[/red]")
                        continue
                    with open(chisel_path, "wb") as f:
                        f.write(zf.read(exe_names[0]))
            else:
                decompressed = gzip.decompress(data)
                with open(chisel_path, "wb") as f:
                    f.write(decompressed)
                os.chmod(chisel_path, 0o755)

            print(f"[green]chisel 已安装到 {chisel_path}[/green]")
            return str(chisel_path)

        except Exception as e:
            print(f"[red]下载失败: {e}[/red]")
            if i < len(urls) - 1:
                continue

    print("请手动下载: https://github.com/jpillora/chisel/releases")
    return None


def _ensure_chisel():
    """确保 chisel 可用，不可用时自动下载"""
    path = _get_chisel_path()
    if path:
        return path
    print("[yellow]chisel 未找到，正在自动下载...[/yellow]")
    return _download_chisel()


def chisel_server(port: int = 25565, host: str = "0.0.0.0", auth: str = None):
    """启动 chisel server"""
    chisel = _ensure_chisel()
    if not chisel:
        return

    cmd = [chisel, "server", "--host", host, "--port", str(port), "--reverse"]
    if auth:
        cmd.extend(["--auth", auth])

    print(f"[green]启动 chisel server，监听 {host}:{port}[/green]")
    print(f"[dim]客户端连接: maque relay <本机IP>:{port} <远程端口>:<本地端口>[/dim]")
    print(f"[dim]按 Ctrl+C 停止[/dim]\n")

    try:
        subprocess.run(cmd)
    except KeyboardInterrupt:
        print("\n[yellow]服务已停止[/yellow]")
    except FileNotFoundError:
        print(f"[red]找不到 chisel: {chisel}[/red]")
    except OSError as e:
        if sys.platform == "win32":
            print(f"[red]Windows 安全策略阻止了 chisel 运行: {e}[/red]")
            print("[yellow]请在 PowerShell (管理员) 中执行:[/yellow]")
            print(f"  Unblock-File {chisel}")
        else:
            print(f"[red]执行 chisel 失败: {e}[/red]")


def chisel_client(server: str, mappings: list[tuple[int, str, int]], auth: str = None):
    """启动 chisel client

    Args:
        server: 服务器地址 host:port
        mappings: [(remote_port, target_host, target_port), ...]
        auth: 认证 user:password
    """
    chisel = _ensure_chisel()
    if not chisel:
        return

    cmd = [chisel, "client"]
    if auth:
        cmd.extend(["--auth", auth])
    cmd.append(server)

    server_host = server.split(":")[0]
    for remote_port, target_host, target_port in mappings:
        cmd.append(f"R:{remote_port}:{target_host}:{target_port}")
        print(
            f"  [blue]{server_host}:{remote_port}[/blue] -> [cyan]{target_host}:{target_port}[/cyan]"
        )

    print(f"\n[green]连接到 {server}[/green]")
    print("[dim]按 Ctrl+C 断开[/dim]\n")

    try:
        subprocess.run(cmd)
    except KeyboardInterrupt:
        print("\n[yellow]中转已断开[/yellow]")
    except FileNotFoundError:
        print(f"[red]找不到 chisel: {chisel}[/red]")
    except OSError as e:
        if sys.platform == "win32":
            print(f"[red]Windows 安全策略阻止了 chisel 运行: {e}[/red]")
            print("[yellow]请在 PowerShell (管理员) 中执行:[/yellow]")
            print(f"  Unblock-File {chisel}")
        else:
            print(f"[red]执行 chisel 失败: {e}[/red]")


def chisel_install(use_mirror: bool = None):
    """下载/更新 chisel 二进制"""
    existing = _get_chisel_path()
    if existing:
        print(f"[yellow]chisel 已存在: {existing}[/yellow]")
        print("[dim]重新下载以更新...[/dim]")

    result = _download_chisel(use_mirror=use_mirror)
    if result:
        print(f"\n[cyan]如需拷贝到内网机器:[/cyan]")
        print(f"  scp {result} user@内网机器:~/.maque/bin/")
