#!/usr/bin/env python
# -*- coding: utf-8 -*-

import argparse
import logging
import requests
import statistics
from copy import deepcopy

from saas.tools.ssm.modules.abc_api import ABCApi
from saas.tools.ssm.modules.dm_api import DeployManager
from saas.tools.ssm.modules.nanny_api import NannyServices
from saas.tools.ssm.modules.nanny_yp_api import SaaSNannyYpWorkflow
import saas.tools.ssm.ssm_quota_management as ssm_quota_management

from saas.tools.abcd.api.external.saas_usage import SaaSUsageConnector

from requests.packages.urllib3.exceptions import InsecureRequestWarning

requests.packages.urllib3.disable_warnings(InsecureRequestWarning)


def prepare_logging(options):
    """
    Prepare logging settings
    :param options: argparse.Namespace
    """
    logger = logging.getLogger()
    handlers = [logging.StreamHandler()]
    if options.debug:
        logger.setLevel(logging.DEBUG)
    else:
        logger.setLevel(logging.INFO)

    if options.logfile:
        handlers.append(logging.FileHandler(options.logfile))
    formatter = logging.Formatter('[%(asctime)s SQS [%(process)d] [%(levelname)s] %(message)s',
                                  datefmt='%Y-%m-%d %H:%M:%S')
    for handler in handlers:
        handler.setFormatter(formatter)
        logger.addHandler(handler)


def sum_resource_dicts(dst, src):
    """
    Dict format {'LOC': { 'CPU': ..., 'RAM': ..., 'HDD': ..., 'SSD': ...}}
    """
    if not dst:
        dst.update(src)
        return
    for loc, usage in src.items():
        if loc not in dst:
            dst[loc] = {}
        for res_type, value in usage.items():
            if res_type in dst[loc]:
                dst[loc][res_type] += value
            else:
                dst[loc][res_type] = value


def collect_service_resources(service_name, service_ctype):
    """
    Main function for collect service resources usage
    :param service_name: type str
    :param service_ctype: type str
    :return: type dict
    """
    logging.info('[{}] Processing service: {}'.format(service_ctype, service_name))
    # Clients
    dm_cl = DeployManager(service_name, service_ctype, use_proxy=False)
    # ds_cl = SaaSDispenserAPI(service_name, cluster_type='prod')
    abc_cl = ABCApi()
    tags_info = dm_cl.get_tags_info()
    if 'nanny_services' not in tags_info.keys():
        logging.critical('Nanny services was not found for service %s ctype %s', service_name, service_ctype)
        return

    nanny_services = [service.split('@')[0] for service in tags_info['nanny_services']]

    # SLA info
    sla_info = dm_cl._get_sla_info().json()
    service_abc = sla_info.get('abc_user_service', '')
    quota_abc = sla_info.get('abc_quota_service', '')
    service_abc_name, service_abc_hr_name = abc_cl.get_hr_service_name(service_abc, lang='en') if service_abc else ('', '')
    quota_abc_name, quota_abc_hr_name = abc_cl.get_hr_service_name(quota_abc, lang='en') if quota_abc else ('', '')

    # Collect resources usage
    resources = {
        'service_name': service_name,
        'service_ctype': service_ctype,
        'service_owner': sla_info.get('owners', ['i024'])[0].split('@')[0],
        'service_abc': {
            'id': service_abc,
            'name': service_abc_name,
            'hr_name': service_abc_hr_name
        },
        'quota_abc': {
            'id': quota_abc,
            'name': quota_abc_name,
            'hr_name': quota_abc_hr_name
        },
        'usage': {},
        'usage_per_nanny': [],
        'nanny_services' : [],
    }
    for nanny_service in nanny_services:
        n_cl = NannyServices(nanny_service, config='')
        if not n_cl.check_service_exists():
            logging.critical('Service %s does not exists. It could do wrong calculation result.', nanny_service)
            return
        if nanny_service == 'saas_yp_cloud_prestable':
            logging.warning('Found service %s, ctype: %s with common prestable group saas_yp_cloud_prestable.', service_name, service_ctype)
            return
        n_yp_cl = SaaSNannyYpWorkflow(nanny_service, timeout=333)
        resources['usage_per_nanny'].append(n_yp_cl.calc_service_resources())
        sum_resource_dicts(resources['usage'], resources['usage_per_nanny'][-1])
        resources['nanny_services'].append(nanny_service)
    return resources


def ctype_services_data(dm_client, ctype):
    """
    Collect ctype service type
    :param dm_client: type saas.tools.ssm.modules.dm_api.DeployManager
    :param ctype: type str
    :return: type list
    """
    MISC_SERVICES = ['searchproxy']
    ABC_ASPECTS = ['service_abc', 'quota_abc']
    result = []
    logging.info(f'Processing CTYPE: {ctype}')
    for service in list(dm_client.get_services(ctype)) + MISC_SERVICES:
        service_resources = collect_service_resources(service, ctype)
        if service_resources:
            result.append(service_resources)
    abc_defaults = {}
    if ctype in ssm_quota_management.SAAS_CTYPES:
        logging.info(f'CTYPE {ctype} considered communal')
    else:
        quota_ids = sorted(filter(None, [res['quota_abc']['id'] for res in result]))
        if quota_ids:
            default_quota_id = statistics.mode(quota_ids)
            logging.info(f'Found default QUOTA ID ({default_quota_id}) for CTYPE: {ctype}')
            for aspect in ABC_ASPECTS:
                abc_defaults[aspect] = next(res[aspect] for res in result
                                            if res['quota_abc']['id'] == default_quota_id and res[aspect]['id'])
        else:
            logging.warning(f'No valid QUOTA ID found for CTYPE: {ctype}')
    for res in result:
        name = res['service_name']
        is_saas_accounted = (ctype == 'testing') or (name in MISC_SERVICES and not abc_defaults)
        is_misc_accounted = name in MISC_SERVICES and abc_defaults and not is_saas_accounted
        if is_saas_accounted:
            logging.info(f'SERVICE {name} in CTYPE {ctype} considered SAAS-accounted')
        elif is_misc_accounted:
            logging.info(f'SERVICE {name} in CTYPE {ctype} considered MISC-accounted')
        for aspect in ABC_ASPECTS:
            if is_misc_accounted:
                res[aspect] = abc_defaults[aspect]
            elif is_saas_accounted or not res[aspect]['id']:
                res[aspect]['id'] = 664
                res[aspect]['name'] = 'saas'
                res[aspect]['hr_name'] = 'SaaS - Search as a Service (DUTY)'
            res[aspect]['id'] = int(res[aspect]['id'])
    return result


def parse_args(*args):
    parser = argparse.ArgumentParser(formatter_class=argparse.RawTextHelpFormatter,
                                     prog="Tool for collect and sync SaaS services usage",
                                     description="""
DESCRIPTION
Get information about SaaS services resource usage. Sync with yt table
Optional arguments: --service, --ctype,  --all, --sync, --read, --logfile, --debug
Requirements:
YT token: https://docs.yandex-team.ru/yt/description/common/auth#using_token
Dispenser token: DISPENSER_TOKEN env var. Could be get from https://oauth.yandex-team.ru/authorize?response_type=token&client_id=fe21371385fa4ac8a24c2e9c318e0561
""")
    parser.add_argument('--service', dest='service')
    parser.add_argument('--ctype', dest='ctype', choices=['stable', 'stable_patents', 'stable_kv', 'stable_middle_kv', 'stable_hamster', 'stable_dj', 'prestable_dj'])
    parser.add_argument('--all', dest='all_services', action='store_true')
    parser.add_argument('--read', dest='read_from_table', action='store_true')
    parser.add_argument('--sync', dest='sync_to_table', action='store_true')
    parser.add_argument('--print', dest='print_result', action='store_true')
    parser.add_argument('--logfile', dest='logfile')
    parser.add_argument('--debug', dest='debug', action='store_true', default=False)

    if args:
        options = parser.parse_args(args)
    else:
        options = parser.parse_args()
    return options


def write_yt_table(rusage):
    written_data = [wd for wd in deepcopy(rusage) if wd['service_ctype'] != 'testing']
    for wd in written_data:
        wd.pop('usage_per_nanny', None)
        wd.pop('nanny_services', None)
    ssm_quota_management.yt_write_table(written_data, ssm_quota_management.YT_USAGE_TABLE_PATH)


def main(*args):
    # Options
    options = parse_args(*args)
    # Logging
    prepare_logging(options)
    dm_cl = DeployManager('', '')
    result = []
    # Basic dm client
    if options.service and options.ctype:
        logging.info('COLLECT INFORMATION ABOUT SERVICE {} CTYPE {}'.format(options.service, options.ctype))
        service_resources = collect_service_resources(options.service, options.ctype)
        if service_resources:
            result.append(service_resources)
    elif options.ctype and options.all_services:
        logging.info('COLLECT INFORMATION ABOUT ALL SERVICES IN CTYPE {}'.format(options.ctype))
        result.extend(ctype_services_data(dm_cl, options.ctype))
    elif options.all_services or options.sync_to_table:
        logging.info('COLLECT INFORMATION ABOUT ALL SERVICES IN ALL CTYPES')
        services_data = dm_cl._get_services().json().keys()
        for ctype in services_data:
            if ctype in {'unused', 'stable_gemini'}:
                continue
            result.extend(ctype_services_data(dm_cl, ctype))
        if options.sync_to_table:
            logging.info('SYNC DATA WITH ABCD DATABASE AND YT_TABLE {}'.format(ssm_quota_management.YT_USAGE_TABLE_PATH))
            SaaSUsageConnector.set_rusage(result)
            write_yt_table(result)
    elif options.read_from_table:
        logging.info('READ DATA FROM YT TABLE {}'.format(ssm_quota_management.YT_USAGE_TABLE_PATH))
        result = ssm_quota_management.yt_get_table()
    if options.print_result:
        logging.info(result)


if __name__ == '__main__':
    main()
