# Standard library imports
import json

# Imports
import click

# Package imports
from . import client
from . import errors
from . import constants
from . import util

HOST = 'GENCOVE_HOST'
HOST_HELP = 'backend host, defaults to ${} -> "{}"'.format(HOST, constants.HOST_DEFAULT)
DEVICE_UUID = 'GENCOVE_DEVICE_UUID'
DEVICE_UUID_HELP = 'unique id for client device, defaults to hardware address'
TOKEN = 'GENCOVE_TOKEN'
TOKEN_HELP = 'backend access token, defaults to ${}'.format(TOKEN)

@click.group()
def cli():
    pass

@cli.group()
def auth():
    pass

@cli.group()
def study():
    pass

@study.group()
def participant():
    pass

@auth.command()
@click.argument('name')
@click.argument('email')
@click.option('--password', default=None, help='password associated with user acccount')
@click.option('--host', default=None, help=HOST_HELP, envvar=HOST)
@click.option('--device-uuid', default=None, help=DEVICE_UUID_HELP, envvar=DEVICE_UUID)
@click.option('--print-token/--no-print-token', default=True, help='indicate backend access token should be printed to console')
@click.option('--print-response/--no-print-response', default=False, help='indicate server response should be printed to console')
def register(name, email, password, host, device_uuid, print_token, print_response):
    '''Register new user on gencove backend'''
    c = client.Client(host, device_uuid)

    try:
        response_dict = c.register(name, email, password, return_response=True)
    except errors.HTTPError as e:
        text_out = util.extract_backend_message(e.response)
        click.echo(text_out, err=True)
        exit(1)
    except errors.GencoveClientError as e:
        click.echo(e, err=True)
        exit(1)

    if print_response:
        click.echo(util.get_output(response_dict))
    if print_token:
        click.echo(c.get_refresh_token())

@auth.command()
@click.argument('email')
@click.option('--password', default=None, help='password associated with user acccount')
@click.option('--host', default=None, help=HOST_HELP, envvar=HOST)
@click.option('--device-uuid', default=None, help=DEVICE_UUID_HELP, envvar=DEVICE_UUID)
@click.option('--print-token/--no-print-token', default=True, help='indicate backend access token should be printed to console')
@click.option('--print-response/--no-print-response', default=False, help='indicate server response should be printed to console')
def login(email, password, host, device_uuid, print_token, print_response):
    '''Log into gencove backend

    CAUTION: This will log out any sessions (tokens) the user has with specified device_uuid
    '''
    c = client.Client(host, device_uuid)

    try:
        response_dict = c.login(email, password, return_response=True)
    except errors.HTTPError as e:
        text_out = util.extract_backend_message(e.response)
        click.echo(text_out, err=True)
        exit(1)
    except errors.GencoveClientError as e:
        click.echo(e, err=True)
        exit(1)

    if print_response:
        click.echo(util.get_output(response_dict))
    if print_token:
        click.echo(c.get_refresh_token())

@auth.command()
@click.option('--token', required=True, help=TOKEN_HELP, envvar=TOKEN)
@click.option('--host', default=None, help=HOST_HELP, envvar=HOST)
@click.option('--device-uuid', default=None, help=DEVICE_UUID_HELP, envvar=DEVICE_UUID)
def logout(token, host, device_uuid):
    '''Log out of gencove backend'''
    c = client.Client(host, device_uuid)
    c.set_refresh_token(token)

    try:
        response_dict = c.logout(return_response=True)
    except errors.HTTPError as e:
        text_out = util.extract_backend_message(e.response)
        click.echo(text_out, err=True)
        exit(1)
    except errors.GencoveClientError as e:
        click.echo(e, err=True)
        exit(1)

    click.echo(util.get_output(response_dict))

@auth.command()
@click.argument('confirm-id')
@click.option('--host', default=None, help=HOST_HELP, envvar=HOST)
@click.option('--device-uuid', default=None, help=DEVICE_UUID_HELP, envvar=DEVICE_UUID)
def email_confirm(confirm_id, host, device_uuid):
    '''Confirm email'''
    c = client.Client(host, device_uuid)

    try:
        response_dict = c.email_confirm(confirm_id, return_response=True)
    except errors.HTTPError as e:
        text_out = util.extract_backend_message(e.response)
        click.echo(text_out, err=True)
        exit(1)
    except errors.GencoveClientError as e:
        click.echo(e, err=True)
        exit(1)

    click.echo(util.get_output(response_dict))

@auth.command()
@click.argument('email')
@click.option('--host', default=None, help=HOST_HELP, envvar=HOST)
@click.option('--device-uuid', default=None, help=DEVICE_UUID_HELP, envvar=DEVICE_UUID)
def password_forgot_init(email, host, device_uuid):
    '''Trigger sending of forgotten password email

    CAUTION: changing password invalidates all sessions (tokens) for current user
    '''
    c = client.Client(host, device_uuid)

    try:
        response_dict = c.email_confirm(confirm_id, return_response=True)
    except errors.HTTPError as e:
        text_out = util.extract_backend_message(e.response)
        click.echo(text_out, err=True)
        exit(1)
    except errors.GencoveClientError as e:
        click.echo(e, err=True)
        exit(1)

    click.echo(util.get_output(response_dict))

@auth.command()
@click.option('--token', required=True, help=TOKEN_HELP, envvar=TOKEN)
@click.option('--password', default=None, help='current password associated with user acccount')
@click.option('--new-password', default=None, help='new password to be associated with user acccount')
@click.option('--new-password-confirm', default=None, help='confirmation of new password')
@click.option('--host', default=None, help=HOST_HELP, envvar=HOST)
@click.option('--device-uuid', default=None, help=DEVICE_UUID_HELP, envvar=DEVICE_UUID)
@click.option('--print-token/--no-print-token', default=True, help='indicate new backend access token should be printed to console')
@click.option('--print-response/--no-print-response', default=False, help='indicate server response should be printed to console')
def password_change(token, password, new_password, new_password_confirm, host, device_uuid, print_token, print_response):
    '''Change password

    CAUTION: changing password invalidates all sessions (tokens) for current user
    '''
    c = client.Client(host, device_uuid)
    c.set_refresh_token(token)

    try:
        response_dict = c.password_change(
            password, new_password, new_password_confirm, return_response=True)
    except errors.HTTPError as e:
        text_out = util.extract_backend_message(e.response)
        click.echo(text_out, err=True)
        exit(1)
    except errors.GencoveClientError as e:
        click.echo(e, err=True)
        exit(1)

    if print_response:
        click.echo(util.get_output(response_dict))
    if print_token:
        click.echo(c.get_refresh_token())

@study.command()
@click.option('--token', required=True, help=TOKEN_HELP, envvar=TOKEN)
@click.option('--host', default=None, help=HOST_HELP, envvar=HOST)
@click.option('--device-uuid', default=None, help=DEVICE_UUID_HELP, envvar=DEVICE_UUID)
def get_all(token, host, device_uuid):
    '''List all studies owned by user'''
    c = client.Client(host, device_uuid)
    c.set_refresh_token(token)

    try:
        studies = c.studies_get()
    except errors.HTTPError as e:
        text_out = util.extract_backend_message(e.response)
        click.echo(text_out, err=True)
        exit(1)
    except errors.GencoveClientError as e:
        click.echo(e, err=True)
        exit(1)

    click.echo(util.pretty_json(studies))

@study.command()
@click.argument('study-id')
@click.option('--token', required=True, help=TOKEN_HELP, envvar=TOKEN)
@click.option('--host', default=None, help=HOST_HELP, envvar=HOST)
@click.option('--device-uuid', default=None, help=DEVICE_UUID_HELP, envvar=DEVICE_UUID)
@click.option('--key', default=None, help='output only this key from study info')
def get(study_id, token, host, device_uuid, key):
    '''Get single study owned by user'''
    c = client.Client(host, device_uuid)
    c.set_refresh_token(token)

    try:
        study = c.study_get(study_id)
    except errors.HTTPError as e:
        text_out = util.extract_backend_message(e.response)
        click.echo(text_out, err=True)
        exit(1)
    except errors.GencoveClientError as e:
        click.echo(e, err=True)
        exit(1)

    if key:
        try:
            click.echo(study[key])
        except KeyError as e:
            click.echo('Key "{}" not found'.format(key), err=True)
            exit(1)
    else:
        click.echo(util.pretty_json(study))

@study.command()
@click.argument('name')
@click.argument('href')
@click.argument('description')
@click.option('--enabled/--disabled', default=True, help='whether the study is enabled upon creation')
@click.option('--discoverable/--private', default=True, help='whether the study is discoverable upon creation')
@click.option('--token', required=True, help=TOKEN_HELP, envvar=TOKEN)
@click.option('--host', default=None, help=HOST_HELP, envvar=HOST)
@click.option('--device-uuid', default=None, help=DEVICE_UUID_HELP, envvar=DEVICE_UUID)
@click.option('--key', default=None, help='output only this key from study info')
def create(
        name, href, description, enabled, discoverable, token, host, 
        device_uuid, key):
    '''Create a new study'''
    c = client.Client(host, device_uuid)
    c.set_refresh_token(token)

    try:
        study = c.study_create(
            name=name, href=href, description=description, 
            study_enabled=enabled, discoverable=discoverable)
    except errors.HTTPError as e:
        text_out = util.extract_backend_message(e.response)
        click.echo(text_out, err=True)
        exit(1)
    except errors.GencoveClientError as e:
        click.echo(e, err=True)
        exit(1)

    if key:
        try:
            click.echo(study[key])
        except KeyError as e:
            click.echo('Key "{}" not found'.format(key), err=True)
            exit(1)
    else:
        click.echo(util.pretty_json(study))

@study.command()
@click.argument('study-id')
@click.option('--name', default=None, help='study name')
@click.option('--href', default=None, help='link to study website')
@click.option('--description', default=None, help='study description')
@click.option('--subsidy-enabled/--subsidy-disabled', default=None, help='whether study stubsidies are enabled')
@click.option('--enabled/--disabled', default=None, help='whether the study is enabled')
@click.option('--discoverable/--private', default=None, help='whether the study is discoverable')
@click.option('--token', required=True, help=TOKEN_HELP, envvar=TOKEN)
@click.option('--host', default=None, help=HOST_HELP, envvar=HOST)
@click.option('--device-uuid', default=None, help=DEVICE_UUID_HELP, envvar=DEVICE_UUID)
@click.option('--key', default=None, help='output only this key from study info')
def update(
        study_id, name, href, description, subsidy_enabled, enabled, 
        discoverable, token, host, device_uuid, key):
    '''Update an existing study'''
    c = client.Client(host, device_uuid)
    c.set_refresh_token(token)

    try:
        study = c.study_update(
            study_id=study_id, name=name, href=href, description=description, 
            subsidy_enabled=subsidy_enabled, study_enabled=enabled, 
            discoverable=discoverable)
    except errors.HTTPError as e:
        text_out = util.extract_backend_message(e.response)
        click.echo(text_out, err=True)
        exit(1)
    except errors.GencoveClientError as e:
        click.echo(e, err=True)
        exit(1)

    if key:
        try:
            click.echo(study[key])
        except KeyError as e:
            click.echo('Key "{}" not found'.format(key), err=True)
            exit(1)
    else:
        click.echo(util.pretty_json(study))

@participant.command()
@click.argument('study-id')
@click.option('--token', required=True, help=TOKEN_HELP, envvar=TOKEN)
@click.option('--host', default=None, help=HOST_HELP, envvar=HOST)
@click.option('--device-uuid', default=None, help=DEVICE_UUID_HELP, envvar=DEVICE_UUID)
def get_all(study_id, token, host, device_uuid):
    '''Get all participants of a study'''
    c = client.Client(host, device_uuid)
    c.set_refresh_token(token)

    try:
        participants = c.study_participants_get(study_id)
    except errors.HTTPError as e:
        text_out = util.extract_backend_message(e.response)
        click.echo(text_out, err=True)
        exit(1)
    except errors.GencoveClientError as e:
        click.echo(e, err=True)
        exit(1)

    click.echo(util.pretty_json(participants))

@participant.command()
@click.argument('study-id')
@click.argument('participant-ids', nargs=-1)
@click.option('--token', required=True, help=TOKEN_HELP, envvar=TOKEN)
@click.option('--host', default=None, help=HOST_HELP, envvar=HOST)
@click.option('--device-uuid', default=None, help=DEVICE_UUID_HELP, envvar=DEVICE_UUID)
def get_raw_data(study_id, participant_ids, token, host, device_uuid):
    '''Get all participants of a study'''
    c = client.Client(host, device_uuid)
    c.set_refresh_token(token)

    try:
        raw_data = c.study_raw_data_get(study_id, participant_ids)
    except errors.HTTPError as e:
        text_out = util.extract_backend_message(e.response)
        click.echo(text_out, err=True)
        exit(1)
    except errors.GencoveClientError as e:
        click.echo(e, err=True)
        exit(1)

    click.echo(util.pretty_json(raw_data))

@cli.command()
@click.option('--env', default='default', help='backend environment. Available environments are: {}'.format(util.get_envs_string()))
def host(env):
    if env in constants.ENVS:
        click.echo(constants.ENVS[env]['host'])
    else:
        click.echo('Unknown environment. Available environments are: {}'.format(util.get_envs_string()), err=True)

@cli.command()
@click.argument('study-id', type=int)
@click.argument('external-id')
@click.argument('study-api-key')
@click.option('--subsidy-in-cents', default=None, type=int, help='kit subsidy in cents provided by study')
@click.option('--url-template', default=constants.DEEPLINK_URL_DEFAULT, help='template for generated URL, must contain \'{jwt}\'')
@click.option('--print-url/--no-print-url', default=True, help='print complete deeplink URL or just JWT')
def jwt(study_id, external_id, study_api_key, subsidy_in_cents, url_template, print_url):
    if not print_url:
        url_template = None

    try:
        output = util.jwt_signed(study_id, external_id, study_api_key, subsidy_in_cents, url_template)
    except ValueError as e:
        click.echo(e, err=True)
        exit(1)
    except TypeError as e:
        click.echo(e, err=True)
        exit(1)

    click.echo(output)
