import time
import argparse
import logging
import json

import requests
#import search.tools.devops.libs.utils as u

from infra.yasm.yasmapi import GolovanRequest

'''
>>>
Примерная методика рассчёта (для истории):

-- Размер очереди : надо взять SLA RPS, посчитать, сколько приходится на 1 прокси,
взять таймаут, вычесть из него Q99 времени ответа бекенда, умножить получившееся время на rps,
получится максимальный размер очерди, выше которого точно не надо.

-- Количество воркеров : взять имеющееся количество запросов in flight на сервисе,
поделить на количество проксей, округлить до целого в большую сторону.

По-хорошему тут стоит ещё оценивать неравномерность загрузки проксей, но это в светлом будущем.
<<<
'''


def get_sla(ctype, service):

    get_sla_url = 'http://saas-dm.yandex.net/process_sla_description?action=get&ctype={ctype}&service={service}'.format(
        ctype=ctype,
        service=service
    )

    sla = requests.get(get_sla_url)
    return sla.json()


def get_searchproxy_quantity_per_dc(ctype):

    locations = ["SAS", "MAN", "VLA"]

    get_searchproxies_url = 'http://saas-dm.yandex.net/api/slots_by_interval/?ctype={ctype}&service=searchproxy'.format(
        ctype=ctype
    )

    proxy_shape = requests.get(get_searchproxies_url).json()

    proxy_quantity = {geo: 0 for geo in locations}
    for shard_range in proxy_shape:
        for slot in shard_range['slots']:
            proxy_quantity[slot['$datacenter$']] += 1

    return proxy_quantity


def get_backend_q99(ctype, service, geo):

    signal_q99 = 'quant(saas_unistat-search-times-{ctype}-{service}-full_dhhh, 99)'.format(
        ctype=ctype,
        service=service
    )

    period = 300
    et = time.time() - period * 5
    st = et - period * 288
    host = 'ASEARCH'
    signals = [
        'itype=rtyserver;ctype=prod;geo={geo}:{signal}'.format(
            geo=geo.lower(),
            signal=signal_q99
        )
    ]

    q99 = []
    for timestamp, values in GolovanRequest(host, period, st, et, signals):
        logging.debug(timestamp)
        logging.debug(values)
        if values[signals[0]] is not None:
            q99.append(values[signals[0]])
    q99.sort()
    logging.debug(q99)
    return q99[int(len(q99) * 0.9)]

def get_backend_time_avg(ctype, service, geo):

    signal_avg = 'havg(saas_unistat-search-times-{ctype}-{service}-full_dhhh)'.format(
        ctype=ctype,
        service=service
    )

    period = 300
    et = time.time() - period * 5
    st = et - period * 288
    host = 'ASEARCH'
    signals = [
        'itype=rtyserver;ctype=prod;geo={geo}:{signal}'.format(
            geo=geo.lower(),
            signal=signal_avg
        )
    ]

    time_avg = []
    for timestamp, values in GolovanRequest(host, period, st, et, signals):
        logging.debug(timestamp)
        logging.debug(values)
        if values[signals[0]] is not None:
            time_avg.append(values[signals[0]])

    logging.debug(time_avg)
    return sum(time_avg)/float(len(time_avg))


def get_inflight_queries(ctype, service, geo):

    period = 300
    et = time.time() - period * 5
    st = et - period * 288
    host = 'ASEARCH'

    signal_inflight = 'div(hcount(saas_unistat-times-{ctype}-{service}_dhhh, 0, inf), {period})'.format(
        ctype=ctype,
        service=service,
        period=period * 1000  # in ms
    )

    signals = [
        'itype=searchproxy;ctype=prod;geo={geo}:{signal}'.format(
            geo=geo.lower(),
            signal=signal_inflight
        )
    ]

    inflights = []
    for timestamp, values in GolovanRequest(host, period, st, et, signals):
        logging.debug(timestamp)
        logging.debug(values)
        if values[signals[0]] is not None:
            inflights.append(values[signals[0]])

    inflights.sort()
    logging.debug(inflights)
    return inflights[int(len(inflights) * 0.9)]


def calculate_queues(ctype, service):
    '''
-- Размер очереди : надо взять SLA RPS, посчитать, сколько приходится на 1 прокси,
взять таймаут, вычесть из него Q99 времени ответа бекенда, умножить получившееся время на rps,
получится максимальный размер очерди, выше которого точно не надо.
    '''

    sla = get_sla(ctype, service)
    total_sla_rps = max(sla.get('search_rps_planned', 0), sla.get('search_rps', 0))
    proxy_quantity_by_geo = get_searchproxy_quantity_per_dc(ctype)
    sla_q99 = sla.get('search_q_99_ms', sla.get('search_q_999_ms', None))
    assert sla_q99 is not None, "SLA Q99 or Q999 timings must be set"

    queue_length_per_dc = {}
    for geo in proxy_quantity_by_geo:

        backend_time_avg = get_backend_time_avg(ctype, service, geo)

        # Total rps for three locations in most cases, for two locations in case of one dc outage.
        sla_rps_per_proxy = total_sla_rps / 2.0 / proxy_quantity_by_geo[geo]
        queue_length_prediction = sla_rps_per_proxy * (sla_q99 - backend_time_avg)

        queue_length_per_dc[geo] = queue_length_prediction

    return queue_length_per_dc


def calculate_threads(ctype, service):
    '''
-- Количество воркеров : взять имеющееся количество запросов in flight на сервисе,
поделить на количество проксей, округлить до целого в большую сторону.
    '''

    proxy_quantity_by_geo = get_searchproxy_quantity_per_dc(ctype)
    threads = {}
    inflights_by_geo = {}

    for geo in proxy_quantity_by_geo:

        inflights = get_inflight_queries(ctype, service, geo)
        inflights_by_geo[geo] = inflights
        threads[geo] = int((inflights / float(proxy_quantity_by_geo[geo])) + 1)

    logging.debug(inflights_by_geo)

    return threads


def parse_cmd_args():

    description = 'Suggest threads/workers configuration for service'
    parser = argparse.ArgumentParser(description=description)

    parser.add_argument(
        '--ctype',
        help='ctype'
    )

    parser.add_argument(
        '--service',
        help='service'
    )

    return parser.parse_args()


def main():

#    u.logger_setup()
    args = parse_cmd_args()

    ctype = args.ctype
    service = args.service

    logging.debug(get_backend_q99(ctype, service, 'sas'))
    logging.debug(get_sla(ctype, service))
    logging.debug(get_searchproxy_quantity_per_dc(ctype))

    threads = calculate_threads(ctype, service)
    queues = calculate_queues(ctype, service)
    threads_and_queues = {}
    for geo in threads:
        threads_and_queues[geo] = {'threads': threads[geo], 'queue_length': queues[geo]}

    print(json.dumps(threads_and_queues, indent=2))


if __name__ == '__main__':
    main()
