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

@author: noob
"""

from common.models import Job, CustomUserReport
from common.util.clients import ClickhouseClient, CacheClient
from common.util.decorators import Memoize  # , approve_required
# from django.contrib.auth.decorators import login_required
from django.http import HttpResponse
from itertools import chain
from monitoring.models import Metric
from json import dumps
import logging


# @login_required # let offlinepage be viewed by unauthorized users
# @approve_required
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', '')
    tag = request.GET.get('tags', '')
    if tab == 'monitoring':
        plot_group = request.GET.get('metrics', '')
    elif tab.startswith('custom_'):
        plot_group = 'main'
    else:
        plot_group = request.GET.get('plot_groups', '')
    machine = request.GET.get('machines', '')
    logging.debug('CASE PASSED: %s', tag)
    if tag == u'Overall':
        tag = ''
    plot_groups_mapper = PlotGroupsMapper(job, tab, tag, machine)
    plot_groups_mapper.dispatch()
    plots = plot_groups_mapper.mapping[plot_group]
    compress_ratio = get_compress_ratio(slider_start, slider_end)

    return HttpResponse(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)) / 1000 or 1
    return compress_ratio


class PlotGroupsMapper(object):
    def __init__(self, job, tab, tag, machine):
        """

        :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.cached = CacheClient()

        self.ch_client = ClickhouseClient()

        self.mapping = {}

    @property
    @Memoize
    def job_obj(self):
        try:
            job_obj = Job.check_job(Job.objects.get(n=self.job))
            return job_obj

        except Job.Deleted:
            logging.exception('Job had been deleted')
            return None
        except:
            logging.exception("Could not get job OBJECT for plot due to:")
            return None

    def dispatch(self):
        if self.tab == 'test_data':
            self.mapping = {'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': [
                    # 'aggregates_table',
                    '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.monitoring_mapping()
            logging.debug('MONITORING MAPPING %s', self.mapping)
        elif self.tab.startswith('custom__'):
            self.custom_report_mapping()
            logging.debug('CUSTOM MAPPING %s', self.mapping)

    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 = [self.custom_metric_groups.values()[i]
                          for i in range(len(self.custom_metric_groups.values()))]
            all_tab = []
            for machine in self.target_metrics.keys():
                all_tab.extend(['monitoring_' + str(metric_group).lower() + '_plot@' + str(machine) for metric_group in
                                self.metric_groups[machine]])
            all_tab += list(chain(*all_custom))  # adding custom metrics
            all_tab += ['monitoring_aggregates_table@' + self.machine]  # adding aggregates table
            self.mapping = {
                'Net': ['monitoring_net_plot@' + str(machine) for machine in self.target_metrics.keys() if
                        'Net' in self.metric_groups[machine]],
                'Disk': ['monitoring_disk_plot@' + str(machine) for machine in self.target_metrics.keys() if
                         'Disk' in self.metric_groups[machine]],
                'Memory': ['monitoring_memory_plot@' + str(machine) for machine in self.target_metrics.keys() if
                           'Memory' in self.metric_groups[machine]],
                'System': ['monitoring_system_plot@' + str(machine) for machine in self.target_metrics.keys() if
                           'System' in self.metric_groups[machine]],
                'CPU': ['monitoring_cpu_plot@' + str(machine) for machine in self.target_metrics.keys() if
                        'CPU' in self.metric_groups[machine]],
                'custom': list(chain(*all_custom)),
                'Aggregates': ['monitoring_aggregates_table@' + self.machine],
                '': all_tab,
            }
        else:
            all_tab = ['monitoring_' + str(metric_group).lower() + '_plot@' + self.machine for metric_group in
                       self.metric_groups[self.machine]]
            all_tab += self.custom_metric_groups[self.machine]  # adding custom metrics
            all_tab += ['monitoring_aggregates_table@' + self.machine]  # adding aggregates table
            self.mapping = {
                'Net': ['monitoring_net_plot@' + self.machine] if 'Net' in self.metric_groups[
                    self.machine] else [],
                'Disk': ['monitoring_disk_plot@' + self.machine] if 'Disk' in self.metric_groups[
                    self.machine] else [],
                'Memory': ['monitoring_memory_plot@' + self.machine] if 'Memory' in self.metric_groups[
                    self.machine] else [],
                'System': ['monitoring_system_plot@' + self.machine] if 'System' in self.metric_groups[
                    self.machine] else [],
                'CPU': ['monitoring_cpu_plot@' + self.machine] if 'CPU' in self.metric_groups[
                    self.machine] else [],
                'custom': self.custom_metric_groups[self.machine],
                'Aggregates': ['monitoring_aggregates_table@' + self.machine],
                '': all_tab,
            }

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

        # Monitoring plots
        if self.machine in ('-1', ''):
            mlots = []
            for machine in self.target_metrics.keys():
                for mlot in plots:
                    if mlot.startswith("monitoring_") and mlot != "monitoring_custom_plot":
                        mlots.append(mlot + "@" + str(machine))
            if "monitoring_custom_plot" in plots:
                mlots += list(chain(*self.custom_metric_groups.values()))
        else:
            mlots = [mlot + '@' + self.machine for mlot in plots if
                     mlot.startswith('monitoring_') and mlot != 'monitoring_custom_plot']
            if 'monitoring_custom_plot' in plots:
                mlots += self.custom_metric_groups[self.machine]

        # 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.endswith('_table')] + mlots + tables
        if self.job_obj.multitag:
            replaceable_plots = {
                'cases_timeline_plot': 'multitag_timeline_plot',
                'cases_avg_timeline_plot': 'multitag_avg_timeline_plot',
                '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

        self.mapping = {
            'main': sort_custom_report_plots(plots)
        }

    @property
    @Memoize
    def target_metrics(self):
        """
        cache for all monitoring plots for this job
        returns dict {target_number:{metric_codes: metric_ids}}
        """
        metrics = self.cached.get('job_%s_monitoring_metrics' % self.job_obj.n)
        if metrics:
            logging.debug('Found monitoring target_metrics for job %s in cache', self.job_obj.n)
            return metrics
        else:
            metrics = {}
            logging.warning('No monitoring target_metrics for job %s in cache', self.job_obj.n)

            sql = '''
                select distinct metric_name
                from loaddb.monitoring_verbose_data_buffer
                where job_id=%(job)s
                and job_date=toDate(%(job_date)s)
                and target_host='%(target)s'
            '''
            for machine_obj in self.job_obj.targets:
                query_params = self.job_obj.basic_query_params.copy()
                query_params['target'] = "%s" % 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)}
            logging.debug('Got monitoring target_metrics for job %s: %s', self.job_obj.n, metrics)
            self.cached.set('job_%s_monitoring_metrics' % self.job_obj.n, metrics)
            return metrics

    @property
    @Memoize
    def metric_groups(self):
        """
        returns dict {machine.n:set([target_metrics])}
        """
        metric_groups = {}
        for machine in self.target_metrics.keys():
            metric_groups[machine] = set(
                [metric.split(':')[0].split('_')[0] for metric in self.target_metrics[machine].keys() if
                 metric.split(":")[0] != "custom"])
        logging.debug("Got metric_groups for job %s tab %s machine %s: %s", self.job, self.tab, self.machine,
                      metric_groups)
        return metric_groups

    @property
    def custom_metrics(self):
        """
        returns dict {machine.n:[metric.codes]}
        """
        custom_metrics = {}
        for machine in self.target_metrics.keys():
            custom_metrics[machine] = [metric for metric in self.target_metrics[machine].keys() if
                                       metric.split(':')[0] == 'custom']
        logging.debug('Got custom_metrics for job %s tab %s machine %s: %s', self.job, self.tab, self.machine,
                      custom_metrics)
        return custom_metrics

    @property
    @Memoize
    def custom_metric_groups(self):
        """
        returns dict {machine.n:[grouped and single metric codes]}
        """
        custom_metric_groups = {}
        for machine in self.target_metrics.keys():
            try:
                custom_metrics = [custom_metric.split(':')[1].split('_')[0] for custom_metric in
                                  self.custom_metrics[machine]]
                single_custom_metrics = ['monitoring_customs:' + metric.split(':')[1] + '_plot@' + str(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.split(':')[1].split('_')[0] + '_plot@' + str(machine) for metric in
                     self.custom_metrics[machine]
                     if custom_metrics.count(metric.split(':')[1].split('_')[0]) > 1])
                custom_metric_groups[machine] = list(grouped_custom_metrics) + single_custom_metrics
                logging.debug('Got custom_metric_groups for job %s tab %s machine %s: %s', self.job, self.tab,
                              self.machine, custom_metric_groups)
            except:
                logging.exception('Could not get custom_metric_groups for job %s tab %s machine %s due to:', self.job,
                                  self.tab, self.machine)
                custom_metric_groups[machine] = []
        return custom_metric_groups
