from argparse import ArgumentParser
from collections import namedtuple
import requests
import yaml
import logging

from library.python.vault_client.instances import Production as VaultClient

from .project_spec import ProjectSpec


Config = namedtuple("Config", "host, auto_token_secret_id, auth_token_secret_field")


def load_toloka_config(env_config, default_config):
    get_or_default = lambda key : env_config[key] if key in env_config else default_config[key]
    return Config(
        get_or_default("host"),
        get_or_default("auth_token_secret_id"),
        get_or_default("auth_token_secret_field"),
    )


def load_config(config_path):
    result = dict()  # environment -> Config
    config = yaml.load(open(config_path), Loader=yaml.FullLoader)
    default_params = config["default"]
    envs_config = config["environments"]
    for env in envs_config:
        result[env] = load_toloka_config(envs_config[env], default_params)

    return result


def load_secret(secret_id, field):
    client = VaultClient(decode_files=True)
    head_version = client.get_version(secret_id)
    return head_version["value"][field]


def get_toloka_project_spec(config, project_id):
    response = requests.get(
        'https://' + config.host + "/api/v1/projects/" + str(project_id),
        headers={
            'Authorization': load_secret(config.auto_token_secret_id, config.auth_token_secret_field),
        }
    )
    if response.status_code == 200:
        return ProjectSpec().from_json(response.json())
    else:
        print("Failed to download project spec: {}. status code: {}".format(project_id, response.status_code))
        raise requests.HTTPError(response.content)


def update_toloka_project_spec(spec, config, project_id):
    response = requests.put(
        'https://' + config.host + "/api/v1/projects/" + str(project_id),
        headers={
            'Authorization': load_secret(config.auto_token_secret_id, config.auth_token_secret_field),
            'content-type': 'application/json'
        },
        json=spec.json()
    )
    if response.status_code != 200:
        print("Failed to update project spec: {}. status code: {}".format(project_id, response.status_code))
        raise requests.HTTPError(response.content)


def main():
    logging.basicConfig(level=logging.DEBUG)
    parser = ArgumentParser(description='update project')
    parser.add_argument('-c', '--config', help='path to configuration file', required=True)
    parser.add_argument('-i', '--project_id', help='project id in toloka', required=True)
    parser.add_argument('-d', '--project_dir', help='path to project', required=True)
    parser.add_argument('-e', '--environment', help='environment',
                        choices=['toloka_production', 'toloka_testing', 'yang_production', 'yang_testing'], required=True)
    parser.add_argument('-t', '--target', help='update spec in toloka or arcadia', choices=['toloka', 'arcadia'])
    parser.add_argument('action', choices=['compare', 'update'])
    argv = parser.parse_args()

    envs_config = load_config(argv.config)

    toloka_spec = get_toloka_project_spec(envs_config[argv.environment], argv.project_id)

    if argv.action == 'compare':
        arcadia_spec = ProjectSpec().from_arcadia(argv.project_id, argv.project_dir)

        print('Comparing projects: toloka => arcadia...')
        content_to_diff = arcadia_spec.diff(toloka_spec)
        if content_to_diff:
            for content in content_to_diff:
                print('Comparing content: {}...'.format(content))
                print('\n'.join(list(content_to_diff[content])))
            print('Projects are NOT equal')
        else:
            print('Projects are equal')
    elif argv.action == 'update':
        assert(argv.target is not None), 'Update target is not specified'

        arcadia_spec = None
        try:
            arcadia_spec = ProjectSpec().from_arcadia(argv.project_id, argv.project_dir)
        except:
            print('Can not load spec from {}'.format(argv.project_dir))

        if arcadia_spec is not None and not arcadia_spec.diff(toloka_spec):
            print('Projects are equal')
        else:
            print('Updating {} project'.format(argv.target))
            if argv.target == 'arcadia':
                toloka_spec.save(argv.project_dir)
            elif argv.target == 'toloka':
                assert(arcadia_spec is not None), 'Failed to update toloka project'
                update_toloka_project_spec(arcadia_spec, envs_config[argv.environment], argv.project_id)
            else:
                print('Unknown update target: {}'.format(argv.target))
    else:
        print('Unknown action: {}'.format(argv.action))

    print('Done!')
