# coding: utf-8

from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals

from saas.library.python.yasm.signal import YasmSignal, YasmSignalIterator
from saas.library.python.yasm.constants import YasmPeriod
import logging
from saas.library.python.nanny_proto.pod import Pod
from saas.library.python.deploy_manager_api.saas_service import SaasService


def get_volumes_data(nanny_pod):
    def findkey(arr, key, value):
        for el in arr:
            if getattr(el, key) == value:
                return el
        return None

    def findmount(arr, value):
        for el in arr:
            if not el.labels.attributes:
                continue
            if findkey(el.labels.attributes, "key", "mount_path").value == value:
                return el
        return None

    data_volume = findkey(nanny_pod.proto_msg.spec.disk_volume_requests, "id", "{}-data".format(nanny_pod.id.replace("_", "-"))) \
                  or findmount(nanny_pod.proto_msg.spec.disk_volume_requests, "/data")

    if data_volume is None:
        return 0, 0

    return data_volume.quota_policy.bandwidth_guarantee, data_volume.quota_policy.bandwidth_limit


def protobuf_to_dict(proto_obj):
    key_list = proto_obj.DESCRIPTOR.fields_by_name.keys()
    d = {}
    for key in key_list:
        d[key] = getattr(proto_obj, key)
    return d


class SaasServiceMetrics(object):

    LOGGER = logging.getLogger(__name__)

    @classmethod
    def for_saas_service(cls, saas_service):
        return cls(saas_service.ctype, saas_service.name)

    def __init__(self, saas_ctype, service_name):
        self._ctype = saas_ctype.replace('-', '_')
        self._service = service_name.replace('-', '_')

    @property
    def saas_service(self):
        return SaasService(self._ctype, self._service)

    def proxy_rps(self, **kwargs):
        return YasmSignal(
            signal='hcount(saas_unistat-times-{ctype}-{service}_dhhh)'.format(ctype=self._ctype, service=self._service),
            itype='searchproxy', **kwargs).signal_iterator

    def proxy_ram_avg(self, **kwargs):
        return YasmSignal(signal='conv(havg(portoinst-memory_usage_slot_hgram), Gi)', itype='searchproxy',
                          prj=self._ctype.replace("stable", "saas").replace("_", "-"), **kwargs).signal_iterator

    def proxy_ram_quant99(self, **kwargs):
        return YasmSignal(signal='conv(quant(portoinst-memory_usage_slot_hgram, 99), Gi)', itype='searchproxy',
                          prj=self._ctype.replace("stable", "saas").replace("_", "-"), **kwargs).signal_iterator

    def proxy_ram_max(self, **kwargs):
        return YasmSignal(signal='conv(max(portoinst-memory_usage_slot_hgram), Gi)', itype='searchproxy',
                          prj=self._ctype.replace("stable", "saas").replace("_", "-"), **kwargs).signal_iterator

    def proxy_cpu_avg(self, **kwargs):
        return YasmSignal(signal='havg(portoinst-cpu_usage_cores_hgram)', itype='searchproxy',
                          prj=self._ctype.replace("stable", "saas").replace("_", "-"), **kwargs).signal_iterator

    def proxy_cpu_quant99(self, **kwargs):
        return YasmSignal(signal='quant(portoinst-cpu_usage_cores_hgram, 99)', itype='searchproxy',
                          prj=self._ctype.replace("stable", "saas").replace("_", "-"), **kwargs).signal_iterator

    def proxy_cpu_max(self, **kwargs):
        return YasmSignal(signal='max(portoinst-cpu_usage_cores_hgram)', itype='searchproxy',
                          prj=self._ctype.replace("stable", "saas").replace("_", "-"), **kwargs).signal_iterator

    def backend_rps(self, **kwargs):
        return YasmSignal(signal='hcount(saas_unistat-search-times-{ctype}-{service}-full_dhhh)'.
                          format(ctype=self._ctype, service=self._service), itype='rtyserver', **kwargs).signal_iterator

    def backend_ram_avg(self, **kwargs):
        return YasmSignal(signal='conv(havg(portoinst-memory_usage_slot_hgram), Gi)',
                          itype='rtyserver',
                          prj="{ctype}-{service}".format(
                              ctype=self._ctype.replace("stable", "saas"),
                              service=self._service).replace("_", "-"), **kwargs).signal_iterator

    def backend_ram_quant99(self, **kwargs):
        return YasmSignal(signal='conv(quant(portoinst-memory_usage_slot_hgram, 99), Gi)',
                          itype='rtyserver',
                          prj="{ctype}-{service}".format(
                              ctype=self._ctype.replace("stable", "saas"),
                              service=self._service).replace("_", "-"), **kwargs).signal_iterator

    def backend_ram_max(self, **kwargs):
        return YasmSignal(signal='conv(max(portoinst-memory_usage_slot_hgram), Gi)',
                          itype='rtyserver',
                          prj="{ctype}-{service}".format(
                              ctype=self._ctype.replace("stable", "saas"),
                              service=self._service).replace("_", "-"), **kwargs).signal_iterator

    def backend_ram_min(self, **kwargs):
        return YasmSignal(signal='conv(min(portoinst-memory_usage_slot_hgram), Gi)',
                          itype='rtyserver',
                          prj="{ctype}-{service}".format(
                              ctype=self._ctype.replace("stable", "saas"),
                              service=self._service).replace("_", "-"), **kwargs).signal_iterator

    def backend_cpu_avg(self, **kwargs):
        return YasmSignal(signal='havg(portoinst-cpu_usage_cores_hgram)',
                          itype='rtyserver',
                          prj="{ctype}-{service}".format(
                              ctype=self._ctype.replace("stable", "saas"),
                              service=self._service).replace("_", "-"), **kwargs).signal_iterator

    def backend_cpu_quant99(self, **kwargs):
        return YasmSignal(signal='quant(portoinst-cpu_usage_cores_hgram, 99)',
                          itype='rtyserver',
                          prj="{ctype}-{service}".format(
                              ctype=self._ctype.replace("stable", "saas"),
                              service=self._service).replace("_", "-"), **kwargs).signal_iterator

    def backend_cpu_max(self, **kwargs):
        return YasmSignal(signal='max(portoinst-cpu_usage_cores_hgram)',
                          itype='rtyserver',
                          prj="{ctype}-{service}".format(
                              ctype=self._ctype.replace("stable", "saas"),
                              service=self._service).replace("_", "-"), **kwargs).signal_iterator

    def backend_cpu_min(self, **kwargs):
        return YasmSignal(signal='min(portoinst-cpu_usage_cores_hgram)',
                          itype='rtyserver',
                          prj="{ctype}-{service}".format(
                              ctype=self._ctype.replace("stable", "saas"),
                              service=self._service).replace("_", "-"), **kwargs).signal_iterator

    def backend_cpu_throttled_count(self, **kwargs):
        return YasmSignal(signal='portoinst-cpu_throttled_cores_tmmv',
                          itype='rtyserver',
                          prj="{ctype}-{service}".format(
                              ctype=self._ctype.replace("stable", "saas"),
                              service=self._service).replace("_", "-"), **kwargs).signal_iterator

    def backend_ssd_io_max(self, **kwargs):  # Not working until io monitorings won't be fixed
        return YasmSignal(signal='max(portoinst-io_ops_/ssd_hgram)',
                          itype='rtyserver',
                          prj="{ctype}-{service}".format(
                              ctype=self._ctype.replace("stable", "saas"),
                              service=self._service).replace("_", "-"), **kwargs).signal_iterator

    def backend_ssd_mbps_max(self, **kwargs):  # Not working until io monitorings won't be fixed
        return YasmSignal(signal='conv(max(portoinst-io_read_bps_/ssd_hgram), Mi)',
                          itype='rtyserver',
                          prj="{ctype}-{service}".format(
                              ctype=self._ctype.replace("stable", "saas"),
                              service=self._service).replace("_", "-"), **kwargs).signal_iterator

    def volume_data_quota_max(self, **kwargs):
        return YasmSignal(signal='conv(quant(portoinst-volume_/data_quota_hgram, max), Gi)',
                          itype='rtyserver',
                          prj="{ctype}-{service}".format(
                              ctype=self._ctype.replace("stable", "saas"),
                              service=self._service).replace("_", "-"), **kwargs).signal_iterator

    def volume_data_quota_min(self, **kwargs):
        return YasmSignal(signal='conv(quant(portoinst-volume_/data_quota_hgram, min), Gi)',
                          itype='rtyserver',
                          prj="{ctype}-{service}".format(
                              ctype=self._ctype.replace("stable", "saas"),
                              service=self._service).replace("_", "-"), **kwargs).signal_iterator

    def volume_data_quota_avg(self, **kwargs):
        return YasmSignal(signal='conv(havg(portoinst-volume_/data_quota_hgram), Gi)',
                          itype='rtyserver',
                          prj="{ctype}-{service}".format(
                              ctype=self._ctype.replace("stable", "saas"),
                              service=self._service).replace("_", "-"), **kwargs).signal_iterator

    def volume_data_avg(self, **kwargs):
        return YasmSignal(signal='conv(havg(portoinst-volume_/data_usage_hgram), Gi)',
                          itype='rtyserver',
                          prj="{ctype}-{service}".format(
                              ctype=self._ctype.replace("stable", "saas"),
                              service=self._service).replace("_", "-"), **kwargs).signal_iterator

    def volume_data_quant99(self, **kwargs):
        return YasmSignal(signal='conv(quant(portoinst-volume_/data_usage_hgram, 99), Gi)',
                          itype='rtyserver',
                          prj="{ctype}-{service}".format(
                              ctype=self._ctype.replace("stable", "saas"),
                              service=self._service).replace("_", "-"), **kwargs).signal_iterator

    def volume_data_max(self, **kwargs):
        return YasmSignal(signal='conv(max(portoinst-volume_/data_usage_hgram), Gi)',
                          itype='rtyserver',
                          prj="{ctype}-{service}".format(
                              ctype=self._ctype.replace("stable", "saas"),
                              service=self._service).replace("_", "-"), **kwargs).signal_iterator

    def volume_data_min(self, **kwargs):
        return YasmSignal(signal='conv(min(portoinst-volume_/data_usage_hgram), Gi)',
                          itype='rtyserver',
                          prj="{ctype}-{service}".format(
                              ctype=self._ctype.replace("stable", "saas"),
                              service=self._service).replace("_", "-"), **kwargs).signal_iterator

    def volume_data_quota_perc(self, **kwargs):
        return YasmSignal(signal='portoinst-volume_/data_usage_perc_txxx',
                          itype='rtyserver',
                          prj="{ctype}-{service}".format(
                              ctype=self._ctype.replace("stable", "saas"),
                              service=self._service).replace("_", "-"), **kwargs).signal_iterator

    def backend_oom_count(self, **kwargs):
        return YasmSignal(signal='hsum(portoinst-ooms_slot_hgram)',
                          itype='rtyserver',
                          prj="{ctype}-{service}".format(
                              ctype=self._ctype.replace("stable", "saas"),
                              service=self._service).replace("_", "-"), **kwargs).signal_iterator

    def volume_nvme_data_quant99(self, **kwargs):
        return YasmSignal(signal='conv(quant(portoinst-volume_/mnt/ssd0/data_usage_hgram, 99), Gi)',
                          itype='rtyserver',
                          prj="{ctype}-{service}".format(
                              ctype=self._ctype.replace("stable", "saas"),
                              service=self._service).replace("_", "-"), **kwargs).signal_iterator

    def volume_nvme_data_avg(self, **kwargs):
        return YasmSignal(signal='conv(havg(portoinst-volume_/mnt/nvme0/data_usage_hgram), Gi)',
                          itype='rtyserver',
                          prj="{ctype}-{service}".format(
                              ctype=self._ctype.replace("stable", "saas"),
                              service=self._service).replace("_", "-"), **kwargs).signal_iterator

    def volume_nvme_data_quota_min(self, **kwargs):
        return YasmSignal(signal='conv(quant(portoinst-volume_/mnt/nvme0/data_quota_hgram, min), Gi)',
                          itype='rtyserver',
                          prj="{ctype}-{service}".format(
                              ctype=self._ctype.replace("stable", "saas"),
                              service=self._service).replace("_", "-"), **kwargs).signal_iterator

    def volume_nvme_data_quota_avg(self, **kwargs):
        return YasmSignal(signal='conv(havg(portoinst-volume_/mnt/nvme0/data_quota_hgram), Gi)',
                          itype='rtyserver',
                          prj="{ctype}-{service}".format(
                              ctype=self._ctype.replace("stable", "saas"),
                              service=self._service).replace("_", "-"), **kwargs).signal_iterator

    def volume_nvme_data_quota_max(self, **kwargs):
        return YasmSignal(signal='conv(quant(portoinst-volume_/mnt/nvme0/data_quota_hgram, max), Gi)',
                          itype='rtyserver',
                          prj="{ctype}-{service}".format(
                              ctype=self._ctype.replace("stable", "saas"),
                              service=self._service).replace("_", "-"), **kwargs).signal_iterator

    def volume_nvme_data_quota_perc(self, **kwargs):
        return YasmSignal(signal='portoinst-volume_/mnt/nvme0/data_usage_perc_txxx',
                          itype='rtyserver',
                          prj="{ctype}-{service}".format(
                              ctype=self._ctype.replace("stable", "saas"),
                              service=self._service).replace("_", "-"), **kwargs).signal_iterator

    def volume_ssd_data_quant99(self, **kwargs):
        return YasmSignal(signal='conv(quant(portoinst-volume_/mnt/ssd0/data_usage_hgram, 99), Gi)',
                          itype='rtyserver',
                          prj="{ctype}-{service}".format(
                              ctype=self._ctype.replace("stable", "saas"),
                              service=self._service).replace("_", "-"), **kwargs).signal_iterator

    def volume_ssd_data_avg(self, **kwargs):
        return YasmSignal(signal='conv(havg(portoinst-volume_/mnt/ssd0/data_usage_hgram), Gi)',
                          itype='rtyserver',
                          prj="{ctype}-{service}".format(
                              ctype=self._ctype.replace("stable", "saas"),
                              service=self._service).replace("_", "-"), **kwargs).signal_iterator

    def volume_ssd_data_min(self, **kwargs):
        return YasmSignal(signal='conv(min(portoinst-volume_/mnt/ssd0/data_usage_hgram), Gi)',
                          itype='rtyserver',
                          prj="{ctype}-{service}".format(
                              ctype=self._ctype.replace("stable", "saas"),
                              service=self._service).replace("_", "-"), **kwargs).signal_iterator

    def volume_ssd_data_max(self, **kwargs):
        return YasmSignal(signal='conv(max(portoinst-volume_/mnt/ssd0/data_usage_hgram), Gi)',
                          itype='rtyserver',
                          prj="{ctype}-{service}".format(
                              ctype=self._ctype.replace("stable", "saas"),
                              service=self._service).replace("_", "-"), **kwargs).signal_iterator

    def volume_ssd_data_quota_min(self, **kwargs):
        return YasmSignal(signal='conv(quant(portoinst-volume_/mnt/ssd0/data_quota_hgram, min), Gi)',
                          itype='rtyserver',
                          prj="{ctype}-{service}".format(
                              ctype=self._ctype.replace("stable", "saas"),
                              service=self._service).replace("_", "-"), **kwargs).signal_iterator

    def volume_ssd_data_quota_avg(self, **kwargs):
        return YasmSignal(signal='conv(havg(portoinst-volume_/mnt/ssd0/data_quota_hgram), Gi)',
                          itype='rtyserver',
                          prj="{ctype}-{service}".format(
                              ctype=self._ctype.replace("stable", "saas"),
                              service=self._service).replace("_", "-"), **kwargs).signal_iterator

    def volume_ssd_data_quota_max(self, **kwargs):
        return YasmSignal(signal='conv(quant(portoinst-volume_/mnt/ssd0/data_quota_hgram, 99), Gi)',
                          itype='rtyserver',
                          prj="{ctype}-{service}".format(
                              ctype=self._ctype.replace("stable", "saas"),
                              service=self._service).replace("_", "-"), **kwargs).signal_iterator

    def volume_ssd_data_quota_perc(self, **kwargs):
        return YasmSignal(signal='portoinst-volume_/mnt/ssd0/data_usage_perc_txxx',
                          itype='rtyserver',
                          prj="{ctype}-{service}".format(
                              ctype=self._ctype.replace("stable", "saas"),
                              service=self._service).replace("_", "-"), **kwargs).signal_iterator

    def network_bandwidth_output(self, **kwargs):
        return YasmSignal(signal='conv(sum(netstat-obytes-eth<0|1|2|3|4|5>_summ), Mi)',
                          itype='rtyserver',
                          prj="{ctype}-{service}".format(
                              ctype=self._ctype.replace("stable", "saas"),
                              service=self._service).replace("_", "-"), **kwargs).signal_iterator

    def network_bandwidth_input(self, **kwargs):
        return YasmSignal(signal='conv(sum(netstat-ibytes-eth<0|1|2|3|4|5>_summ), Mi)',
                          itype='rtyserver',
                          prj="{ctype}-{service}".format(
                              ctype=self._ctype.replace("stable", "saas"),
                              service=self._service).replace("_", "-"), **kwargs).signal_iterator

    def dm_replica_size_docs_max(self, **kwargs):
        return YasmSignal(signal='dm_dashboard-{}-{}-max-replica-docs_txxv'.format(self._ctype, self._service),
                          itype='deploymanager', **kwargs).signal_iterator

    def dm_replica_size_docs_min(self, **kwargs):
        return YasmSignal(signal='dm_dashboard-{}-{}-min-replica-docs_txxv'.format(self._ctype, self._service),
                          itype='deploymanager', **kwargs).signal_iterator

    def dm_replica_size_gb_max(self, **kwargs):
        return YasmSignal(signal='dm_dashboard-{}-{}-max-replica-size-gb_txxv'.format(self._ctype, self._service),
                          itype='deploymanager', **kwargs).signal_iterator

    def dm_replica_size_gb_min(self, **kwargs):
        return YasmSignal(signal='dm_dashboard-{}-{}-min-replica-size-gb_txxv'.format(self._ctype, self._service),
                          itype='deploymanager', **kwargs).signal_iterator

    @property
    def pods_resources_allocated(self):
        nanny_services = self.saas_service.nanny_services
        if not nanny_services:
            self.LOGGER.error("Couldn't found any nanny service for saas service {}/{}".format(self._service, self._ctype))
            return []
        if len(nanny_services) > 1:
            self.LOGGER.warning("Found more than 1 nanny service for saas service {}/{}: {}, get first".format(self._service, self._ctype, nanny_services))

        pods = sum([list(Pod.list(cluster=key, service_id=nanny_services[0])) for key in ["SAS", "MAN", "VLA"]], [])
        pods_resources = [protobuf_to_dict(el.proto_msg.spec.resource_requests) for el in pods]

        for i in range(len(pods)):
            pods_resources[i]['ssd_mbps_guarantee'], pods_resources[i]['ssd_mbps_limit'] = get_volumes_data(pods[i])

        print(pods[0].proto_msg.spec.resource_requests)
        keys = pods_resources[0].keys()
        return {key: [el[key] for el in pods_resources] for key in keys}

    def linear(self, signal1, signal2, interval_begin=None, interval_end=None, period=YasmPeriod.five_seconds, with_coefs=False, first_iter_params=None, second_iter_params=None):
        first_iter_params = {} if first_iter_params is None else first_iter_params
        second_iter_params = {} if second_iter_params is None else second_iter_params
        if type(signal1) == YasmSignalIterator:
            signal1 = signal1.values(interval_begin, interval_end, period=period, **first_iter_params)
        if type(signal2) == YasmSignalIterator:
            signal2 = signal2.values(interval_begin, interval_end, period=period, **second_iter_params)
        def get_linear(x, y):
            sum_x = sum(x)
            sum_y = sum(y)
            sum_xy = sum([xp*yp for xp, yp in zip(x, y)])
            sum_x2 = sum([xp*xp for xp in x])
            n = len(x)
            k = 0 if not n and not (sum_x2/n - (sum_x/n)**2) else (sum_xy/n - sum_x*sum_y/(n**2))/(sum_x2/n - (sum_x/n)**2)
            b = 0 if not n else sum_y/n - k*sum_x/n
            return k, b
        k, b = get_linear(signal1, signal2)
        def f(x, coef_k, coef_b):
            return [coef_k*xp+coef_b for xp in x]
        if with_coefs:
            return f(signal1, k, b), (float(k), float(b))
        return f(signal1, k, b)
