#!/usr/bin/env python
# -*- coding: utf8 -*-

"""

TODO
- убрать умолчальный конфиг (добавить явный параметр в https://a.yandex-team.ru/arc/trunk/arcadia/direct/infra/direct-utils/ppcdev-scripts-cron/etc/cron.d/yandex-du-ppcdev-scripts-cron)

"""

import argparse
import requests
import urllib
import json
import yaml

from requests.packages.urllib3.exceptions import InsecureRequestWarning
requests.packages.urllib3.disable_warnings(InsecureRequestWarning)

CONFIG_PATH = "/etc/upload-composite-secret/upload-composite-secret-conf.yaml"
token = ""


def get_headers(content_type=False):
    headers = { 
        "Authorization": "OAuth %s" % token,
    }
    if content_type:
        headers['Content-Type'] = 'application/json'

    return headers


def parse_options():
    parser = argparse.ArgumentParser(
        description="Составляет и обновляет общий (композитный) секрет для локальной доставки из других секретов"
    )
    parser.add_argument(
        "-c", "--config-path", dest="config_path",
        help="путь к файлу с конфигом (по умолчанию: %s)" % CONFIG_PATH, type=str, default=CONFIG_PATH
    )
    opts, extra = parser.parse_known_args()

    if extra:
        parser.error("uknown params")

    return opts


def read_config(config_path=CONFIG_PATH):
    with open(config_path, 'r') as fd:
        return yaml.load(fd)


def read_token(token_path):
    with open(token_path, 'r') as fd:
        return fd.read().strip()


def get_secret(secret):
    return requests.get(
        "https://vault-api.passport.yandex.net/1/secrets/%s/" % urllib.quote(secret),
        headers=get_headers(), timeout=10, verify=False
    ).json()['secret']


def get_version(version):
    return requests.get(
        "https://vault-api.passport.yandex.net/1/versions/%s/" % urllib.quote(version),
        headers=get_headers(content_type=True), verify=False, timeout=10
    ).json()['version']


def get_last_version(secret_versions):
    return max((version["created_at"], version["version"]) for version in secret_versions)[1]


def run():
    global token

    opts = parse_options()
    config = read_config(opts.config_path)
    token = read_token(config['token_path'])

    all_values = {}

    for secret in config['target_secrets']:
        try:
            secret_data = get_secret(secret)
            if not secret_data.get("secret_versions", []):
                continue

            last_version = get_last_version(secret_data["secret_versions"])
            version_data = get_version(last_version)

            for pair in version_data['value']:
                all_values["%s.%s" % (version_data['secret_name'], pair['key'])] = pair['value']

        except Exception as e:
            print "ERROR while processing secret %s: (%s) %s" % (secret, type(e), e) 

    comp_secret = get_secret(config['composite_secret'])

    comp_values = {}
    comp_last_version = ""
    if comp_secret.get("secret_versions", []):
        comp_last_version = get_last_version(comp_secret["secret_versions"])
        comp_version_data = get_version(comp_last_version)
        for pair in comp_version_data['value']:
            comp_values[pair['key']] = pair['value']

    values_to_update = [
        {'key': key, 'value': value} for key, value in all_values.items() if key not in comp_values or value != comp_values[key]
    ]
    values_to_update.extend({'key': key} for key in comp_values if key not in all_values)
    if not values_to_update:
        return

    if not comp_last_version:
        requests.post(
            "https://vault-api.passport.yandex.net/1/secrets/%s/versions/" % urllib.quote(comp_secret['uuid']),
            data=json.dumps({'value': values_to_update}),
            headers=get_headers(content_type=True), verify=False, timeout=10
        )
    else:
        requests.post(
            "https://vault-api.passport.yandex.net/1/versions/%s/" % urllib.quote(comp_last_version),
            data=json.dumps({'check_head': True, 'diff': values_to_update}),
            headers=get_headers(content_type=True), verify=False, timeout=10
        )


if __name__ == '__main__':
    run()

