from infra.awacs.proto import api_pb2, api_stub, model_pb2
import nanny_rpc_client
from saas.library.python.nanny_proto.rpc_client_base import NannyRpcClientBase
from saas.library.python.deploy_manager_api.client import DeployManagerApiClient
from google.protobuf import json_format
from saas.library.python.bundle_networks import BundleNetworks
from saas.library.python.awacs import balancer
from saas.library.python.awacs import backend
from saas.library.python.awacs import upstream
from saas.library.python.awacs import l3balancer
from saas.library.python.awacs import certificate
from saas.library.python.awacs import its
from saas.library.python.awacs import helpers
import json
from library.python import resource
import logging
import time


class NamespaceManager(NannyRpcClientBase):
    LOGGER = logging.getLogger(__name__)
    _RPC_URL = "https://awacs.yandex-team.ru/api/"
    _OAUTH_SLUG = 'awacs'
    _API_STUB = api_stub.NamespaceServiceStub
    _API_DM = None

    @classmethod
    def _init_dm_client(cls):
        if cls._API_DM is None:
            cls._API_DM = DeployManagerApiClient()

    def __init__(self):
        super(NannyRpcClientBase, self).__init__()
        self._init_client()
        self._init_dm_client()
        self.balancer_manager = balancer.BalancerManager()
        self.backend_manager = backend.BackendManager()
        self.upstream_manager = upstream.UpstreamManager()
        self.certificate_manager = certificate.CertificateManager()
        self.l3_balancer_manager = l3balancer.L3BalancerManager()
        self.its_manager = its.ITSManager()

    def is_namespace_exist(self, ctype):
        try:
            self.get_namespace(ctype)
            return True
        except nanny_rpc_client.exceptions.NotFoundError:
            return False

    def separated_namespace_for_ctype(self, ctype):
        if self.is_namespace_exist(ctype):
            return helpers.namespace_id_from_ctype(ctype)
        else:
            return ""

    def set_up_alerting(self, ctype):
        req_pb = api_pb2.UpdateNamespaceRequest()
        ns = self.get_namespace(ctype).namespace
        req_pb.meta.CopyFrom(ns.meta)
        req_pb.spec.CopyFrom(ns.spec)
        settings_dict = json.loads(resource.find('saas/library/python/awacs/default_alerting_settings.json'))
        alert_settings = model_pb2.NamespaceSpec.AlertingSettings()
        json_format.ParseDict(settings_dict, alert_settings)
        req_pb.spec.alerting.CopyFrom(alert_settings)
        return self._CLIENT.update_namespace(req_pb)

    @property
    def represented_ctypes_in_common(self):
        all_ctypes = self._API_DM.ctypes
        common_upstreams = [el.meta.id for el in self.upstream_manager.list_upstreams("common")]
        return set(all_ctypes).intersection(set(common_upstreams))

    def set_upstreams_for_service(self, service, ctype, timeout_from_sla=False):
        self.LOGGER.info("Setting upstreams for service {} in {}".format(service, ctype))

        if ctype not in self.represented_ctypes_in_common:
            self.LOGGER.warning("Upstream can't be created due to selected ctype not presented in common balancer")
        else:
            self.upstream_manager.set_common_upstream_for_service(service, ctype, timeout_from_sla=timeout_from_sla)

        if self.is_namespace_exist(ctype):
            self.upstream_manager.set_separated_upstream_for_service(service, ctype, timeout_from_sla=timeout_from_sla)

    def label_for_new_upstream(self, ctype):
        arr = [int(el.spec.labels['order'][-5:]) for el in self.upstream_manager.list_upstreams(ctype) if not int(el.spec.labels['order'])//10000]
        if arr:
            return str(max(arr)+1).zfill(8)
        else:
            return str(1).zfill(8)

    def create_ferryman_upstream(self, ferryman_nanny, ferryman_dc):
        ferryman_backend = {"dc": ferryman_dc, "name": ferryman_nanny}

        self.upstream_manager.create_upstream("ferryman", backends=[ferryman_backend, ], ferryman_name=ferryman_nanny, connect_timeout='100ms',
                                              backend_timeout="40s", balancer_attempts_num=2, retry_responses=False, upstream_type="ferryman",
                                              order_label=self.label_for_new_upstream("ferryman"))

    def create_ferryman_backend(self, ferryman_nanny, ferryman_dc, endpoint_set_id):
        self.backend_manager.create_backend(backend_id=ferryman_nanny, ctype="ferryman", service_id=ferryman_nanny, dc=ferryman_dc, endpoint_set_id=endpoint_set_id)

    def clear_service_upstreams(self, ctype, service):
        if self.upstream_manager.get_upstream("common", "{}-{}".format(ctype, service)):
            self.upstream_manager.remove_upstream("common", "{}-{}".format(ctype, service))
        if self.upstream_manager.get_upstream(ctype, service):
            self.upstream_manager.remove_upstream(ctype, service)

    def set_up_l3_balancer(self, ctype, l3_balancer_id='', **kwargs):
        network_macro = BundleNetworks().create_balancer_network_for_bundle(ctype.upper())
        balancers = [balancer.meta.id for balancer in
                     self.balancer_manager.list_balancers(ctype).balancers]
        return self.l3_balancer_manager.create_l3_balancer(ctype, helpers.namespace_id_from_ctype(ctype), balancers, l3_balancer_id, network_macro, **kwargs)

    def set_l3_balancer(self, ctype, l3_balancer_id):
        clusters = [balancer.meta.location.yp_cluster for balancer in self.balancer_manager.list_balancers(ctype).balancers]
        balancers = [balancer.spec.config_transport.nanny_static_file.service_id for balancer in self.balancer_manager.list_balancers(ctype).balancers]
        res = []
        for cluster, balancer_id in zip(clusters, balancers):
            res.append(self.l3_balancer_manager.reallocate_pods(balancer_id, cluster, l3_balancer_id))
        return res

    def update_namespace(self, ctype):
        req_pb = api_pb2.UpdateNamespaceRequest()
        req_pb.meta.id = self.get_namespace(ctype).namespace.meta.id
        req_pb.meta.abc_service_id = self.get_namespace(ctype).namespace.meta.abc_service_id
        req_pb.order.content.backends = self.get_namespace(ctype).namespace.order.content.backends
        self._CLIENT.update_namespace(req_pb)

    def get_proxies(self, ctype):
        return self._API_DM.get_proxy_endpoint_sets(ctype)

    def get_namespace(self, ctype):
        namespace_id = helpers.namespace_id_from_ctype(ctype)
        req_pb = api_pb2.GetNamespaceRequest()
        req_pb.id = namespace_id
        return self._CLIENT.get_namespace(req_pb)

    def order_https_certificate(self, ctype):
        return self.certificate_manager.order_https_certificate(helpers.namespace_id_from_ctype(ctype))

    def set_its_ruchka(self, ctype):
        self.its_manager.commit_its_config(helpers.namespace_id_from_ctype(ctype))
        self.its_manager.create_its_config(id=helpers.namespace_id_from_ctype(ctype))
        section_ids = set([upstream.spec.yandex_balancer.config.l7_upstream_macro.by_dc_scheme.dc_balancer.weights_section_id for upstream in self.upstream_manager.list_upstreams(ctype)])
        for section in section_ids:
            self.its_manager.create_section(helpers.namespace_id_from_ctype(ctype), section)
            sections = self.get_its_sections(ctype)
            current_section = sections['sections'][section]
            for location in current_section['locations']:
                current_section['locations'][location]['weight'] = current_section['locations'][location]['default_weight']
            self.its_manager.set_weights(helpers.namespace_id_from_ctype(ctype), sections)

    def get_its_sections(self, ctype):
        return self.its_manager.sections_weights(helpers.namespace_id_from_ctype(ctype))

    def get_certificate(self, ctype, certificate_id):
        return self.certificate_manager.get_certificate(helpers.namespace_id_from_ctype(ctype), certificate_id)

    def wait_for_active_state(self, ctype):
        """
            Wait for all processes on balancers are finished.
            :return:
        """
        self.LOGGER.info("Waiting for active balancers state")
        time.sleep(60)
        balancers = self.balancer_manager.list_balancers(ctype).balancers
        while "True" in [el.status.in_progress.status for el in balancers]:
            time.sleep(60)
            balancers = self.balancer_manager.list_balancers(ctype).balancers
