#!/usr/bin/python

import logging
import requests
import json
import argparse
import random
import subprocess

import search.tools.devops.libs.utils as u
import search.tools.devops.libs.nanny_services as ns

# from saas.tools.devops.saas_service_check.checks.check_if_topology_bump_is_safe import check_if_topology_bump_is_safe

from saas.tools.devops.saas_service_check.checks.service_overriden import service_overriden
from saas.tools.devops.saas_service_check.checks.topology_good import topology_good
from saas.tools.devops.saas_service_check.checks.topology_outdated import topology_outdated

from saas.tools.devops.saas_service_check.actions.update_owners import update_owners
from saas.tools.devops.saas_service_check.actions.fix_zero_power import fix_zero_power
from saas.tools.devops.saas_service_check.actions.safe_up_topology import safe_up_topology

from time import localtime
from statface_client import StatfaceClient, STATFACE_PRODUCTION

SAMPLE_SIZE = 3


# TBD : export it to nanny lib
def get_current_topology(service):
    response = ns.get_service_runtime_attrs(service)
    topology = None
    if response.ok:
        try:
            topology = response.json()["content"]["instances"]["extended_gencfg_groups"]["groups"]
        except:
            logging.error('Nanny responce is not like valid JSON')
    return topology


# TBD : export it to nanny lib
def get_nanny_limits_override(nanny_service):
    logging.debug("analyzing service {} for overrides".format(nanny_service))
    topology = get_current_topology(nanny_service)
    assert topology is not None, 'Failed to retrieve topology for {} or topology is empty'.format(nanny_service)
    logging.debug("Toopology for service %s is : %s", nanny_service, topology)

    all_limits_override = {}
    for x in topology:
        if 'limits' in x:
            limits_override = {limit: x['limits'][limit] for limit in x['limits'] if limit in ['cpu_guarantee', 'cpu_limit', 'memory_guarantee', 'memory_limit']}
            if len(limits_override) > 0:
                logging.debug('Limits are overriden for service %s, group %s!', nanny_service, x['name'])
                all_limits_override[x['name']] = limits_override

    return all_limits_override


def parse_cmd_args():

    description = '''
    TBD : write a good text here
    '''

    parser = argparse.ArgumentParser(description=description)

    parser.add_argument(
        '-d', '--debug',
        default=False,
        action='store_true',
        help='Dump debug info'
    )

    parser.add_argument(
        '--print-saas-services',
        default=False,
        action='store_true',
        help='Print saas services hierarchy (ctypes + services + related nanny services)'
    )

    parser.add_argument(
        '--upload-to-stat',
        default=False,
        action='store_true',
        help='Upload data to stat'
    )

    parser.add_argument(
        '--sample',
        default=False,
        const=SAMPLE_SIZE,
        type=int,
        nargs='?',
        help='Analyze sample of N services per ctype instead of all SaaS Cloud. Defauln N is {}'.format(SAMPLE_SIZE)
    )

    parser.add_argument(
        '--set-itag',
        default=False,
        action='store_true',
        help='set itag to hardcoded set of services'
    )

    parser.add_argument(
        '--shuffle',
        default=False,
        action='store_true',
        help='Shuffle list of services to operate'
    )

    parser.add_argument(
        '--checks',
        nargs="*",
        default=[],
        help='List of checks that can be applied to SaaS services. Possible values : {}'.format(['all', 'topology_outdated', 'topology_not_good', 'nanny_overrides', ])
    )

    parser.add_argument(
        '--actions',
        nargs="*",
        default=[],
        help='List of actions that can be applied to SaaS services. Possible values : {}'.format(['fix_zero_power', 'safe_up_topology'])
    )

    parser.add_argument(
        '--include-proxies',
        default=False,
        action='store_true',
        help='Also iterate proxy services'
    )

    parser.add_argument(
        '--include-staging',
        default=False,
        action='store_true',
        help='Include testing and prestable services'
    )

    parser.add_argument(
        '--stat',
        default=False,
        action='store_true',
        help='Collect resource allocation statistics'
    )

    return parser.parse_args()


def main():

    # check_log = open("check_log", "w")
    action_log = open("action_log", "w")

    args = parse_cmd_args()
    if args.debug:
        u.logger_setup(verbose_level=2)
        logging.info("Config : {}".format(str(args)))
    else:
        u.logger_setup(verbose_level=1)

    checks = ["service_overriden", "topology_outdated", "topology_good"]
    report = {x: {} for x in checks}
    stat = {x: {} for x in ['SAS', 'MAN', 'VLA']}

    services = []

    allowed_ctypes = [
        "stable",
        "stable_kv",
        "stable_middle_kv",
    ]

    if args.include_staging:
        allowed_ctypes.append('testing')
        allowed_ctypes.append('prestable')

    ctypes = [ctype for ctype in requests.get("http://saas-dm.yandex.net/ctypes").json() if ctype in allowed_ctypes]
    if args.shuffle:
        random.shuffle(ctypes)

    all_saas_services = requests.get("http://saas-dm.yandex.net/get_services").json()

    script = ""

    for ctype in ctypes:

        saas_services = all_saas_services[ctype]['rtyserver'].keys()
        if args.include_proxies:
            saas_services = saas_services + all_saas_services[ctype]['searchproxy'].keys() + all_saas_services[ctype]['indexerproxy'].keys()

        if args.sample:
            print args.sample
            random.shuffle(saas_services)
            saas_services = saas_services[:args.sample]
            logging.error("Using sample mode. Will process only {} in ctype {}".format(saas_services, ctype))

        if args.shuffle:
            random.shuffle(saas_services)

        if args.set_itag:
            no_indexing_services = [
                "news-personal",
                "ymediapers_kv",
                "market-backoffice-1",
                "market-backoffice-2",
                "iznanka-embeddings",
                "ppl_images",
                "cv-ontodb-images",
                "images_duck",
                "images_duck_priemka",
                "video_doc_recom",
                "zen_doc",
                "mobile-application-kv"
            ]
            trunked_saas_services = [service for service in saas_services if service in no_indexing_services]
            saas_services = trunked_saas_services

        for saas_service in saas_services:

            slot_info = requests.get(
                'http://saas-dm.yandex.net/api/slots_and_stat/:rtyserver?&ctype={}&service={}&filter=result.slot.properties.NANNY_SERVICE_ID'.format(
                    ctype,
                    saas_service
                ),
                timeout=600
            )
            try:
                slots = slot_info.json()['slots']

            except:
                logging.error("Can't get slot info about %s/%s", ctype, saas_service)

            if len(slots) > 0:

                logging.info("Service discovered %s", {'ctype': ctype, 'service': saas_service})

                nanny_services = []
                for slot_stack in slots:
                    for slot in slot_stack['slots']:
                        nanny_service = slot.get('result.slot.properties.NANNY_SERVICE_ID', None)
                        if nanny_service not in nanny_services:
                            nanny_services.append(nanny_service)

                if None in nanny_services:
                    logging.debug("Service %s/%s has down slots or slots without nanny affinity", ctype, saas_service)

                if nanny_services == [] or nanny_services == [None]:
                    logging.error("Could not retrieve nanny services in %s/%s", ctype, saas_service)

                service_description = {
                    "ctype": ctype,
                    "saas_service": saas_service,
                    "nanny_services": nanny_services,
                    "topology": {nanny_service: get_current_topology(nanny_service) for nanny_service in nanny_services if nanny_service is not None}
                }

                logging.info("Service info retrieved : %s", service_description)

                services.append(service_description)

                ###
                #
                # Good place fof saas service aware logic
                #
                ###

                for nanny_service in nanny_services:
                    if nanny_service is not None:

                        ###
                        #
                        # Good place for nanny_service aware logic
                        #
                        ###

                        if 'nanny_overrides' in args.checks:
                            logging.info("checking service {} for nanny overrides".format(nanny_service))
                            override_check_result = service_overriden(nanny_service)
                            if override_check_result[0]:
                                report['service_overriden'][nanny_service] = override_check_result[1]

                        if '???' in args.checks:
                            if args.set_itag:
                                script += "./saas-update-tags --service {} --saas-ctype {} --saas-service {} --mark-saas-no-indexing\n".format(nanny_service, ctype, saas_service)

                        if 'safe_up_topology' in args.actions:
                            logging.info("Safe up topology for service {}".format(nanny_service))
                            used_groups = [simple_topology['name'] for simple_topology in service_description["topology"][nanny_service]]
                            if safe_up_topology(nanny_service, used_groups, new_topology='stable-113-r164') is not None:
                                logging.info('New snapshot for service {} commited'.format(nanny_service))
                            else:
                                logging.info('Service {} not updated'.format(nanny_service))

                        for simple_topology in service_description["topology"][nanny_service]:
                            group = simple_topology['name']
                            if simple_topology['release'] == 'trunk':
                                tag = 'trunk'
                            else:
                                tag = simple_topology['release'].split('/')[1]

                            ###
                            #
                            # Good place for gencfg_group aware logic
                            #
                            ###

                            if 'topology_outdated' in args.checks:
                                logging.info("checking group {} for outdate".format(group))
                                topology_outdated_check_result = topology_outdated(group, old_topology=tag)
                                if topology_outdated_check_result[0]:
                                    report['topology_outdated'][group] = topology_outdated_check_result[1]

                            if 'topology_not_good' in args.checks:
                                logging.info("checking group {} for stupid mistakes".format(group))
                                topology_good_check_result = topology_good(group)
                                if not topology_good_check_result[0]:
                                    report['topology_good'][group] = topology_good_check_result[1]
                                    update_owners(group)

                            if 'fix_zero_power' in args.actions:
                                logging.info("fixing zero power for group {}".format(group))
                                fix_zero_power(group, generilka_path="/home/i024/GENERILKA", do_pre_check=True, check_suggested_power=True)

                            if "SAS_SAAS_CLOUD" not in group and "VLA_SAAS_CLOUD" not in group and "MAN_SAAS_CLOUD" not in group:
                                action_log.write("{}\n".format(group))
                                action_log.flush()

                            if args.stat:
                                if group[:3] in ['SAS', 'MAN', 'VLA']:
                                    geo = group[:3]

                                    get_power_script = "./utils/common/show_power.py -g {} -t json".format(group)
                                    logging.debug(get_power_script)
                                    get_power = subprocess.Popen(
                                        get_power_script,
                                        shell=True,
                                        cwd="/home/i024/GENERILKA",
                                        stdout=subprocess.PIPE
                                    )
                                    power = json.loads("".join(get_power.stdout.readlines()))
                                    cpu_guarantee = float(power[group]['total']['instances_cpu_avg'])
                                    mem_guarantee = float(power[group]['total']['instances_memory_avg'])
                                    stat[geo][group] = {
                                        'cpu_guarantee': cpu_guarantee,
                                        'mem_guarantee': mem_guarantee,
                                        'cpu_per_mem_ratio': cpu_guarantee / mem_guarantee / 40,
                                        'instances_count': int(power[group]['total']['instances_memory_avg']),
                                    }
                                else:
                                    logging.error("Unconventional gencfg group naming : %s", group)

    if args.print_saas_services:
        print json.dumps(services, indent=2)

    else:
        print json.dumps(report, indent=2)

    print json.dumps(stat, indent=2)

    if args.upload_to_stat:

        statface_client = StatfaceClient(host=STATFACE_PRODUCTION)
        statface_report = statface_client.get_report('SAAS/resources/topology_problems')

        local_date = localtime()
        year = local_date.tm_year
        month = local_date.tm_mon
        day = local_date.tm_mday

        stat_data = []

        stat_data.append({
            "fielddate": "{}-{}-{}".format(year, month, day),
            "geo": "ALL",
            "service_overriden": len(report['service_overriden']),
            'topology_outdated': len(report['topology_outdated']),
            'topology_not_good': len(report['topology_good']),
            'total_services': len(services)
        })

        statface_report.upload_data(scale='daily', data=stat_data)

    if args.set_itag:
        print script

if __name__ == '__main__':

    main()
