import logging
from configparser import ConfigParser
from contextlib import contextmanager
import time
import re


def parse_duration(duration):
    """
    Stolen from yandex-tank
    Parse duration string, such as '3h2m3s' into seconds
    >>> parse_duration('3h2m3s')
    10923
    >>> parse_duration('5')
    5
    """
    if not duration:
        return 0
    _re_token = re.compile('([-0-9.]+)([dhms]?)')

    def parse_token(dur, multiplier):
        multipliers = {
            'd': 86400,
            'h': 3600,
            'm': 60,
            's': 1,
        }
        if multiplier:
            return int(float(dur) * multipliers.get(multiplier, 1))
        else:
            return int(float(dur))

    return sum(parse_token(*token) for token in _re_token.findall(duration))


@contextmanager
def log_time_context(tag='', longer=0):
    """

    :param tag: log time preceeded with this tag
    :param longer: log only if execution took longer than this (seconds)
    :return:
    """
    start = time.time()
    yield
    end = time.time()
    if end - start > longer:
        logging.error('CONTEXT TIMER %s: %s' % (tag, round(end - start, 3)))


def xss_escape(s):
    return s.replace('>', '&gt;').replace('<', '&lt;')


def xss_unescape(s):
    return s.replace('&gt;', '>').replace('&lt;', '<')


def escape_string(s):
    return s.replace('\\', '\\\\').replace("'", "\\'")


def count_percentage(count_data):
    """
    returns list
    :param count_data: list of count
    """
    summa = float(sum(count_data))
    percent_values = [round(float(value) * 100 / summa, 3)
                      for value in count_data]
    return percent_values


def transliterate(text):
    transdict = {
        'а': 'a', 'А': 'A',
        'Б': 'B', 'б': 'b',
        'В': 'V', 'в': 'v',
        'Г': 'G', 'г': 'g',
        'Д': 'D', 'д': 'd',
        'Е': 'E', 'е': 'e',
        'Ё': 'Yo', 'ё': 'yo',
        'Ж': 'Zh', 'ж': 'zh',
        'З': 'Z', 'з': 'z',
        'И': 'I', 'и': 'i',
        'Й': 'Y', 'й': 'y',
        'К': 'K', 'к': 'k',
        'Л': 'L', 'л': 'l',
        'М': 'M', 'м': 'm',
        'Н': 'N', 'н': 'n',
        'О': 'O', 'о': 'o',
        'П': 'p', 'п': 'p',
        'Р': 'R', 'р': 'r',
        'С': 'S', 'с': 's',
        'Т': 'T', 'т': 't',
        'У': 'U', 'у': 'u',
        'Ф': 'F', 'ф': 'f',
        'Х': 'Kh', 'х': 'kh',
        'Ц': 'Ts', 'ц': 'ts',
        'Ч': 'Ch', 'ч': 'ch',
        'Ш': 'Sh', 'ш': 'sh',
        'Щ': 'Sch', 'щ': 'sch',
        'Ъ': '`', 'ъ': '`',
        'Ы': 'Y', 'ы': 'y',
        'Ь': '`', 'ь': '`',
        'Э': 'E', 'э': 'e',
        'Ю': 'Yu', 'ю': 'yu',
        'Я': 'Ya', 'я': 'ya',
        ' ': '-', '_': '-',
    }
    return ''.join(transdict.get(symbol, symbol) for symbol in text)


def format_job_dates(jobs):
    dates = [job_obj.basic_query_params['job_date'] for job_obj in jobs]
    return ', '.join('toDate({})'.format(date) for date in dates)


class Ini2Json(object):
    """
    Not actually a JSON, but an OrderedDict of OrderedDicts
    """

    def __init__(self, ini_conf: str):
        self.ini_conf = ini_conf
        self.cp = ConfigParser()

    def convert(self) -> dict:
        self.cp.read_string(self.ini_conf)
        json_conf = self.cp._sections
        json_conf['DEFAULT'] = self.cp.defaults()
        json_conf['DEFAULT']['__name__'] = 'DEFAULT'
        for section in json_conf:
            if json_conf[section].get('__name__'):
                del json_conf[section]['__name__']
            for param in json_conf[section]:
                json_conf[section][param] = json_conf[section][param].replace('\n', '\n\t')
        return json_conf


class Json2Ini(object):
    """
    supports plain dict and no nested options for ini
    """
    def __init__(self, conf_json):
        self.conf_json = conf_json

    def convert(self):
        conf_ini = ''
        for section in self.conf_json.items():
            conf_ini += '[%s]\n' % str(section[0])
            for option in section[1].items():
                try:
                    conf_ini += '%s=%s\n' % tuple(str(o) for o in option)
                except UnicodeEncodeError:
                    conf_ini += '%s=%s\n' % option
        return conf_ini[:-1]


# shard = 01
# replica = fqdn

# PRODUCTION FQDN: kshm02i.yandex.net, kshm02e.yandex.net, kshm03f.yandex.net

# TESTING FQDN: kshm01ht.cloud.load.yandex.net, kshm01it.cloud.load.yandex.net, kshm02ht.cloud.load.yandex.net

# noinspection SqlResolve
CLICKHOUSE_DB_STRUCT_REPLICATED = '''
CREATE TABLE IF NOT EXISTS loaddb.rt_microsecond_histograms (job_date Date, job_id UInt32, tag String, time DateTime, bin UInt32, cnt UInt32) ENGINE = ReplicatedMergeTree('/clickhouse/tables/{shard}/rt_microsecond_histograms', '{replica}', job_date, job_id, (job_id, tag, time), 8192);
CREATE TABLE IF NOT EXISTS loaddb.rt_quantiles (job_date Date, job_id UInt32, tag String, time DateTime, q50 Float64,  q75 Float64, q80 Float64,  q85 Float64,  q90 Float64,  q95 Float64,  q98 Float64,  q99 Float64,  q100 Float64) ENGINE = ReplicatedMergeTree('/clickhouse/tables/{shard}/rt_quantilies', '{replica}', job_date, job_id, (job_id, tag, time), 8192);
CREATE TABLE IF NOT EXISTS loaddb.rt_microsecond_details (job_date Date, job_id UInt32, tag String, time DateTime, connect_time_sum UInt64, send_time_sum UInt64, latency_sum UInt64, receive_time_sum UInt64, reqps UInt32, resps UInt32, threads UInt32, igress UInt32, egress UInt32, self_load UInt8) ENGINE = ReplicatedMergeTree('/clickhouse/tables/{shard}/rt_microsecond_details', '{replica}', job_date, job_id, (job_id, tag, time), 8192);
CREATE TABLE IF NOT EXISTS loaddb.net_codes (job_date Date, job_id UInt32, tag String, time DateTime, code Int32, cnt UInt32) ENGINE = ReplicatedMergeTree('/clickhouse/tables/{shard}/net_codes', '{replica}', job_date, job_id, (job_id, tag, time, code), 8192);
CREATE TABLE IF NOT EXISTS loaddb.proto_codes (job_date Date, job_id UInt32, tag String, time DateTime, code Int32, cnt UInt32) ENGINE = ReplicatedMergeTree('/clickhouse/tables/{shard}/proto_codes', '{replica}', job_date, job_id, (job_id, tag, time, code), 8192);
CREATE TABLE IF NOT EXISTS loaddb.monitoring_verbose_data (job_date Date, job_id UInt32, target_host String, metric_name String, time DateTime, value Float64) ENGINE = ReplicatedMergeTree('/clickhouse/tables/{shard}/monitoring_verbose_data', '{replica}', job_date, job_id, (job_id, target_host, metric_name, time), 8192);


CREATE TABLE IF NOT EXISTS loaddb.rt_microsecond_histograms_buffer AS rt_microsecond_histograms ENGINE = Buffer(loaddb, rt_microsecond_histograms, 16, 10, 100, 10000, 1000000, 10000000, 20000000);
CREATE TABLE IF NOT EXISTS loaddb.rt_quantiles_buffer AS rt_quantiles ENGINE = Buffer(loaddb, rt_quantiles, 16, 10, 100, 10000, 1000000, 10000000, 20000000);
CREATE TABLE IF NOT EXISTS loaddb.rt_microsecond_details_buffer AS rt_microsecond_details ENGINE = Buffer(loaddb, rt_microsecond_details, 16, 10, 100, 10000, 1000000, 10000000, 20000000);
CREATE TABLE IF NOT EXISTS loaddb.net_codes_buffer AS net_codes ENGINE = Buffer(loaddb, net_codes, 16, 10, 100, 10000, 1000000, 10000000, 20000000);
CREATE TABLE IF NOT EXISTS loaddb.proto_codes_buffer AS proto_codes ENGINE = Buffer(loaddb, proto_codes, 16, 10, 100, 10000, 1000000, 10000000, 20000000);
CREATE TABLE IF NOT EXISTS loaddb.monitoring_verbose_data_buffer AS monitoring_verbose_data ENGINE = Buffer(loaddb, monitoring_verbose_data, 16, 10, 100, 10000, 1000000, 10000000, 20000000);
'''

# noinspection SqlResolve
CLICKHOUSE_DB_STRUCT = '''
CREATE TABLE IF NOT EXISTS loaddb.rt_microsecond_histograms (job_date Date, job_id UInt32, tag String, time DateTime, bin UInt32, cnt UInt32) ENGINE = MergeTree(job_date, job_id, (job_id, tag, time), 8192);
CREATE TABLE IF NOT EXISTS loaddb.rt_quantiles (job_date Date, job_id UInt32, tag String, time DateTime, q50 Float64,  q75 Float64, q80 Float64,  q85 Float64,  q90 Float64,  q95 Float64,  q98 Float64,  q99 Float64,  q100 Float64) ENGINE = MergeTree(job_date, job_id, (job_id, tag, time), 8192);
CREATE TABLE IF NOT EXISTS loaddb.rt_microsecond_details (job_date Date, job_id UInt32, tag String, time DateTime, connect_time_sum UInt64, send_time_sum UInt64, latency_sum UInt64, receive_time_sum UInt64, reqps UInt32, resps UInt32, threads UInt32, igress UInt32, egress UInt32, self_load UInt8) ENGINE = MergeTree(job_date, job_id, (job_id, tag, time), 8192);
CREATE TABLE IF NOT EXISTS loaddb.net_codes (job_date Date, job_id UInt32, tag String, time DateTime, code Int32, cnt UInt32) ENGINE = MergeTree(job_date, job_id, (job_id, tag, time, code), 8192);
CREATE TABLE IF NOT EXISTS loaddb.proto_codes (job_date Date, job_id UInt32, tag String, time DateTime, code Int32, cnt UInt32) ENGINE = MergeTree(job_date, job_id, (job_id, tag, time, code), 8192);
CREATE TABLE IF NOT EXISTS loaddb.monitoring_verbose_data (job_date Date, job_id UInt32, target_host String, metric_name String, time DateTime, value Float64) Engine = MergeTree(job_date, job_id, (job_id, target_host, metric_name, time), 8192);

CREATE TABLE IF NOT EXISTS loaddb.rt_microsecond_histograms_buffer AS loaddb.rt_microsecond_histograms ENGINE = Buffer(loaddb, rt_microsecond_histograms, 16, 10, 100, 10000, 1000000, 10000000, 20000000);
CREATE TABLE IF NOT EXISTS loaddb.rt_quantiles_buffer AS loaddb.rt_quantiles ENGINE = Buffer(loaddb, rt_quantiles, 16, 10, 100, 10000, 1000000, 10000000, 20000000);
CREATE TABLE IF NOT EXISTS loaddb.rt_microsecond_details_buffer AS loaddb.rt_microsecond_details ENGINE = Buffer(loaddb, rt_microsecond_details, 16, 10, 100, 10000, 1000000, 10000000, 20000000);
CREATE TABLE IF NOT EXISTS loaddb.net_codes_buffer AS loaddb.net_codes ENGINE = Buffer(loaddb, net_codes, 16, 10, 100, 10000, 1000000, 10000000, 20000000);
CREATE TABLE IF NOT EXISTS loaddb.proto_codes_buffer AS loaddb.proto_codes ENGINE = Buffer(loaddb, proto_codes, 16, 10, 100, 10000, 1000000, 10000000, 20000000);
CREATE TABLE IF NOT EXISTS loaddb.monitoring_verbose_data_buffer AS loaddb.monitoring_verbose_data ENGINE = Buffer(loaddb, monitoring_verbose_data, 16, 10, 100, 10000, 1000000, 10000000, 20000000);
'''

CLICKHOUSE_VOLTA_DB_STRUCT = '''
CREATE DATABASE IF NOT EXISTS volta;

USE volta;

CREATE TABLE IF NOT EXISTS logs (key_date Date, test_id String, time UInt64, value String) ENGINE = ReplicatedMergeTree('/clickhouse/tables/{shard}/logs', '{replica}', key_date, test_id, (test_id, time), 8192);
CREATE TABLE IF NOT EXISTS current (key_date Date, test_id String, time UInt64, value Float32) ENGINE = ReplicatedMergeTree('/clickhouse/tables/{shard}/current', '{replica}', key_date, test_id, (test_id, time), 8192);

CREATE TABLE IF NOT EXISTS currents (key_date Date, test_id String, uts UInt64, value Float32) ENGINE = ReplicatedMergeTree('/clickhouse/tables/{shard}/currents', '{replica}', key_date, test_id, (test_id, uts), 8192);
CREATE TABLE IF NOT EXISTS syncs (key_date Date, test_id String, sys_uts UInt64, log_uts UInt64, app String, tag String, message String) ENGINE = ReplicatedMergeTree('/clickhouse/tables/{shard}/syncs', '{replica}', key_date, test_id, (test_id, sys_uts), 8192);
CREATE TABLE IF NOT EXISTS events (key_date Date, test_id String, sys_uts UInt64, log_uts UInt64, app String, tag String, message String) ENGINE = ReplicatedMergeTree('/clickhouse/tables/{shard}/events', '{replica}', key_date, test_id, (test_id, sys_uts), 8192);
CREATE TABLE IF NOT EXISTS metrics (key_date Date, test_id String, sys_uts UInt64, log_uts UInt64, app String, tag String, value Float64) ENGINE = ReplicatedMergeTree('/clickhouse/tables/{shard}/metrics', '{replica}', key_date, test_id, (test_id, sys_uts), 8192);
CREATE TABLE IF NOT EXISTS fragments (key_date Date, test_id String, sys_uts UInt64, log_uts UInt64, app String, tag String, message String) ENGINE = ReplicatedMergeTree('/clickhouse/tables/{shard}/fragments', '{replica}', key_date, test_id, (test_id, sys_uts), 8192);
CREATE TABLE IF NOT EXISTS logentries (key_date Date, test_id String, sys_uts UInt64, message String) ENGINE = ReplicatedMergeTree('/clickhouse/tables/{shard}/logentries', '{replica}', key_date, test_id, (test_id, sys_uts), 8192);
'''
