#!/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
                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()
