# -*- coding: utf-8 -*-
"""
Created on Jul 26, 2013

@author: noob
"""
import collections
import json
import logging
from itertools import chain

from django.http import HttpResponse

from common.models import Job, CustomUserReport
from common.util.clients import ClickhouseClient
from common.util.decorators import memoized_property, cached
from monitoring.models import Metric


def get_plots_to_show(request, job):
    """

    :param request: HTTP Request
    :param job: Job NUMBER
    """
    slider_start = request.GET.get('slider_start', '')
    slider_end = request.GET.get('slider_end', '')
    tab = request.GET.get('tab', '')
    case = request.GET.get('tags', '')
    mobile = request.GET.get('mobile', '')
    if tab == 'monitoring':
        plot_group = request.GET.get('metrics', '')
    elif tab.startswith('custom_') or tab == 'mobile':
        plot_group = 'main'
    else:
        plot_group = request.GET.get('plot_groups', '')
    machine = request.GET.get('machines', '')
    if case == 'Все':
        case = ''
    plot_groups_mapper = PlotGroupsMapper(job, tab, case, machine, mobile)
    plot_groups_mapper.dispatch()
    plots = plot_groups_mapper.mapping[plot_group]
    compress_ratio = get_compress_ratio(slider_start, slider_end)

    return HttpResponse(json.dumps({'compress_ratio': int(compress_ratio),
                                    'plots': plots,
                                    'tab': tab}), content_type='application/json')


def get_compress_ratio(slider_start, slider_end):
    """

    :param slider_start:
    :param slider_end:
    """
    compress_ratio = (int(slider_end) - int(slider_start)) // 500 or 1  # 1..999 points
    return compress_ratio


class PlotGroupsMapper(object):
    def __init__(self, job, tab, tag, machine, mobile=''):
        """
        Распихивает метрики по группам (графикам) и графики по вкладкам
        :param job: Job NUMBER
        :param tab: monitoring or test_data
        :param tag: string
        :param machine: string Server.n
        """
        self.job = job
        self.tab = tab
        self.tag = tag
        self.machine = machine
        self.mobile = mobile

        self.mapping = {}

        self.ch_client = ClickhouseClient()

    @memoized_property
    def job_obj(self):
        try:
            job_obj = Job.objects.get(n=self.job)
            return job_obj
        except Job.DoesNotExist:
            logging.exception("Could not get job OBJECT for plot due to:")
            return None

    def dispatch(self):
        if self.tab == 'test_data':
            self.mapping.update({
                'main': [
                    'quantiles_timeline_plot',
                    'instanses_timeline_plot' if self.job_obj.scheme_type == 'rps' else 'timesdist_timeline_plot',
                    'http_timeline_plot',
                    'net_timeline_plot',
                ],
                'extended': [
                    'avgtimes_timeline_plot',
                    'timesdist_timeline_plot' if self.job_obj.scheme_type == 'rps' else 'instanses_timeline_plot',
                    'cases_timeline_plot',
                    'cases_avg_timeline_plot',
                    'sent_received_plot',
                ],
                'additional': [
                    'times_dist_plot',
                    'quantiles_vs_times',
                    'avgtimes_vs_rps',
                    'avgtimes_vs_instanses',
                    'quantiles_vs_rps',
                    'quantiles_vs_instanses',
                ],
                'tables': [
                    'quantiles_cumulative_table',
                    'http_dist_table',
                    'net_dist_table',
                    'times_dist_table',
                ],
            })

            if self.job_obj.multitag:
                if not self.tag and self.job_obj.tags:
                    self.mapping['tables'].insert(0, 'multitag_dist_table')
                    self.mapping['tables'].insert(0, 'multitag_httpcodes_table')
                    self.mapping['tables'].insert(0, 'multitag_netcodes_table')
                    self.mapping['tables'].insert(0, 'multitag_cumulative_quantiles_table')
            else:
                if not self.tag and self.job_obj.tags:
                    self.mapping['tables'].insert(0, 'cases_dist_table')
                    self.mapping['tables'].insert(0, 'cases_httpcodes_table')
                    self.mapping['tables'].insert(0, 'cases_netcodes_table')
                    self.mapping['tables'].insert(0, 'cases_cumulative_quantiles_table')
        elif self.tab == 'monitoring':
            self.mapping.update(self.monitoring_mapping())
        elif self.tab.startswith('custom__'):
            self.mapping.update(self.custom_report_mapping())
        elif self.tab == 'mobile':
            self.mapping.update({'main': ['mobile_quantiles_plot@{}'.format(self.mobile)]})

    def monitoring_mapping(self):
        """
        plot mapping may vary according to some initial parameters: i.e. case (aka machine)
        """
        if self.machine == '-1':  # all targets
            all_custom = list(self.custom_metric_groups.values())
            all_tab = []
            for machine in list(self.target_metrics.keys()):
                all_tab.extend(sorted(
                    'monitoring_{}_plot@{}'.format(str(metric_group).lower(), machine)
                    for metric_group in self.metric_groups[machine]
                ))
            all_tab += sorted(chain(*all_custom))  # adding custom and telegraf metrics
            all_tab += ['monitoring_aggregates_table@{}'.format(self.machine)]  # adding aggregates table
            mapping = {
                'Net': ['monitoring_net_plot@{}'.format(machine) for machine in list(self.target_metrics.keys()) if
                        'Net' in self.metric_groups[machine]],
                'Disk': ['monitoring_disk_plot@{}'.format(machine) for machine in list(self.target_metrics.keys()) if
                         'Disk' in self.metric_groups[machine]],
                'Memory': ['monitoring_memory_plot@{}'.format(machine) for machine in list(self.target_metrics.keys()) if
                           'Memory' in self.metric_groups[machine]],
                'System': ['monitoring_system_plot@{}'.format(machine) for machine in list(self.target_metrics.keys()) if
                           'System' in self.metric_groups[machine]],
                'CPU': ['monitoring_cpu_plot@{}'.format(machine) for machine in list(self.target_metrics.keys()) if
                        'CPU' in self.metric_groups[machine]],
                'custom': sorted(
                    cm for cm in chain(*all_custom)
                    if not cm.split(':')[1].startswith('cpu-cpu')
                       and not cm.split(':')[1].startswith('diskio-')
                       and not cm.split(':')[1].startswith('net-')
                ),
                'Агрегаты': ['monitoring_aggregates_table@{}'.format(self.machine)],
                '': all_tab
            }

            for k in self.telegraf_metric_groups:
                for machine in list(self.target_metrics.keys()):
                    mapping[k] += self.telegraf_metric_groups[k][machine]

        else:
            all_tab = ['monitoring_{}_plot@{}'.format(str(metric_group).lower(), self.machine) for metric_group in
                       self.metric_groups[self.machine]]
            all_tab += self.custom_metric_groups[self.machine]  # adding custom and telegraf metrics
            all_tab.sort()
            all_tab += ['monitoring_aggregates_table@{}'.format(self.machine)]  # adding aggregates table

            mapping = {
                'Net': ['monitoring_net_plot@{}'.format(self.machine)] if 'Net' in self.metric_groups[
                    self.machine] else [],
                'Disk': ['monitoring_disk_plot@{}'.format(self.machine)] if 'Disk' in self.metric_groups[
                    self.machine] else [],
                'Memory': ['monitoring_memory_plot@{}'.format(self.machine)] if 'Memory' in self.metric_groups[
                    self.machine] else [],
                'System': ['monitoring_system_plot@{}'.format(self.machine)] if 'System' in self.metric_groups[
                    self.machine] else [],
                'CPU': ['monitoring_cpu_plot@{}'.format(self.machine)] if 'CPU' in self.metric_groups[
                    self.machine] else [],
                'custom': sorted(
                    cm for cm in self.custom_metric_groups[self.machine]
                    if not cm.split(':')[1].startswith('cpu-cpu')
                       and not cm.split(':')[1].startswith('diskio-')
                       and not cm.split(':')[1].startswith('net-')
                ),
                'Агрегаты': ['monitoring_aggregates_table@{}'.format(self.machine)],
                '': all_tab,
            }

            for k in sorted(self.telegraf_metric_groups.keys()):
                mapping[k] += self.telegraf_metric_groups[k][self.machine]

        return mapping

    def custom_report_mapping(self):
        """
        Custom user report
        """
        plots = sorted(CustomUserReport.objects.get(n=int(self.tab.split('__')[1])).plots)

        # Monitoring plots
        if self.machine in ['-1', '']:  # all targets
            mlots = []
            for machine in list(self.target_metrics.keys()):
                for mlot in plots:
                    if mlot.startswith('monitoring_') and mlot != 'monitoring_custom_plot':
                        mlots.append('{}@{}'.format(mlot, machine))
            if 'monitoring_custom_plot' in plots:
                mlots += sorted(
                    cm for cm in chain(*list(self.custom_metric_groups.values()))
                    if not any(cm.split(':')[1].startswith(prefix) for prefix in ('cpu-cpu', 'diskio-', 'net-'))
                )
        else:
            mlots = ['{}@{}'.format(mlot, self.machine) for mlot in plots if
                     mlot.startswith('monitoring_') and mlot != 'monitoring_custom_plot']
            if 'monitoring_custom_plot' in plots:
                mlots += [
                    cm for cm in self.custom_metric_groups[self.machine]
                    if not any(cm.split(':')[1].startswith(prefix) for prefix in ('cpu-cpu', 'diskio-', 'net-'))
                ]

        mobile_plots = ['mobile_quantiles_plot@{}'.format(self.mobile)] if self.mobile else []

        # Tables
        tables = [table for table in plots if table.endswith('_table') and table != 'monitoring_aggregates_table']

        plots = [plot for plot in plots if
                 not plot.startswith('monitoring_')
                 and not plot.startswith('mobile_')
                 and not plot.endswith('_table')] + mlots + tables + mobile_plots
        if self.job_obj.multitag:
            replaceable_plots = {
                'cases_dist_table': 'multitag_dist_table',
                'cases_httpcodes_table': 'multitag_httpcodes_table',
                'cases_netcodes_table': 'multitag_netcodes_table',
                'cases_cumulative_quantiles_table': 'multitag_cumulative_quantiles_table',
            }
            for index, plot in enumerate(plots):
                if plot in replaceable_plots:
                    plots[index] = replaceable_plots[plot]

        def sort_custom_report_plots(plots):
            """
            :param plots: list of strings
            :return: list of strings
            """
            try:
                ordered_plots = []
                for o in CustomUserReport.plots_order():
                    for p in plots:
                        if p.startswith(o):
                            ordered_plots.append(plots.pop(plots.index(p)))
                ordered_plots += plots
                return ordered_plots
            except:
                return plots

        return {'main': sort_custom_report_plots(plots)}

    @memoized_property
    def target_metrics(self):
        """
        memcached for all monitoring plots for this job
        returns dict {target_number:{metric_codes: metric_ids}}
        """
        cache_key = ('job_{}_monitoring_metrics'.format(self.job_obj.n))

        @cached(cache_key)
        def fetch():
            metrics = {}
            sql = '''
                select distinct metric_name
                from loaddb.monitoring_verbose_data_buffer
                where job_id={job}
                and job_date=toDate({job_date})
                and target_host='{target}'
            '''
            for machine_obj in self.job_obj.targets:
                query_params = self.job_obj.basic_query_params.copy()
                query_params['target'] = machine_obj.host
                metrics[str(machine_obj.n)] = {Metric.objects.get_or_create(code=value[0])[0].code: value[0]
                                          for value in self.ch_client.select(sql, query_params=query_params)}
            return metrics
        return fetch()

    @memoized_property
    def metric_groups(self):
        """
        returns dict {machine.n:set([target_metrics])}
        """
        metric_groups = {}
        for machine in list(self.target_metrics.keys()):
            metric_groups[machine] = set(
                [metric.split(':')[0].split('_')[0] for metric in list(self.target_metrics[machine].keys()) if
                 metric.split(':')[0] != 'custom'])
        return metric_groups

    @property
    def custom_metrics(self):
        """
        returns dict {machine.n:[metric.codes]}
        """
        custom_metrics = {}
        for machine in list(self.target_metrics.keys()):
            custom_metrics[machine] = [metric for metric in list(self.target_metrics[machine].keys()) if
                                       metric.split(':')[0] == 'custom']
        return custom_metrics

    @memoized_property
    def telegraf_metric_groups(self):
        """
        returns dict {group {machine.n:: metrics}}
        """
        telegraf_metric_groups = {
            'CPU': collections.defaultdict(list),
            'Disk': collections.defaultdict(list),
            'Net': collections.defaultdict(list),
        }
        for machine in list(self.target_metrics.keys()):
            # CPU
            custom_cpu_metrics = [
                custom_metric.split(':')[1].split('_')[0]
                for custom_metric in self.custom_metrics[machine]
                if custom_metric.split(':')[1].startswith('cpu-cpu')
            ]
            single_custom_cpu_metrics = [
                'telegraf_customs:{metric}_plot@{machine}'.format(metric=metric.split(':')[1], machine=machine)
                for metric in self.custom_metrics[machine]
                if custom_cpu_metrics.count(metric.split(':')[1].split('_')[0]) == 1
            ]
            grouped_custom_cpu_metrics = set([
                'telegraf_customg:{metric_group}_plot@{machine}'
                .format(metric_group=metric.split(':')[1].split('_')[0], machine=machine)
                for metric in self.custom_metrics[machine]
                if custom_cpu_metrics.count(metric.split(':')[1].split('_')[0]) > 1
            ])
            telegraf_metric_groups['CPU'][machine].extend(
                list(grouped_custom_cpu_metrics) + single_custom_cpu_metrics
            )
            # DISK
            custom_disk_metrics = [
                custom_metric.split(':')[1].split('_')[0]
                for custom_metric in self.custom_metrics[machine]
                if custom_metric.split(':')[1].startswith('diskio-')
            ]
            single_custom_disk_metrics = [
                'telegraf_customs:{metric}_plot@{machine}'.format(metric=metric.split(':')[1], machine=machine)
                for metric in self.custom_metrics[machine]
                if custom_disk_metrics.count(metric.split(':')[1].split('_')[0]) == 1
            ]
            grouped_custom_disk_metrics = set([
                'telegraf_customg:{metric_group}_plot@{machine}'
                .format(metric_group=metric.split(':')[1].split('_')[0], machine=machine)
                for metric in self.custom_metrics[machine]
                if custom_disk_metrics.count(metric.split(':')[1].split('_')[0]) > 1
            ])
            telegraf_metric_groups['Disk'][machine].extend(
                list(grouped_custom_disk_metrics) + single_custom_disk_metrics
            )
            # NET
            custom_net_metrics = [
                custom_metric.split(':')[1].split('_')[0]
                for custom_metric in self.custom_metrics[machine]
                if custom_metric.split(':')[1].startswith('net-')
            ]
            single_custom_net_metrics = [
                'telegraf_customs:{metric}_plot@{machine}'.format(metric=metric.split(':')[1], machine=machine)
                for metric in self.custom_metrics[machine]
                if custom_net_metrics.count(metric.split(':')[1].split('_')[0]) == 1
            ]
            grouped_custom_net_metrics = set([
                'telegraf_customg:{metric_group}_plot@{machine}'
                .format(metric_group=metric.split(':')[1].split('_')[0], machine=machine)
                for metric in self.custom_metrics[machine]
                if custom_net_metrics.count(metric.split(':')[1].split('_')[0]) > 1
            ])
            telegraf_metric_groups['Net'][machine].extend(
                list(grouped_custom_net_metrics) + single_custom_net_metrics
            )

        return telegraf_metric_groups

    @memoized_property
    def custom_metric_groups(self):
        """
        returns dict {machine.n:[grouped and single metric codes]}
        """
        custom_metric_groups = {}
        for machine in list(self.target_metrics.keys()):
            custom_metrics = [
                custom_metric.split(':')[1].split('_')[0]
                for custom_metric in self.custom_metrics[machine]
            ]
            # if not any(custom_metric.split(':')[1].startswith(prefix) for prefix in ('cpu-cpu', 'diskio-', 'net-'))
            single_custom_metrics = [
                'monitoring_customs:{metric}_plot@{machine}'.format(metric=metric.split(':')[1], machine=machine)
                for metric in self.custom_metrics[machine]
                if custom_metrics.count(metric.split(':')[1].split('_')[0]) == 1
            ]
            grouped_custom_metrics = set([
                'monitoring_customg:{metric_group}_plot@{machine}'
                .format(metric_group=metric.split(':')[1].split('_')[0], machine=machine)
                for metric in self.custom_metrics[machine]
                if custom_metrics.count(metric.split(':')[1].split('_')[0]) > 1
            ])
            custom_metric_groups[machine] = sorted(list(grouped_custom_metrics) + single_custom_metrics)
        return custom_metric_groups
