#!/usr/bin/env python

"""Get netmon data and print it out."""
from __future__ import unicode_literals, print_function

# this one have to go first because it imports gevent.
from walle.main import load_config

import argparse
import json
from collections import OrderedDict, Counter

from sepelib.core import config

from walle.clients import netmon
from walle.expert.constants import NETMON_CONNECTIVITY_LEVEL, NETMON_CONNECTIVITY_LEVEL_DC, \
    NETMON_CONNECTIVITY_WINDOW_DC, NETMON_CONNECTIVITY_WINDOW_QUEUE, NETMON_CONNECTIVITY_LEVEL_QUEUE, \
    NETMON_CONNECTIVITY_LEVEL_SWITCH, NETMON_CONNECTIVITY_WINDOW_SWITCH
from walle.expert.netmon import make_check_generator
from walle.util.setup import setup_logging


def _parse_args():
    usage = """
        $ %(prog)s [-c <config>]
        $ %(prog)s [-c <config>] -s man1-4s83 -p --level switch
        $ %(prog)s [-c <config>] -s man1-4s83 -p --scores
        $ %(prog)s [-c <config>] -s man1-4s83 -p --scores --cross-dc-probes
    """

    parser = argparse.ArgumentParser(description="Show data that Wall-E fetches from Netmon.", usage=usage)
    parser.add_argument("-c", "--config", required=False, help="configuration file path")
    parser.add_argument("-d", "--debug", default=False, action="store_true", help="debug mode")

    parser.add_argument("-s", "--switch", help="Show data for a switch")
    parser.add_argument("--probes", action="store_true", help="Also dump list of probes for the switch")
    parser.add_argument("--scores", action="store_true", help="Count scores")

    known_levels = [NETMON_CONNECTIVITY_LEVEL_DC, NETMON_CONNECTIVITY_LEVEL_QUEUE, NETMON_CONNECTIVITY_LEVEL_SWITCH]
    default_level = NETMON_CONNECTIVITY_LEVEL
    parser.add_argument("--level", default=default_level, choices=known_levels, help="Fetch data for network level")
    parser.add_argument("--cross-dc-probes", dest="local", action="store_false", help="Include cross-dc probes")
    parser.add_argument("--source-switch", default=None, help="Include only probes from given switch")
    parser.add_argument("--max-score", default=1.0, type=float,
                        help="Include only probes with score not higher then given value")

    config.augment_args_parser(parser)
    return parser.parse_args()


def get_switch_metrics(alive_data, seen_hosts_data, switch_name):
    queue_metrics, dc_metrics, walle_check = None, None, None
    switch_metrics = find_one(alive_data["switches"], "name", switch_name)

    if switch_metrics:
        queue_metrics = find_one(alive_data["queues"], "name", switch_metrics["queue"])
        dc_metrics = find_one(alive_data["datacenters"], "name", switch_metrics["dc"])

        queue_metrics = append_seen_hosts([queue_metrics], seen_hosts_data["queue"])
        dc_metrics = append_seen_hosts([dc_metrics], seen_hosts_data["dc"])

        walle_check = get_walle_check(switch_metrics, alive_data, seen_hosts_data)
        # we are debugging here, we need readable data
        walle_check["metadata"] = json.loads(walle_check["metadata"])

    return switch_metrics, queue_metrics, dc_metrics, walle_check


def get_probes(switch_metrics, level, local=True, source_switch=None, max_score=1.0):
    window = {
        NETMON_CONNECTIVITY_LEVEL_DC: NETMON_CONNECTIVITY_WINDOW_DC,
        NETMON_CONNECTIVITY_LEVEL_QUEUE: NETMON_CONNECTIVITY_WINDOW_QUEUE,
        NETMON_CONNECTIVITY_LEVEL_SWITCH: NETMON_CONNECTIVITY_WINDOW_SWITCH,
    }[level]

    probes = netmon.get_probes(
        level=level, local=local,
        window=window, max_score=max_score,
        switch_name=switch_metrics["name"],
        switch_queue=switch_metrics["queue"],
        switch_dc=switch_metrics["dc"],
    )
    cleanup_probes(probes)

    if source_switch:
        probes["probes"] = [probe for probe in probes["probes"] if probe["source_switch"] == source_switch]

    return probes


def count_scores(probes):
    counter = Counter(probe["score"] for probe in probes["probes"])
    counter["total"] = sum(counter.values())

    return counter


def cleanup_probes(probes):
    for probe in probes["probes"]:
        del probe["target_queue"]
        del probe["source_queue"]
        del probe["target_switch"]
        del probe["source_dc"]
        del probe["target_dc"]
        del probe["source_port"]
        del probe["target_port"]
        del probe["target_address"]
        del probe["source_address"]


def get_walle_check(switch, alive_data, seen_hosts_data):
    check_generator = make_check_generator(alive_data, seen_hosts_data)
    return check_generator.make_check(switch)


def find_one(iterable, key, value):
    return next((item for item in iterable if item[key] == value), None)


def add_seen_hosts_percent(node):
    percent = "{:0.2f}%".format(node["seen"] * 100.0 / node["total"])
    return dict(node, alive_hosts=percent)


def append_seen_hosts(alive_data, seen_hosts_data):
    seen_hosts_index = {node["name"]: add_seen_hosts_percent(node) for node in seen_hosts_data}
    missing = {"seen": None, "total": None}

    for node in alive_data:
        node.update(seen_hosts_index.get(node["name"], missing))

    return alive_data  # allow flow style usage


def main():
    args = _parse_args()
    load_config(args)
    setup_logging(config)

    alive_metrics = netmon.get_alive_metrics(args.level)
    seen_hosts_metrics = netmon.get_seen_hosts()

    metrics = OrderedDict()

    if args.switch:
        switch_metrics, queue_metrics, dc_metrics, walle_check = get_switch_metrics(
            alive_metrics, seen_hosts_metrics, args.switch)

        metrics["switches"] = [switch_metrics]
        metrics["queues"] = [queue_metrics]
        metrics["datacenters"] = [dc_metrics]
        metrics["wall-e"] = walle_check

        if switch_metrics and (args.probes or args.scores):
            probes = get_probes(switch_metrics, level=args.level, local=args.local,
                                source_switch=args.source_switch, max_score=args.max_score)
            if args.probes:
                metrics["probes"] = probes

            if args.scores:
                metrics["scores"] = count_scores(probes)

    else:
        metrics["switches"] = alive_metrics["switches"]
        metrics["queues"] = append_seen_hosts(alive_metrics["queues"], seen_hosts_metrics["queue"])
        metrics["datacenters"] = append_seen_hosts(alive_metrics["datacenters"], seen_hosts_metrics["dc"])

    print(json.dumps(metrics, indent=2))


if __name__ == "__main__":
    main()
