#!/usr/bin/env python
# -*- coding: utf-8 -*-

import argparse
import os
import re
import subprocess
import sys
import yaml
import logging

sys.path.insert(0, '/opt/direct-py/startrek-python-client-sni-fix')
from startrek_client import Startrek
logging.getLogger('startrek_client').setLevel(logging.CRITICAL)

"""
TODO 
  * сравнивать установленную версию с той, которую устанавливали
  * не устанавливать версию, если предыдущая выставлена недавно
  * симлинки -get, -set
  * короткие алиасы для сервисов
  * команда "покажи список сервисов"

"""

def get_startrek_client():
    with open("/etc/direct-tokens/startrek") as fh:
        startrek_token = fh.read().strip()
    startrek_client = Startrek(token=startrek_token, useragent='direct-version')
    return startrek_client


def get_version_node_content(zk_host, zk_port, app):
    out = subprocess.check_output( ["direct-zkcli", "-H", zk_host, "-p", str(zk_port), "cat", apps[app]['zk_node']])
    return out


def cmd_get(opts):
    # TODO использовать библиотеку, см. zkdelivery_get.py 
    text = get_version_node_content(opts.host, opts.port, opts.app)
    print text
    return 


def cmd_set(opts):
    text = get_version_node_content(opts.host, opts.port, opts.app)
    print "Old value:\n%s" % text
    # TODO использовать библиотеку, см. zkdelivery_set.py 
    # echo 1.140417.140800-1 ; echo -n time= ; date -Iseconds) | direct-zkcli -H 127.0.0.1 set /direct/versions/direct

    cmd = "(echo %s ; echo -n time= ; date -Iseconds) | direct-zkcli -H %s -p %s set %s" % (opts.version, opts.host, opts.port, apps[opts.app]['zk_node'] )
    exit_code = subprocess.call(cmd, shell=True)
    if exit_code != 0:
        die("can't set version")
    text = get_version_node_content(opts.host, opts.port, opts.app)
    print "New value:\n%s" % text
    return 


def cmd_apps(opts):
    for app in sorted(apps.keys()):
        print("%s\t\t%s" % (app, apps[app]['zk_node']))
    return


def die(message=''):
    sys.stderr.write(message + "\n")
    exit(1)


def parse_args():
    parser = argparse.ArgumentParser()
    parser.add_argument("-f", "--force", help="Игнорировать валидацию. Использовать только в экстренных случаях.", action="store_true")
    parser.add_argument("-q", "--quiet", help="Молчаливый режим.", action="store_true")
    parser.add_argument("-H", "--host", help="Хост ZooKeeper. По умолчанию 127.0.0.1.", type=str, default="127.0.0.1")
    parser.add_argument("-p", "--port", help="Порт ZooKeeper. По умолчанию 2181.", type=int, default=2181)
    parser.add_argument("-e", "--environment", help="Окружение. По умолчанию продакшн", type=str, default='prod')
    opts, extra = parser.parse_known_args()
    if len(extra) == 0:
        die("ERROR: cmd expected")
    return(opts, extra)


def validate_version(version, app):
    startrek_client = get_startrek_client()
    releases = list(startrek_client.issues.find(
        'Queue: DIRECT Type: Release Components: "%s" Status: "Можно выкатывать"' % (
            apps[app]['tracker-component']
        )
    ))
    if not releases:
        die("ERROR: can't validate version of app '%s' because there are no ready for deploy releases %s" % (
           app,
           "(you can skip validation using --force)"
        ))

    if app == 'dna':
        version_pattern = r'[0-9]+\.[a-z0-9\-\.]+$'
    else:
        version_pattern = r'1\.\d+(?:\.\d+)?-1'

    version_found = False
    release_versions = []
    for release in releases:
        release_version = re.search(version_pattern, release.summary)
        if not release_version:
            release_versions.append('Unknown version in %s' % release.key)
        release_version = release_version.group(0)
        release_versions.append(release_version)

        if release_version == version:
            version_found = True
            break

    if not version_found:
        die("ERROR: expected versions %s but '%s' was provided %s" % (
            str(release_versions),
            version,
            "(you can skip validation using --force)"
        ))


def parse_options():
    opts, extra = parse_args()
    # если скрипт зовут по имени direct-version-<cmd> -- вытаскиваем cmd
    my_name = os.path.basename( sys.argv[0] )
    m = re.match("^direct-version-(\S+)$", my_name)
    if m:
        opts.cmd = m.group(1)
    else:
        opts.cmd = extra.pop(0)

    # валидируем параметры в зависимости от команды
    if opts.cmd == 'set':
        if len(extra) != 2 and opts.environment == 'prod':
            die("ERROR: 'set' takes exactly 2 parameters, %s given\n" % len(extra))
        opts.app = extra[0]
        if opts.app not in apps:
            die("ERROR: unknown app %s, stop" % opts.app)
        opts.version = extra[1]
        if not re.match(r"^[0-9a-z\.-]+$", opts.version):
            die("ERROR: bad version %s" % opts.version)

        if opts.environment == 'prod' and not opts.force:
            validate_version(opts.version, opts.app)

    elif opts.cmd == 'get':
        opts.app = extra[0]
        if len(extra) != 1:
            die("ERROR: 'get' tages exactly 1 parameter, %s given\n" % len(extra))
        if opts.app not in apps:
            die("ERROR: unknown app %s, stop" % opts.app)
    if opts.cmd not in cmds: 
        die("ERROR: unknown cmd %s, stop" % opts.cmd)
    return opts


def get_zk_nodes_from_config(config_file, env):
    with open(config_file, 'r') as f:
        data = yaml.load(f)

    apps = {}
    for app in data['apps']:
        if 'zookeeper-version-node' not in data['apps'][app]:
            continue
        apps[app] = {
                        'zk_node' : data['apps'][app]['zookeeper-version-node'].replace('/direct', '/direct/np/%s' % env if env != 'prod' else '/direct', 1),
                        'tracker-component': data['apps'][app].get('tracker-component', '')
                        #'zk_node' : data['apps'][app]['zookeeper-version-node'],
                     }
    return apps


def run():
    global apps
    opts, _ = parse_args()
    apps = get_zk_nodes_from_config('/etc/yandex-direct/direct-apps.conf.yaml', opts.environment)
    opts = parse_options()
    cmds[opts.cmd]['code'](opts)
    exit(0)


cmds = {
        'set': {
            'code': cmd_set,
            },
        'get': {
            'code': cmd_get,
            },
        'apps': {
            'code': cmd_apps,
            },
        }


if __name__ == '__main__':
    run()

