"""Remote tar/zip compress & decompress via SSH with permission/sudo fallbacks."""

from __future__ import annotations

import posixpath
import secrets
import shlex

from server.domain.models import Server
from server.infrastructure.ssh.remote_shell import exec_ssh_command_with_fallback


def _common_parent_dir(paths: list[str]) -> str:
    normalized = [posixpath.normpath(p) for p in paths]
    if len(normalized) == 1:
        p = normalized[0]
        return posixpath.dirname(p) if not p.endswith("/") else p.rstrip("/")
    parts_list = [p.strip("/").split("/") for p in normalized if p.startswith("/")]
    if not parts_list:
        return "/"
    common: list[str] = []
    for segment_lists in zip(*parts_list):
        if len(set(segment_lists)) == 1:
            common.append(segment_lists[0])
        else:
            break
    if not common:
        return "/"
    return "/" + "/".join(common)


def _tar_member_names(paths: list[str], work_dir: str) -> list[str]:
    members: list[str] = []
    work = work_dir.rstrip("/") or "/"
    for raw in paths:
        p = posixpath.normpath(raw)
        if p == work or p.rstrip("/") == work:
            members.append(".")
            continue
        prefix = work + "/"
        if p.startswith(prefix):
            members.append(p[len(prefix) :])
        else:
            members.append(p.lstrip("/"))
    # unique preserve order
    seen: set[str] = set()
    out: list[str] = []
    for m in members:
        if m not in seen:
            seen.add(m)
            out.append(m)
    return out


def tar_compress_commands(paths: list[str], dest: str) -> list[str]:
    """Build tar commands: direct write, then /tmp staging + mv."""
    dest_norm = posixpath.normpath(dest)
    work_dir = _common_parent_dir(paths)
    members = _tar_member_names(paths, work_dir)
    if not members:
        raise ValueError("没有可压缩的文件")

    work_q = shlex.quote(work_dir)
    members_q = " ".join(shlex.quote(m) for m in members)
    dest_q = shlex.quote(dest_norm)
    dest_parent = posixpath.dirname(dest_norm) or "/"
    dest_base = posixpath.basename(dest_norm)

    commands: list[str] = []
    if dest_parent == work_dir:
        commands.append(f"tar -czf {shlex.quote(dest_base)} -C {work_q} {members_q}")
    else:
        commands.append(f"tar -czf {dest_q} -C {work_q} {members_q}")

    tmp = f"/tmp/nexus-compress-{secrets.token_hex(8)}.tar.gz"
    commands.append(
        f"tar -czf {shlex.quote(tmp)} -C {work_q} {members_q} && mv -f {shlex.quote(tmp)} {dest_q}"
    )
    return commands


def zip_compress_commands(paths: list[str], dest: str) -> list[str]:
    dest_norm = posixpath.normpath(dest)
    work_dir = _common_parent_dir(paths)
    members = _tar_member_names(paths, work_dir)
    work_q = shlex.quote(work_dir)
    members_q = " ".join(shlex.quote(m) for m in members)
    dest_q = shlex.quote(dest_norm)
    dest_parent = posixpath.dirname(dest_norm) or "/"
    dest_base = posixpath.basename(dest_norm)
    dest_arg = shlex.quote(dest_base) if dest_parent == work_dir else dest_q

    inner = f"cd {work_q} && zip -qr {dest_arg} {members_q}"
    tmp = f"/tmp/nexus-compress-{secrets.token_hex(8)}.zip"
    staged = (
        f"cd {work_q} && zip -qr {shlex.quote(tmp)} {members_q} "
        f"&& mv -f {shlex.quote(tmp)} {dest_q}"
    )
    return [inner, staged]


async def run_remote_compress(
    server: Server,
    paths: list[str],
    dest: str,
    fmt: str,
    *,
    timeout: int = 120,
) -> dict:
    """Try compress commands until one succeeds (with sudo fallback per command)."""
    if fmt == "tar.gz":
        commands = tar_compress_commands(paths, dest)
    elif fmt == "zip":
        commands = zip_compress_commands(paths, dest)
    else:
        return {
            "exit_code": -1,
            "stderr": f"不支持的格式: {fmt}",
            "stdout": "",
            "status": "error",
        }

    last = {"exit_code": -1, "stderr": "", "stdout": "", "status": "failed"}
    for cmd in commands:
        last = await exec_ssh_command_with_fallback(server, cmd, timeout=timeout)
        if last.get("exit_code") == 0:
            return last
    return last
