#!/usr/bin/python3

"""
ssh through n hosts, using a syntax similar to uucp.

EG deep-ssh host1!host2!host3 command
Alternative, ssh-like interface: deep-ssh-like -ofoo remote.host.com -s sftp
"""

# pylint: disable=wrong-import-position

# symlink me to deep-ssh in a bin directory to get the command version of
# this, so you can use me from shell programs, not just python scripts

import os
import subprocess
import sys
import typing

sys.path.insert(0, "/usr/local/lib")
import bashquote as bashquote_mod  # nopep8

# OpenSSH_3.9p1 has a -Y and broken -X
# Sun_SSH_1.0.1 has normal -X


def usage(retval: int) -> None:
    """Output a usage message."""
    if retval:
        write = sys.stderr.write
    else:
        write = sys.stdout.write

    write("Usage: %s host2!host2!...!hostn command\n" % sys.argv[0])
    write("\t--do-not-echo-chain\n")
    write("\t--echo-chain\n")
    write("\t--do-not-echo-command\n")
    write("\t--echo-command\n")
    write("\t--ssh-options\n")
    write("\t--help\n")
    write("\n")
    write("If sys.argv[0] is deep-ssh-like, act like rsh/ssh in command line option parsing.\n")

    sys.exit(retval)


def handle(
    *,
    optional_opts: str,
    chain: str,
    command: str,
    always_opts: str = "-Y -A",
    popen: int = 0,
    echo_chain: bool = True,
    echo_command: bool = False,
    subsystem=None,
) -> typing.Union[int, os._wrap_close, subprocess.Popen]:
    # pylint: disable=too-many-arguments,too-many-branches,too-many-locals
    """
    Do the actual work :).

    chain is the bang path.

    command is the command to run on bang path.

    If echo_chain is True, output the bang path to stderr.

    if echo_command is True, output the command to run.

    If popen is 0 (or False), return an exit code from os.system().
    If popen is 1 (or True), return os.popen().
    If popen is 2, return subprocess.Popen().
    """
    if command and subsystem:
        raise ValueError("Both command ({}) and subsystem ({}) in handle".format(command, subsystem))

    chain_list = chain.split("!")

    if echo_chain:
        sys.stderr.write("%s\n" % ("!".join(chain_list)))

    number = len(chain_list)

    chain_list.reverse()

    for linkno in range(number):
        command_bashquote = bashquote_mod.BashquoteString()
        name_and_host_bashquote = bashquote_mod.BashquoteString()
        if command:
            command_bashquote.add(command)
            quoted_command = command_bashquote.result()
        else:
            if subsystem:
                if not linkno:
                    # This isn't really a quoted command! It's an option that tells sshd to start a subsystem.
                    # It's only to be specified for the final hop.
                    quoted_command = "-s {}".format(subsystem)
                else:
                    # Just quote the command like normal - this isn't the final hop.
                    command_bashquote.add(command)
                    quoted_command = command_bashquote.result()
            else:
                raise ValueError("Neither command nor subsystem in handle")
        if "%" in chain_list[linkno]:
            fields = chain_list[linkno].split("%")
            name_and_host_bashquote.add(fields[0])
            regular_part = name_and_host_bashquote.result()
            port = fields[1]
            command = "ssh -p %s %s %s %s %s" % (port, always_opts, optional_opts, regular_part, quoted_command)
        else:
            name_and_host_bashquote.add(chain_list[linkno])
            command = "ssh %s %s %s %s" % (always_opts, optional_opts, name_and_host_bashquote.result(), quoted_command)

    if echo_command:
        sys.stderr.write("Executing %s\n" % command)

    if popen == 0:
        retval = os.system(command)
        if retval is None:
            retval = 0
        else:
            retval = int(retval / 256)
        return retval
    elif popen == 1:
        return os.popen(command, "r")
    elif popen == 2:
        return subprocess.Popen(command, shell=True, stdout=subprocess.PIPE, encoding="utf-8")
    else:
        raise ValueError("popen option must be 0, 1 or 2.")


def main() -> None:
    # pylint: disable=too-many-branches,too-many-statements
    """Run command (or subsystem) on host."""
    echo_chain = False
    echo_command = False
    optional_opts = ""
    command = ""
    subsystem = None

    if sys.argv[0].endswith("/deep-ssh-like") or sys.argv[0] == "deep-ssh-like":
        # Act like rsh (or ssh), for the benefit of rsync or sshfs.
        optional_opts_list = []
        non_opt_count = 0
        end_of_options = False
        while sys.argv[1:]:
            # sys.stderr.write('sys.argv[1:] is {}\n'.format(sys.argv[1:]))
            if sys.argv[1] == "--":
                end_of_options = True
                del sys.argv[1]
                continue
            if end_of_options or not sys.argv[1].startswith("-"):
                if non_opt_count == 0:
                    chain = sys.argv[1]
                    del sys.argv[1]
                    non_opt_count += 1
                    continue
                elif non_opt_count == 1:
                    # This is a candidate for some bashquote quoting...  But not today.
                    command = " ".join(sys.argv[1:])
                    del sys.argv[1:]
                    non_opt_count += 1
                    continue
            if sys.argv[1] == "-s":
                subsystem = sys.argv[2]
                # We consume both arguments for this one.
                del sys.argv[1]
                del sys.argv[1]
                continue
            optional_opts_list.append(sys.argv[1])
            del sys.argv[1]
        if subsystem and command:
            sys.stderr.write("{}: Error: Both command ({}) and subsystem ({}) specified\n".format(sys.argv[0], command, subsystem))
            sys.exit(1)
        optional_opts = " ".join(optional_opts_list)
        # chain = sys.argv[1]
        # del sys.argv[1]
        # if command:
        #     command = ' '.join(sys.argv[1:])
    else:
        if len(sys.argv) < 3:
            usage(1)

        chain = sys.argv[1]
        command = sys.argv[2]

        # with open('/tmp/t', 'w') as file_:
        #     file_.write(str(sys.argv))

        while sys.argv[3:]:
            if sys.argv[3] == "--do-not-echo-chain":
                echo_chain = False
            elif sys.argv[3] == "--echo-chain":
                echo_chain = True
            elif sys.argv[3] == "--do-not-echo-command":
                echo_command = False
            elif sys.argv[3] == "--echo-command":
                echo_command = True
            elif sys.argv[3] == "--ssh-options":
                optional_opts = sys.argv[4]
                del sys.argv[3]
            elif sys.argv[3] in ["--help", "-h"]:
                usage(0)
            else:
                sys.stderr.write("%s: Illegal option: %s\n" % (sys.argv[0], sys.argv[3]))
                usage(1)
            del sys.argv[3]

    retval = handle(
        optional_opts=optional_opts,
        chain=chain,
        command=command,
        echo_chain=echo_chain,
        echo_command=echo_command,
        subsystem=subsystem,
    )
    assert isinstance(retval, int)
    sys.exit(retval)


if __name__ == "__main__":
    main()
