#!/usr/bin/env python3

from collections import namedtuple
import logging
import sys

import pkg_resources

TopLevelDefaults = namedtuple(
    'TopLevelDefaults',
    [
        'verbose',
        'retries',
        'retry_backoffs',
        'proto',
        'parentfile',
        'pullfile',
        'changelog_date_override_file',
    ],
)
top_level_defaults = TopLevelDefaults(
    verbose=False,
    retries=5,
    retry_backoffs = [2 ** i for i in range(5)],
    proto='https',
    parentfile=pkg_resources.resource_filename(
        'gitubuntu',
        'parent_overrides.txt',
    ),
    pullfile=pkg_resources.resource_filename(
        'gitubuntu',
        'pull_overrides.txt',
    ),
    changelog_date_override_file=pkg_resources.resource_filename(
        'gitubuntu',
        'changelog_date_overrides.txt',
    ),
)

epilog_text = """
For more information on the commands see:

  $ git ubuntu <command> --help
  $ man git-ubuntu-<command>

More information about git ubuntu itself is available at:

  $ man git-ubuntu

See also:

  https://wiki.ubuntu.com/UbuntuDevelopment/Merging/GitWorkflow
"""

def main():
    try:
        import argparse
        from importlib import import_module
        from os import isatty
        import shutil
        from subprocess import CalledProcessError
        import textwrap
        from gitubuntu.run import run

        import argcomplete

        logging.getLogger('keyring').setLevel(logging.WARNING)

        known_subcommands = {
            'import': 'gitubuntu.importer',
            'importer-service-broker': 'gitubuntu.importer_service_broker',
            'importer-service-poller': 'gitubuntu.importer_service_poller',
            'importer-service-worker': 'gitubuntu.importer_service_worker',
            'merge': 'gitubuntu.merge',
            'clone': 'gitubuntu.clone',
            'tag': 'gitubuntu.tag',
            'queue': 'gitubuntu.queue',
            'remote': 'gitubuntu.remote',
            'submit': 'gitubuntu.submit',
            'export-orig': 'gitubuntu.exportorig',
            'prepare-upload': 'gitubuntu.prepare_upload',
        }

        known_network_subcommands = {
            'import',
            'clone',
            'export-orig',
            'queue',
            'remote',
            'submit',
        }

        parser = argparse.ArgumentParser(
            description='A git-based Ubuntu package maintenance system',
            formatter_class=argparse.RawTextHelpFormatter,
            epilog='',
        )
        subparsers = parser.add_subparsers(
            dest='subcommand',
            help='See below',
            metavar='<command>',
        )
        # This help ends up not being very useful to the end user
        # subparsers.required = True

        # common flags to all subcommands
        base_subparser = argparse.ArgumentParser(add_help=False)
        base_subparser.add_argument(
            '-v', '--verbose',
            action='store_true',
            help="Increase verbosity",
            default=top_level_defaults.verbose,
        )
        network_base_subparser = argparse.ArgumentParser(add_help=False)
        network_base_subparser.add_argument(
            '--retries',
            type=int,
            help="Number of times to attempt to retry downloading from "
                 "Launchpad.",
            default=top_level_defaults.retries,
        )
        network_base_subparser.add_argument(
            '--retry-backoffs',
            type=lambda s : [int(item) for item in s.split(',')],
            help="Comma-separated list of backoffs in seconds to use "
                "between each retry attempt. Default is exponential "
                "backoff.",
            default=argparse.SUPPRESS,
        )
        known_protos = ['git', 'http', 'https', 'git+ssh']
        network_base_subparser.add_argument(
            '--proto',
            metavar='[%s]' % '|'.join(known_protos),
            choices=known_protos,
            help="Specify protocol to use for fetch. Default: %(default)s",
            default=top_level_defaults.proto,
        )

        # Deprecated commands
        subparsers.add_parser("build")
        subparsers.add_parser("build-source")
        subparsers.add_parser("import-local")
        subparsers.add_parser("import-ppa")
        subparsers.add_parser("lint")
        subparsers.add_parser("review")

        width, _ = shutil.get_terminal_size()
        subhelp_width = width - 4
        subcommand_text = 'Commands:\n'
        for sub in sorted(known_subcommands):
            m = import_module(known_subcommands[sub])
            if sub in known_network_subcommands:
                help_text = m.parse_args(
                    subparsers,
                    [base_subparser, network_base_subparser],
                )
            else:
                help_text = m.parse_args(subparsers, [base_subparser])
            if sub in known_subcommands:
                subcommand_text += '\n'
                subcommand_text += '\n'.join(
                    textwrap.wrap(
                        help_text,
                        subhelp_width,
                        break_on_hyphens=False,
                        initial_indent='  ',
                        subsequent_indent='    ',
                    )
                )
        parser.add_argument(
            '-P', '--parentfile',
            type=str,
            help=argparse.SUPPRESS,
            default=top_level_defaults.parentfile,
        )
        parser.add_argument(
            '-L', '--pullfile',
            type=str,
            help=argparse.SUPPRESS,
            default=top_level_defaults.pullfile,
        )
        parser.add_argument(
            '--changelog-date-override-file',
            type=str,
            help=argparse.SUPPRESS,
            default=top_level_defaults.changelog_date_override_file,
        )
        parser.epilog = subcommand_text + "\n" + epilog_text

        argcomplete.autocomplete(parser)
        args = parser.parse_args()
        if 'subcommand' not in args or args.subcommand is None:
            parser.print_help()
            sys.exit(1)
        elif args.subcommand == 'build':
            sys.stderr.write('"build" is no longer available\n')
            sys.exit(1)
        elif args.subcommand == 'build-source':
            sys.stderr.write('"build-source" is no longer available\n')
            sys.exit(1)
        elif args.subcommand == 'import-local':
            sys.stderr.write('"import-local" is not currently available, please use "bin/git-dsc-commit" from the source tree instead\n')
            sys.exit(1)
        elif args.subcommand == 'import-ppa':
            sys.stderr.write('"import-ppa" is not currently available, please use "bin/git-dsc-commit" from the source tree instead\n')
            sys.exit(1)
        elif args.subcommand == 'lint':
            sys.stderr.write('"lint" is not currently available\n')
            sys.exit(1)
        elif args.subcommand == 'review':
            sys.stderr.write('"review" is not currently available\n')
            sys.exit(1)

        # fragile, assumes developers will pass the base_subparser above
        # should make this structural in the classes
        if args.verbose:
            level=logging.DEBUG
        else:
            level=logging.INFO
        logging.basicConfig(
            level=level,
            format='%(asctime)s - %(levelname)s:%(message)s',
            datefmt='%m/%d/%Y %H:%M:%S',
        )
        if args.subcommand in known_network_subcommands:
            try:
                retry_backoffs = args.retry_backoffs
                if len(retry_backoffs) != args.retries:
                    logging.error(
                        "Number of backoffs specified in "
                        "--retry-backoffs must match --retries."
                    )
                    sys.exit(1)
            except AttributeError:
                args.retry_backoffs = [2 ** i for i in range(args.retries)]

        try:
            run(
                ['git', 'config', 'gitubuntu.lpuser'],
                verbose_on_failure=False,
            )
        except CalledProcessError:
            if isatty(sys.stdin.fileno()):
                user = input(
                    "gitubuntu.lpuser is not set. What is your "
                    "Launchpad username? We will set this in your "
                    "personally global git configuration for you. "
                    "To abort, press Enter.\nUsername: "
                )
                if len(user) == 0:
                    logging.error("git-ubuntu requires a launchpad user")
                    sys.exit(1)
                else:
                    logging.warning(
                        "Setting the Launchpad user for git-ubuntu to %s "
                        "with `git config --global gitubuntu.lpuser %s`.",
                        user,
                        user,
                    )
                    run(['git', 'config', '--global', 'gitubuntu.lpuser', user])
            else:
                logging.error(
                    "Not connected to a TTY and no git-ubuntu lpuser "
                    "defined. Please run `git config --global "
                    "gitubuntu.lpuser <Launchpad username>` and re-run "
                    "this command."
                )
                sys.exit(1)

        sys.exit(args.func(args))
    except KeyboardInterrupt:
        sys.stderr.write('User abort\n')
        sys.exit(130)

if __name__ == '__main__':
    main()
