# coding: utf-8

"""
Represent complex SaaS entity including Nanny service, service in DM and so on
"""

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

from typing import List, AnyStr, Iterable, Callable  # noqa
import saas.tools.devops.lib23.saas_service as s_service  # noqa

import logging
import argparse

import saas.tools.devops.lib23.deploy_manager_api as dm_api
import saas.tools.devops.lib23.nanny_helpers as nanny_helpers

LOGGER = logging.getLogger(__name__)


class SaaSEntity(object):

    dm = dm_api.DeployManagerApiClient()

    def __init__(self, ctype=None, saas_service=None, nanny_service=None, gencfg_groups=None):

        self._saas_ctype = ctype
        self._saas_service = saas_service
        self._nanny_service = nanny_service
        self._gencfg_groups = gencfg_groups
        self._hosts = None
        self._slots = None

        # Can be optimized
        # self._entities = None
        # self._ambiguous = True

    def filter_iterate(self, saas_ctypes=None, saas_services=None, nanny_services=None):

        logging.debug("Iterating SaaS with saas_ctypes=%s, saas_services=%s, nanny_services=%s", saas_ctypes, saas_services, nanny_services)
        matching_entities = []

        if saas_ctypes is None:
            saas_ctypes = self.dm.stable_ctypes

        # print saas_ctypes

        for ctype in saas_ctypes:

            all_saas_services = set(self.dm.get_services(ctype))
            if saas_services is None:
                _saas_services = all_saas_services
            else:
                _saas_services = [s for s in saas_services if s.name in all_saas_services]

            for saas_service in _saas_services:
                service_nanny_services = self.dm.get_nanny_services(ctype, saas_service)

                if nanny_services is None:
                    _nanny_services = service_nanny_services
                else:
                    _nanny_services = [s for s in nanny_services if s in service_nanny_services]

                for nanny_service in _nanny_services:
                    # TODO : make gencfg iteration, host iteration and slot iteration

                    if (self._nanny_service is None or self._nanny_service == nanny_service) and \
                       (self._saas_service is None or self._saas_service == saas_service.name) and \
                       (self._saas_ctype is None or self._saas_ctype == ctype):

                        matching_entities.append({
                            'saas_ctype': ctype,
                            'saas_service': saas_service,
                            'nanny_service': nanny_service
                            })

        return matching_entities

    def get_entities(self):

        entities = self.filter_iterate(
            saas_ctypes=[self._saas_ctype] if self._saas_ctype else None,
            saas_services=[self._saas_service] if self._saas_service else None,
            nanny_services=[self._nanny_service] if self._nanny_service else None,
            )
        return entities

    def get_entity(self):

        entities = self.get_entities()
        assert len(entities) > 0, "No matching SaaS entities found"
        assert len(entities) == 1, "Can't exactly determine SaaS entity"
        return entities[0]

    def set_saas_ctype(self, saas_ctype):
        self._saas_ctype = saas_ctype

    def set_saas_service(self, saas_service):
        self._saas_service = saas_service

    def set_nanny_service(self, nanny_service):
        self._nanny_service = nanny_service

    def set_gencfg_group(self, gencfg_group):
        pass

    def set_host(self):
        pass

    def set_slot(self):
        pass


def guess_nanny_service_by_saas_service(saas_service, saas_ctypes=None):
    saas_ctypes = saas_ctypes if saas_ctypes else dm_api.DeployManagerApiClient().stable_ctypes
    e = SaaSEntity()
    matching_entities = e.filter_iterate(saas_services=[saas_service], saas_ctypes=saas_ctypes)
    assert len(matching_entities) == 1, \
        "Can't unambigously guess service {} in ctypes {}, found {} matches".format(saas_service, saas_ctypes, len(matching_entities))
    return matching_entities[0]['nanny_service']


def saas_service_iterator(saas_ctypes=None, saas_services=None, nanny_services=None, gencfg_groups=None, extra_filter=lambda s: True):
    # type: (List[AnyStr], List[AnyStr], List[AnyStr], List[AnyStr], Callable) -> Iterable[s_service.SaasService]
    """
    :param saas_ctypes: List of saas ctypes ex: ['stable', 'stable_kv']
    :param saas_services: List os saas service names, ex: ['sovetnik_filter', 'turbo_ecommerce']
    :param nanny_services: List of nanny service names, ex: ['saas_cloud_turbo_ecommerce', ]
    :param gencfg_groups: List of gencfg group names
    :param extra_filter: Extra filter lambda(SaasService) -> bool
    """
    dm = dm_api.DeployManagerApiClient()
    ctypes = saas_ctypes if saas_ctypes else dm.stable_ctypes
    nanny_services = set([nanny_helpers.NannyService(nanny_service) for nanny_service in nanny_services]) if nanny_services else None
    gencfg_groups = set(gencfg_groups) if gencfg_groups else None
    LOGGER.info('Iterating over saas services, filters: ctype: %s, service: %s, nanny_service: %s, gencfg: %s',
                ctypes, saas_services, nanny_services, gencfg_groups)

    for ctype in ctypes:
        def filter_function(s):
            return (saas_services and s.name in saas_services) or \
                   (nanny_services and nanny_services.intersection(s.nanny_services)) or \
                   (gencfg_groups and gencfg_groups.intersection(s.gencfg_groups_names))

        ctype_services = dm.get_services(ctype)
        no_filters = not (saas_services or nanny_services or gencfg_groups)
        for saas_service in ctype_services:
            if (no_filters or filter_function(saas_service)) and extra_filter(saas_service):
                yield saas_service
            else:
                continue


def get_unique_service(saas_ctype=None, saas_service_name=None, nanny_service=None, gencfg_group=None):
    # type: (AnyStr, AnyStr, AnyStr, AnyStr) -> s_service.SaasService
    saas_ctypes = {saas_ctype, } if saas_ctype else None
    saas_services = [saas_service_name, ] if saas_service_name else None
    nanny_services = [nanny_service, ] if nanny_service else None
    gencfg_groups = [gencfg_group, ] if gencfg_group else None
    matching_services = list(saas_service_iterator(saas_ctypes, saas_services, nanny_services, gencfg_groups))
    if len(matching_services) > 1:
        LOGGER.error('More than one saas service match with filters: %s', matching_services)
        raise RuntimeError('Too many services')
    elif not matching_services:
        LOGGER.error(
            'No services matched filters: ctype: %s, service: %s, nanny_service: %s, gencfg: %s', saas_ctype, saas_service_name, nanny_service, gencfg_group
        )
        raise RuntimeError('No services')

    return matching_services[0]


def get_saas_entity_arg_parser():
    parser = argparse.ArgumentParser(add_help=False)
    arg_group = parser.add_argument_group('saas entity')
    arg_group.add_argument('--saas-ctype',    help='SaaS ctype', choices=dm_api.DeployManagerApiClient().ctypes)
    arg_group.add_argument('--saas-service',  help='SaaS service name')
    arg_group.add_argument('--nanny-service', help='Match saas service by nanny service')
    arg_group.add_argument('--gencfg-group',  help='Match saas service by gencfg group')
    return parser


def get_saas_service_from_args(args):
    saas_ctype = args.saas_ctype or None
    saas_service = args.saas_service or None
    nanny_service = args.nanny_service or None
    gencfg_group = args.gencfg_group or None
    if not any([saas_ctype, saas_service, nanny_service, gencfg_group]):
        raise ValueError('Set at least one filter to determine service')
    return get_unique_service(saas_ctype, saas_service, nanny_service, gencfg_group)
