#!/usr/bin/env python3
"""rodnoy-ssh — универсальный SSH-помощник для rodnoy.site (deploy@93.183.81.233).

Назначение: запускать команды на сервере из любой новой сессии AI без
повторной настройки окружения. openssh-client в окружении может отсутствовать,
поэтому используется paramiko.

ЗАВИСИМОСТИ:
    pip install paramiko   (или через venv: python3 -m pip install paramiko)

ИСПОЛЬЗОВАНИЕ:
    # Выполнить одну команду
    python3 rodnoy-ssh.py 'whoami; hostname; date'

    # Выполнить локальный скрипт на сервере (загрузится через SFTP, запустится bash)
    python3 rodnoy-ssh.py --file /path/to/script.sh

    # Скачать файл с сервера
    python3 rodnoy-ssh.py --get /remote/path.txt /local/path.txt

    # Залить файл на сервер (под sudo — в /var/www/...)
    python3 rodnoy-ssh.py --put /local/file.txt /remote/file.txt [--sudo]

    /etc/hosts НЕ трогается — ключ ищется по путям:
      1. $RODNOY_SSH_KEY  (env)
      2. /home/z/my-project/scripts/deploy_key  (текущее место)
      3. ./deploy_key       (рядом со скриптом)
      4. ~/.ssh/rodnoy_deploy_key

ПАРАМЕТРЫ ОКРУЖЕНИЯ:
    RODNOY_SSH_KEY   путь к приватному ключу
    RODNOY_SSH_HOST  93.183.81.233
    RODNOY_SSH_USER  deploy
    RODNOY_SSH_PORT  22

КОДЫ ВЫХОДА:
    Совпадают с exit-code команды на сервере (0 = успех).
"""
import os
import sys
import stat
import shlex
import paramiko


def find_key():
    candidates = [
        os.environ.get("RODNOY_SSH_KEY"),
        "/home/z/my-project/scripts/deploy_key",
        os.path.join(os.path.dirname(os.path.abspath(__file__)), "deploy_key"),
        os.path.expanduser("~/.ssh/rodnoy_deploy_key"),
    ]
    for p in candidates:
        if p and os.path.exists(p):
            # Исправляем права, если они слишком открытые
            try:
                if os.stat(p).st_mode & 0o077:
                    os.chmod(p, 0o600)
            except OSError:
                pass
            return p
    raise SystemExit(
        "[rodnoy-ssh] Приватный ключ не найден. Положите deploy_key в один из:\n  "
        + "\n  ".join(c for c in candidates if c)
    )


def connect():
    client = paramiko.SSHClient()
    client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
    client.connect(
        hostname=os.environ.get("RODNOY_SSH_HOST", "93.183.81.233"),
        port=int(os.environ.get("RODNOY_SSH_PORT", "22")),
        username=os.environ.get("RODNOY_SSH_USER", "deploy"),
        key_filename=find_key(),
        look_for_keys=False,
        allow_agent=False,
        timeout=20,
        banner_timeout=20,
        auth_timeout=20,
    )
    return client


def run(client, cmd, timeout=300):
    stdin, stdout, stderr = client.exec_command(cmd, timeout=timeout, get_pty=False)
    out = stdout.read().decode("utf-8", errors="replace")
    err = stderr.read().decode("utf-8", errors="replace")
    code = stdout.channel.recv_exit_status()
    return out, err, code


def put_file(client, local_path, remote_path, use_sudo=False, owner=None):
    """Загрузить файл на сервер. Если use_sudo — загрузка в /tmp,
    затем sudo mv + chown (для записи в /var/www, /etc/nginx, ...)."""
    if not use_sudo:
        sftp = client.open_sftp()
        sftp.put(local_path, remote_path)
        sftp.close()
        return
    # sudo path: sftp в /tmp, потом mv через sudo
    tmp_remote = "/tmp/_rodnoy_ssh_put_" + os.path.basename(remote_path)
    sftp = client.open_sftp()
    sftp.put(local_path, tmp_remote)
    sftp.close()
    mv_cmd = f"sudo install -m 644 {shlex.quote(tmp_remote)} {shlex.quote(remote_path)}"
    if owner:
        mv_cmd += f" && sudo chown {shlex.quote(owner)} {shlex.quote(remote_path)}"
    mv_cmd += f" && rm -f {shlex.quote(tmp_remote)}"
    out, err, code = run(client, mv_cmd)
    if code != 0:
        sys.stderr.write(err)
        raise SystemExit(f"[rodnoy-ssh] put failed (exit {code})")


def get_file(client, remote_path, local_path, use_sudo=False):
    if use_sudo:
        # cp в /tmp с правом чтения, затем sftp get
        tmp_remote = "/tmp/_rodnoy_ssh_get_" + os.path.basename(remote_path)
        cmd = f"sudo cp {shlex.quote(remote_path)} {shlex.quote(tmp_remote)} && sudo chmod 644 {shlex.quote(tmp_remote)}"
        out, err, code = run(client, cmd)
        if code != 0:
            sys.stderr.write(err)
            raise SystemExit(f"[rodnoy-ssh] sudo cp failed (exit {code})")
        sftp = client.open_sftp()
        sftp.get(tmp_remote, local_path)
        sftp.close()
        run(client, f"rm -f {shlex.quote(tmp_remote)}")
    else:
        sftp = client.open_sftp()
        sftp.get(remote_path, local_path)
        sftp.close()


def main():
    args = sys.argv[1:]
    if not args:
        print(__doc__)
        sys.exit(2)

    mode = "exec"
    if args[0] in ("--file", "-f"):
        mode = "file"
        script_path = args[1]
        cmd_args = args[2:]
        with open(script_path, "r", encoding="utf-8") as f:
            script_body = f.read()
    elif args[0] in ("--get", "-g"):
        mode = "get"
        remote = args[1]
        local = args[2]
    elif args[0] in ("--put", "-p"):
        mode = "put"
        local = args[1]
        remote = args[2]
        use_sudo = "--sudo" in args[3:]
        owner = None
        if "--owner" in args:
            i = args.index("--owner")
            owner = args[i + 1]
    elif args[0] in ("--help", "-h"):
        print(__doc__)
        sys.exit(0)

    client = connect()
    try:
        if mode == "file":
            sftp = client.open_sftp()
            remote = "/tmp/_rodnoy_ssh_exec.sh"
            with sftp.open(remote, "w") as f:
                f.write(script_body)
            sftp.chmod(remote, 0o755)
            sftp.close()
            cmd = f"bash {remote}"
            if cmd_args:
                cmd += " " + " ".join(cmd_args)
            # Передаём аргументы через env, чтобы не было проблем с quoting
            out, err, code = run(client, cmd)
            if out:
                sys.stdout.write(out)
                if not out.endswith("\n"):
                    sys.stdout.write("\n")
            if err:
                sys.stderr.write(err)
                if not err.endswith("\n"):
                    sys.stderr.write("\n")
            # Чистим
            run(client, f"rm -f {remote}")
            sys.exit(code)
        elif mode == "get":
            use_sudo = "--sudo" in args[3:]
            get_file(client, remote, local, use_sudo=use_sudo)
            print(f"[rodnoy-ssh] downloaded: {remote} -> {local}")
        elif mode == "put":
            put_file(client, local, remote, use_sudo=use_sudo, owner=owner)
            print(f"[rodnoy-ssh] uploaded: {local} -> {remote}")
        else:
            cmd = " ".join(args)
            out, err, code = run(client, cmd)
            if out:
                sys.stdout.write(out)
                if not out.endswith("\n"):
                    sys.stdout.write("\n")
            if err:
                sys.stderr.write(err)
                if not err.endswith("\n"):
                    sys.stderr.write("\n")
            sys.exit(code)
    finally:
        client.close()


if __name__ == "__main__":
    main()
