from kazoo.client import KazooClient

import time
import json


ZK_HOSTS = ('saas-zookeeper1.search.yandex.net:14880,'
            'saas-zookeeper2.search.yandex.net:14880,'
            'saas-zookeeper3.search.yandex.net:14880,'
            'saas-zookeeper4.search.yandex.net:14880,'
            'saas-zookeeper5.search.yandex.net:14880')


def init_zk():
    zk = KazooClient(ZK_HOSTS)
    zk.start()
    return zk


def get_file_from_version(fpath, ver, zk):
    fullp = '/saas10/history/' + fpath + str(ver)
    res = ''
    for i in range(0, 10000):
        cfullp = fullp + (('.$' + str(i)) if i > 0 else '')
        if zk.exists(cfullp):
            res += zk.get(cfullp)[0]
        else:
            break
    return res


def get_file(fpath, zk):
    ver = zk.get('/saas10/' + fpath)[0]
    return get_file_from_version(fpath, ver, zk)


def nodes_count(path, zk):
        chls = zk.get_children('/saas10/'+path)
        cnt = 1
        for ch in chls:
            cnt += nodes_count(path + '/' + ch, zk)
        return cnt


def nodes_size(path, zk):
        sz = zk.get_acls('/saas10/'+path)[1].dataLength
        if sz > 0:
            return sz
        chls = zk.get_children('/saas10/'+path)
        sz = 0
        for ch in chls:
            sz += nodes_size(path + '/' + ch, zk)
        return sz


def nodes_max_ts(path, zk):
        ts = zk.get_acls(path)[1].mtime
        chls = zk.get_children(path)
        for ch in chls:
            ts = max(ts, nodes_max_ts(path + '/' + ch, zk))
        return ts


def ts_days_ago(mtime, now_ts):
    return (now_ts - mtime/1000) / (3600 * 24)


def extract_cluster_ver(content):
        cont = json.loads(content)
        vers = []
        for cf in cont['files']:
            if 'cluster_meta_version' in cf:
                vers.append( cf['cluster_meta_version'])
            if cf['type'] == 'searchmap':
                vers.append(cf['version'])
        return vers


def extract_used_formulas_c(content):
    cont = json.loads(content)
    formulas = []
    for cf in cont['files']:
        if cf['type'] == 'plain':
            formulas.append( (cf['path'], cf['version']) )
    return formulas


def extract_used_formulas(service, zk):
    path = '/saas10/configs/'+service
    children_1 = zk.get_children(path)
    formulas = []
    for ch_1 in children_1:
        children_2 = zk.get_children(path + '/' + ch_1)
        for ch_2 in children_2:
            cont = get_file('configs/' + service + '/' + ch_1 + '/' + ch_2, zk)
            formulas.extend(extract_used_formulas_c(cont))
    form_paths = [f[0] for f in formulas]
    form_paths = list(set(form_paths))
    form_paths = sorted(form_paths)
    return form_paths


def list_deleted_configs(zk, service):
    hist_cf = zk.get_children('/saas10/history/configs/'+service)
    actual_cf = zk.get_children('/saas10/configs/'+service)
    deleted_cf = []
    for cf in hist_cf:
        found = False
        for acf in actual_cf:
            if acf in cf:
                found = True
                print 'presents: ', cf
                break
        if not found:
            deleted_cf.append(cf)
    deleted_cf = sorted(deleted_cf)
    deleted_cf = ['/configs/' + service + '/' + d for d in deleted_cf]
    return deleted_cf


def filter_used_configs(deleted_configs, used_configs):
    deleted_unused = []
    for d in deleted_configs:
        do_rm = True
        for u in used_configs:
            if u in d:
                do_rm = False
                print 'deleted but present in current_config: ', u
                break
        if do_rm:
            deleted_unused.append(d)
    return deleted_unused


def current_cluster_versions(zk, ctype):
    services = zk.get_children('/saas10/configs/')
    servs_ct = [s for s in services if zk.exists('/saas10/configs/' + s + '/' + ctype)]
    servs_ct = ['configs/' + s + '/' + ctype for s in servs_ct]
    servs_ct = sorted(servs_ct)
    versions = []
    for p in servs_ct:
        curs = zk.get_children('/saas10/' + p)
        print p
        for c in curs:
            content = get_file(p + '/' + c, zk)
            vers_c = extract_cluster_ver(content)
            versions.extend(vers_c)
            print vers_c
    versions = list(set(versions))
    versions = sorted(versions)
    return versions


def get_cluster_metas_history(zk, ctype):
    alls = zk.get_children('/saas10/history/common/' + ctype)
    alls = [p for p in alls if 'cluster.meta' in p]
    alls = sorted(alls)
    return alls


def clear_locks(zk, locks_path='/saas10/locks/abstract_locks', skip_to=0):
    now = int(time.time())
    locks = zk.get_children(locks_path)
    locks = sorted(locks)
    print 'total locks ', len(locks)
    for i, lock in enumerate(locks):
        if i < skip_to:
            continue
        lts = nodes_max_ts(locks_path + '/' + lock, zk)
        ago = ts_days_ago(lts, now)
        if ago > 45:
            print ago, lock
            zk.delete(locks_path + '/' + lock, recursive=True)
        else:
            if i % 500 == 0:
                print 'watched ', i


def clear_tasks(tasks_path, zk):
    tasks = zk.get_children(tasks_path)
    tasks = sorted(tasks)
    print 'total tasks ' , len(tasks)
    now = int(time.time())
    for i, task in enumerate(tasks):
        path = tasks_path + '/' + task
        try:
            tts = zk.get_acls(path)[1].mtime
        except Exception as e:
            print 'error on ', path, e
            continue
        ago = ts_days_ago(tts, now)
        if ago > 45:
            print ago, path
            zk.delete(path)
        else:
            if i % 100 == 0:
                print 'watched ', i


def clear_cluster_metas(zk, ctype):
    vers = current_cluster_versions(zk, ctype)
    hist = get_cluster_metas_history(zk, ctype)
    now = int(time.time())
    for h in hist:
        do_rm = True
        for v in vers:
            if '000' + str(v) in h:
                do_rm = False
                break
        if do_rm:
            cts = zk.get_acls('/saas10/history/common/' +ctype + '/' + h)[1].mtime
            ago = ts_days_ago(cts, now)
            if ago > 45:
                print ago, h
                zk.delete('/saas10/history/common/' + ctype + '/' + h)


def clear_deleted_configs_prep(zk, service):
    used = extract_used_formulas(service, zk)
    deleted_all = list_deleted_configs(zk, service)
    deleted = filter_used_configs(deleted_all, used)
    now = int(time.time())
    result = []
    for d in deleted:
        dts = zk.get_acls('/saas10/history/' + d)[1].mtime
        ago = ts_days_ago(dts, now)
        print ago, d
        if ago > 120:
            result.append('/saas10/history/' + d)
    return result


'''
cd zk_clean
ya py

from saas.tools.devops.zk_clean import *

zk = init_zk()

# SAFE
clear_locks(zk)
clear_locks(zk, locks_path='/saas_service10/queue/locks/abstract_locks')

# SAFE
paths = ['/saas10/history/failed_tasks/', '/saas10/failed_tasks/', '/saas10/history/cluster_tasks/', '/saas10/cluster_tasks/']
for p in paths:
    clear_tasks(p, zk)

# CAUTION
# Make sure that proxies are fully deployed recently
ctypes = ['stable', 'stable_kv', 'prestable'] # any existing ctype
for ctype in ctypes:
    clear_cluster_metas(zk, ctype)

# DANGER
# Make sure that service is fully deployed
# Look at output and files before actual removing
to_del = clear_deleted_configs_prep(zk, 'my_service')
to_del
for td in to_del:
    zk.delete(td, recursive=True)

'''
