#!/usr/bin/env python
from __future__ import print_function

import argparse
import sys

import openidc_client
import requests.exceptions

import odcs.client.odcs
import json

env_config = {
    'fedora': {
        'prod': {
            'server_url': 'https://odcs.fedoraproject.org',
        },
        'staging': {
            'server_url': 'https://odcs.stg.fedoraproject.org',
        }
    },
    'redhat': {
        'prod': {
            'server_url': 'https://odcs.engineering.redhat.com',
        },
        'staging': {
            'server_url': 'https://odcs.stage.engineering.redhat.com',
        }
    }
}

id_provider_config = {
    'prod': 'https://id.fedoraproject.org/openidc/',
    'staging': 'https://id.stg.fedoraproject.org/openidc/',
}

parser = argparse.ArgumentParser(
    description='''\
%(prog)s - Command line client.

If you have problems authenticating with OpenID Connect, try:

  $ rm -rf ~/.openidc/

Example usage:

''',
    formatter_class=argparse.RawDescriptionHelpFormatter,
)
parser.add_argument(
    '--redhat', action='store_const',
    const='redhat', default='fedora', dest='infra',
    help='Use internal ODCS infra environment. If omitted, Fedora Infra will '
         'be used by default.')
parser.add_argument(
    '--staging', action='store_const',
    const='staging', default='prod', dest='env',
    help='Use Fedora Infra or internal staging environment, which depends on '
         'if --redhat is specified. If omitted, production environment will '
         'be used.')
parser.add_argument(
    '--server', default=None, help="Use custom ODCS server.")
parser.add_argument(
    '--no-wait', action='store_true',
    help='When used, odcs client will not wait for the action to finish.')
parser.add_argument(
    '-q', '--quiet', action='store_true',
    help='Run without detailed log messages')

subparsers = parser.add_subparsers(
    description='These commands you can use to operate composes with ODCS')


create_parser = subparsers.add_parser(
    'create', help='Low-level command to create a new compose')
create_parser.set_defaults(command='create')
create_parser.add_argument(
    'source_type', default=None,
    choices=['tag', 'module', 'raw_config', 'pulp', 'build'],
    help="Type for the source. Must be 'tag' or 'module'")
create_parser.add_argument(
    'source', default="",
    help="Source for the compose. May be a koji tag or a "
    "whitespace separated list of modules.")
create_parser.add_argument(
    '--flag', default=[], action='append',
    help="Flag to pass to influence the compose.")
create_parser.add_argument(
    '--result', default=[], action='append',
    help="Results of a compose to influence the compose.")
create_parser.add_argument(
    '--sigkey', default=[], action='append',
    help="Signing key ID using which the packages must be signed - "
    "\none\" for no signing key.")
create_parser.add_argument(
    '--koji-event', default=None,
    help="Koji event for populating package set")
create_parser.add_argument(
    '--arch', default=[], action='append',
    help="Koji arch to build the compose for.")
create_parser.add_argument(
    'packages', metavar='package', nargs='*',
    help='Packages to be included in the compose.')
create_parser.add_argument(
    'builds', metavar='build', nargs='*',
    help='Builds to be included in the compose.')
create_parser.add_argument(
    '--module-defaults-url', default="",
    metavar='module_defaults_url',
    help="URL to git repository with module defaults.")
create_parser.add_argument(
    '--module-defaults-commit', default="",
    metavar='module_defaults_commit',
    help="Git commit/branch from which to take the module defaults.")
create_parser.add_argument(
    '--modular-tag', default=[], action='append',
    metavar="modular_koji_tags",
    help="Koji tag with module builds.")
create_parser.add_argument(
    '--lookaside-repo', default=[], action='append',
    metavar="lookaside_repos",
    help="Koji tag with module builds.")
create_parser.add_argument(
    '--label', default=None,
    help="Label for raw_config compose.")
create_parser.add_argument(
    '--compose-type', default=None,
    help="Compose type for raw_config compose.")

create_tag_parser = subparsers.add_parser(
    'create-tag', help='Create new compose from Koji tag.')
create_tag_parser.set_defaults(command='create-tag')
create_tag_parser.add_argument(
    'tag', default="",
    help="Koji tag name.")
create_tag_parser.add_argument(
    '--flag', default=[], action='append',
    help="Flag to pass to influence the compose.")
create_tag_parser.add_argument(
    '--sigkey', default=[], action='append',
    help="Signing key ID using which the packages must be signed - "
    "\none\" for no signing key.")
create_tag_parser.add_argument(
    '--koji-event', default=None,
    help="Koji event for populating package set")
create_tag_parser.add_argument(
    '--arch', default=[], action='append',
    help="Koji arch to build the compose for.")
create_tag_parser.add_argument(
    'packages', metavar='package', nargs='*',
    help='Koji packages to be included in the compose.')
create_tag_parser.add_argument(
    '--build', default=[], action='append',
    help='Builds to be included in the compose.')
create_tag_parser.add_argument(
    '--module-defaults-url', default="",
    metavar='module_defaults_url',
    help="URL to git repository with module defaults.")
create_tag_parser.add_argument(
    '--module-defaults-commit', default="",
    metavar='module_defaults_commit',
    help="Git commit/branch from which to take the module defaults.")
create_tag_parser.add_argument(
    '--modular-tag', default=[], action='append',
    metavar="modular_koji_tags",
    help="Koji tag with module builds.")
create_tag_parser.add_argument(
    '--lookaside-repo', default=[], action='append',
    metavar="lookaside_repos",
    help="Koji tag with module builds.")

create_module_parser = subparsers.add_parser(
    'create-module', help='Create new compose from modules.')
create_module_parser.set_defaults(command='create-module')
create_module_parser.add_argument(
    '--flag', default=[], action='append',
    help="Flag to pass to influence the compose.")
create_module_parser.add_argument(
    '--sigkey', default=[], action='append',
    help="Signing key ID using which the packages must be signed - "
    "\none\" for no signing key.")
create_module_parser.add_argument(
    '--arch', default=[], action='append',
    help="Koji arch to build the compose for.")
create_module_parser.add_argument(
    'modules', metavar='nsvc', nargs='+',
    help='Modules to be included in the compose.')
create_module_parser.add_argument(
    '--module-defaults-url', default="",
    metavar='module_defaults_url',
    help="URL to git repository with module defaults.")
create_module_parser.add_argument(
    '--module-defaults-commit', default="",
    metavar='module_defaults_commit',
    help="Git commit/branch from which to take the module defaults.")
create_module_parser.add_argument(
    '--lookaside-repo', default=[], action='append',
    metavar="lookaside_repos",
    help="Koji tag with module builds.")

create_pulp_parser = subparsers.add_parser(
    'create-pulp', help='Create new compose from Pulp content_sets.')
create_pulp_parser.set_defaults(command='create-pulp')
create_pulp_parser.add_argument(
    '--flag', default=[], action='append',
    help="Flag to pass to influence the compose.")
create_pulp_parser.add_argument(
    'content_sets', metavar='content_set', nargs='+',
    help='Content sets to be included in the compose.')

create_raw_config_parser = subparsers.add_parser(
    'create-raw-config', help='Create new compose from Pungi raw configuration.')
create_raw_config_parser.set_defaults(command='create-raw-config')
create_raw_config_parser.add_argument(
    'raw_config_name',
    help='Name of raw_config compose as defined in ODCS Server.')
create_raw_config_parser.add_argument(
    'raw_config_commit',
    help='Commit or branch name to get raw_config from.')
create_raw_config_parser.add_argument(
    '--flag', default=[], action='append',
    help="Flag to pass to influence the compose.")
create_raw_config_parser.add_argument(
    '--label', default=None,
    help="Label for raw_config compose.")
create_raw_config_parser.add_argument(
    '--compose-type', default=None,
    help="Compose type for raw_config compose.")
create_raw_config_parser.add_argument(
    '--koji-event', default=None,
    help="Koji event for populating package set")

create_build_parser = subparsers.add_parser(
    'create-build', help='Create new compose from Koji builds.')
create_build_parser.set_defaults(command='create-build')
create_build_parser.add_argument(
    '--flag', default=[], action='append',
    help="Flag to pass to influence the compose.")
create_build_parser.add_argument(
    'builds', metavar='NVR', nargs='+',
    help='Koji builds NVRs.')
create_build_parser.add_argument(
    '--sigkey', default=[], action='append',
    help="Signing key ID using which the packages must be signed - "
    "\none\" for no signing key.")

wait_parser = subparsers.add_parser(
    'wait', help='wait for a compose to finish')
wait_parser.set_defaults(command='wait')
wait_parser.add_argument(
    'compose_id', default=None,
    help="ODCS compose id")


delete_parser = subparsers.add_parser(
    'delete', help='delete compose')
delete_parser.set_defaults(command='delete')
delete_parser.add_argument(
    'compose_id', default=None,
    help="ODCS compose id")

renew_parser = subparsers.add_parser(
    'renew', help='renew compose')
renew_parser.set_defaults(command='renew')
renew_parser.add_argument(
    'compose_id', default=None,
    help="ODCS compose id")

get_parser = subparsers.add_parser(
    'get', help='get compose info')
get_parser.set_defaults(command='get')
get_parser.add_argument(
    'compose_id', default=None,
    help="ODCS compose id")

args = parser.parse_args()

if not hasattr(args, "command"):
    parser.print_help()
    sys.exit(0)

if args.server is None:
    odcs_url = env_config[args.infra][args.env]['server_url']
else:
    odcs_url = args.server

if args.infra == 'fedora':
    id_provider = id_provider_config[args.env]

    # Get the auth token using the OpenID client.
    oidc = openidc_client.OpenIDCClient(
        'odcs',
        id_provider,
        {'Token': 'Token', 'Authorization': 'Authorization'},
        'odcs-authorizer',
        'notsecret',
    )

    scopes = [
        'openid',
        'https://id.fedoraproject.org/scope/groups',
        'https://pagure.io/odcs/new-compose',
        'https://pagure.io/odcs/renew-compose',
        'https://pagure.io/odcs/delete-compose',
    ]
    try:
        token = oidc.get_token(scopes, new_token=True)
        token = oidc.report_token_issue()
    except requests.exceptions.HTTPError as e:
        print(e.response.text)
        raise

    client = odcs.client.odcs.ODCS(
        odcs_url,
        auth_mech=odcs.client.odcs.AuthMech.OpenIDC,
        openidc_token=token,
    )
else:
    client = odcs.client.odcs.ODCS(
        odcs_url,
        auth_mech=odcs.client.odcs.AuthMech.Kerberos,
    )

request_args = {}
if getattr(args, "flag", False):
    request_args["flags"] = args.flag
if getattr(args, "arch", False):
    request_args["arches"] = args.arch
if getattr(args, "lookaside_repo", False):
    request_args["lookaside_repos"] = args.lookaside_repo
if getattr(args, "label", False):
    request_args["label"] = args.label
if getattr(args, "compose_type", False):
    request_args["compose_type"] = args.compose_type

try:
    args.sigkey = [key.replace('none', '') for key in getattr(args, "sigkey", [])]
    if args.command == "create":
        result = client.new_compose(
            source=args.source,
            source_type=args.source_type,
            packages=args.packages,
            results=args.result,
            sigkeys=args.sigkey,
            koji_event=args.koji_event,
            builds=args.builds,
            modular_koji_tags=args.modular_tag,
            module_defaults_url=args.module_defaults_url,
            module_defaults_commit=args.module_defaults_commit,
            **request_args
        )
    elif args.command == "create-tag":
        source = odcs.client.odcs.ComposeSourceTag(
            args.tag, args.packages, args.build, args.sigkey, args.koji_event,
            args.modular_tag, args.module_defaults_url, args.module_defaults_commit)
        result = client.request_compose(source, **request_args)
    elif args.command == "create-module":
        source = odcs.client.odcs.ComposeSourceModule(
            args.modules, args.sigkey, args.module_defaults_url, args.module_defaults_commit)
        result = client.request_compose(source, **request_args)
    elif args.command == "create-pulp":
        source = odcs.client.odcs.ComposeSourcePulp(args.content_sets)
        result = client.request_compose(source, **request_args)
    elif args.command == "create-raw-config":
        source = odcs.client.odcs.ComposeSourceRawConfig(
            args.raw_config_name, args.raw_config_commit, args.koji_event)
        result = client.request_compose(source, **request_args)
    elif args.command == "create-build":
        source = odcs.client.odcs.ComposeSourceBuild(
            args.builds, args.sigkey)
        result = client.request_compose(source, **request_args)
    elif args.command == "wait":
        result = {"id": int(args.compose_id)}
    elif args.command == "delete":
        args.no_wait = True
        result = client.delete_compose(args.compose_id)
    elif args.command == "renew":
        result = client.renew_compose(args.compose_id)
    elif args.command == "get":
        result = client.get_compose(args.compose_id)
    else:
        print("Unknown command %s" % args.command)
except requests.exceptions.HTTPError as e:
    # error message gets printed in ODCS class.
    sys.exit(-1)

if args.no_wait:
    print(json.dumps(result, indent=4, sort_keys=True))
else:
    if not args.quiet:
        print("Waiting for command %s on compose %d to finish." %
              (args.command, result["id"]))
    result = client.wait_for_compose(result["id"], 3600)
    print(json.dumps(result, indent=4, sort_keys=True))
