# -*- coding: utf-8 -*-
"""
Created on Mar 31, 2014

@author: noob
"""

from .plots_base import TimelineComparePlot
from common.models import Server
from common.util.decorators import memoized_property
from common.util.meta import format_job_dates
from django.core.exceptions import ObjectDoesNotExist
from monitoring.models import Metric
import logging


class MonitoringComparePlot(TimelineComparePlot):
    yaxis_label = ''

    @memoized_property
    def target_obj(self):
        return Server.objects.get(n=self.target)

    @property
    def subtitle(self):
        helpers_map = {'all': 'Весь тест'}
        try:
            helper = helpers_map[self.helper]
        except KeyError:
            helper = self.helper
        subtitle = ''
        if helper != 'Весь тест':
            subtitle += str(helper)
            if helper.isdigit():
                subtitle += ' reqps'
        return subtitle

    @memoized_property
    def metrics(self):
        """
        returns Metric queryset
        """
        sql = '''
            select distinct metric_name
            from loaddb.monitoring_verbose_data_buffer
            where job_id in ({jobs})
            and job_date in ({job_dates})
            '''
        query_params = {
            'jobs': ','.join(str(job_obj.n) for job_obj in self.job_objects),
            'job_dates': format_job_dates(self.job_objects),
        }
        if self.target != '-1':
            sql += " and target_host='{target}'"
            query_params['target'] = self.target_obj.host
        metric_objects = []
        metric_objects += [Metric.objects.get_or_create(
            code=m[0])[0] for m in self.ch_client.select(sql, query_params=query_params)]
        return set(metric_objects)

    def fetch_data(self, job_obj):
        """

        :param job_obj: Job OBJECT
        """
        try:
            sql = '''
                select data from
                (
                select intDiv(toUInt32(time), {compress_ratio})*{compress_ratio} as t
                from loaddb.monitoring_verbose_data_buffer
                where job_id={job}
                and job_date=toDate({job_date})
                and time >= toDateTime({start})
                and time <= toDateTime({end})
                group by t
                order by t
                ) all left join
                (
                select
                round(avg(value), 3) as data,
                intDiv(toUInt32(time), {compress_ratio})*{compress_ratio} as t
                from loaddb.monitoring_verbose_data_buffer
                where job_id={job}
                and job_date=toDate({job_date})
                and target_host='{target}'
                and metric_name='{metric}'
                and time >= toDateTime({start})
                and time <= toDateTime({end})
                group by t
                order by t
                )
                using t'''
            query_params = job_obj.basic_query_params.copy()
            query_params.update({
                'start': self.intervals[job_obj.n]['start'],
                'end': self.intervals[job_obj.n]['end'],
                'compress_ratio': self.compress_ratio,
                'target': self.target_obj.host,
                'metric': Metric.objects.get(id=self.selector).code
            })
            return [v[0] for v in self.ch_client.select(sql, query_params=query_params)]

        except:
            logging.exception(
                'Could not fetch data for {} for job {}, due to:'
                .format(self.__class__.__name__, job_obj.n)
            )
            return []

    @memoized_property
    def series_data(self):
        series_data = []
        if not self.selector:
            self.selector = self.default_selector
        if self.selector is None:
            return None
        for job_obj in self.job_objects:
            serie_data = self.fetch_data(job_obj)
            serie = {
                'marker': {
                    'enabled': False,
                    'states': {
                        'hover': {
                            'enabled': False,
                        },
                    },
                },
                'color': self.color_mapping(self.job_objects.index(job_obj)),
                'name': str(job_obj.n) + ' ' + str(job_obj.name),
                'data': serie_data,
                'label': self.yaxis_label,
            }
            series_data.append(serie)
            if not job_obj.monitoring_only:
                scheme_serie = {
                    'yAxis': 1,
                    'marker': {
                        'enabled': False,
                        'states': {
                            'hover': {
                                'enabled': False,
                            },
                        },
                    },
                    'color': self.color_mapping(self.job_objects.index(job_obj)),
                    'connectNulls': self.scheme_types[job_obj.n] == 'threads',
                    'name': self.scheme_types[job_obj.n],
                    'data': self.scheme_data[job_obj.n],
                    'dashStyle': 'ShortDot',
                    'label': '',
                }
                series_data.append(scheme_serie)
        return series_data

    @memoized_property
    def scheme_data(self):
        """
        gets scheme data (made in one method not to rape db)
        """
        scheme_data = {}
        for job_obj in self.job_objects:
            sql = '''
                select scheme from
                (
                select intDiv(toUInt32(time), {compress_ratio})*{compress_ratio} as t
                from loaddb.monitoring_verbose_data_buffer
                where job_id={job}
                and job_date=toDate({job_date})
                and time >= toDateTime({start})
                and time <= toDateTime({end})
                group by t
                order by t
                ) all left join
                (
                select intDiv(toUInt32(time), {compress_ratio})*{compress_ratio} as t,
                any({scheme_type}) as scheme
                from loaddb.rt_microsecond_details_buffer
                where job_id={job}
                and job_date=toDate({job_date})
                and time >= toDateTime({start})
                and time <= toDateTime({end})
                group by t
                order by t
                )
                using t'''
            query_params = job_obj.basic_query_params.copy()
            query_params.update({
                'scheme_type': self.scheme_types[job_obj.n],
                'start': self.intervals[job_obj.n]['start'],
                'end': self.intervals[job_obj.n]['end'],
                'compress_ratio': self.compress_ratio
            })
            fetched_data = self.ch_client.select(sql, query_params=query_params)
            # processing raw data
            try:
                if self.scheme_types[job_obj.n] == 'threads':
                    scheme_data[job_obj.n] = [value[0] if value[0] else None for value in fetched_data]
                else:
                    scheme_data[job_obj.n] = [value[0] if value[0] else 0 for value in fetched_data]
            except:
                logging.exception('Could not get scheme_data for job {} due to:'.format(job_obj.n))
                scheme_data[job_obj.n] = []
        return scheme_data

    @property
    def default_selector(self):
        try:
            return self.selectors[0][0]
        except IndexError:
            return None


class MonitoringNetComparePlot(MonitoringComparePlot):
    @property
    def title(self):
        try:
            return 'Сеть [{}] @ {}'.format(
                [sel[1] for sel in self.selectors if sel[0] == self.selector][0], self.target_obj.name)
        except:
            return 'Сеть @ {}'.format(self.target_obj.name)

    @property
    def yaxis_label(self):
        if self.selector in ('31', '32'):  # recv, send
            return ' bytes'
        else:
            return ''

    @property
    def selectors(self):
        try:
            selectors = []
            for metric in sorted(self.metrics, key=lambda m: m.code, reverse=True):
                if metric.code.split('_')[0] == 'Net':
                    selectors.append([str(metric.id), metric.code.split('_')[1]])
            return selectors
        except:
            logging.exception('Could not get selectors for {} for {} due to:'.format(self.__class__.__name__,
                              [job_obj.n for job_obj in self.job_objects]))
            return []


class MonitoringCPUComparePlot(MonitoringComparePlot):
    yaxis_label = ' %'

    @property
    def title(self):
        try:
            return 'Процессор [{}] @ {}'.format(
                [sel[1] for sel in self.selectors if sel[0] == self.selector][0], self.target_obj.name)
        except:
            return 'Процессор @ {}' .format(self.target_obj.name)

    @property
    def selectors(self):
        try:
            selectors = []
            for metric in sorted(self.metrics, key=lambda m: m.code, reverse=True):
                if metric.code.split('_')[0] == 'CPU':
                    selectors.append([str(metric.id), metric.code.split('_')[1]])
            return selectors
        except:
            logging.exception('Could not get selectors for {} for {} due to:'.format(self.__class__.__name__,
                              [job_obj.n for job_obj in self.job_objects]))
            return []


class MonitoringMemoryComparePlot(MonitoringComparePlot):
    yaxis_label = ' bytes'

    @property
    def title(self):
        try:
            return 'Память [{}] @ {}'.format(
                [sel[1] for sel in self.selectors if sel[0] == self.selector][0], self.target_obj.name)
        except:
            return 'Память @ {}'.format(self.target_obj.name)

    @property
    def selectors(self):
        try:
            selectors = []
            for metric in sorted(self.metrics, key=lambda m: m.code, reverse=True):
                if metric.code.split('_')[0] == 'Memory':
                    selectors.append([str(metric.id), metric.code.split('_')[1]])
            return selectors
        except:
            logging.exception('Could not get selectors for {} for {} due to:'.format(self.__class__.__name__,
                              [job_obj.n for job_obj in self.job_objects]))
            return []


class MonitoringDiskComparePlot(MonitoringComparePlot):
    yaxis_label = ' bytes'

    @property
    def title(self):
        try:
            return 'Диск [{}] @ {}'.format(
                [sel[1] for sel in self.selectors if sel[0] == self.selector][0], self.target_obj.name)
        except:
            return 'Диск @ {}'.format(self.target_obj.name)

    @property
    def selectors(self):
        try:
            selectors = []
            for metric in sorted(self.metrics, key=lambda m: m.code, reverse=True):
                if metric.code.split('_')[0] == 'Disk':
                    selectors.append([str(metric.id), metric.code.split('_')[1]])
            return selectors
        except:
            logging.exception('Could not get selectors for {} for {} due to:'.format(self.__class__.__name__,
                              [job_obj.n for job_obj in self.job_objects]))
            return []


class MonitoringSystemComparePlot(MonitoringComparePlot):
    yaxis_label = ''

    @property
    def title(self):
        try:
            return 'Система [{}] @ {}'.format(
                [sel[1] for sel in self.selectors if sel[0] == self.selector][0], self.target_obj.name)
        except:
            return 'Система @ {}'.format(self.target_obj.name)

    @property
    def selectors(self):
        try:
            selectors = []
            for metric in sorted(self.metrics, key=lambda m: m.code, reverse=True):
                if metric.code.split('_')[0] == 'System':
                    selectors.append([str(metric.id), metric.code.split('_')[1]])
            return selectors
        except:
            logging.exception('Could not get selectors for {} for {} due to:'.format(self.__class__.__name__,
                              [job_obj.n for job_obj in self.job_objects]))
            return []


class MonitoringCustomComparePlot(MonitoringComparePlot):
    yaxis_label = ''

    @property
    def title(self):
        try:
            return self.custom_metric.split(':')[1] + ' [{}] @ {}'.format(
                [sel[1] for sel in self.selectors if sel[0] == self.selector][0], self.target_obj.name)
        except:
            return self.custom_metric.split(':')[1] + ' @ {}'.format(self.target_obj.name)

    @property
    def selectors(self):
        try:
            selectors = []
            if self.custom_metric.split(':')[0] == 'customg':
                for metric in sorted(self.metrics, key=lambda m: m.code, reverse=True):
                    if metric.code.split(':')[0] == 'custom' and metric.code.split(':')[1].split('_')[0] == \
                            self.custom_metric.split(':')[1]:
                        selectors.append([str(metric.id), '_'.join(metric.code.split(':')[1].split('_')[1:])])
            return selectors
        except:
            logging.exception('Could not get selectors for {} for {} due to:'.format(self.__class__.__name__,
                              [job_obj.n for job_obj in self.job_objects]))
            return []

    @property
    def default_selector(self):
        if self.custom_metric.split(':')[0] == 'customs':
            try:
                return Metric.objects.get_or_create(code='custom:' + self.custom_metric.split(':')[1])[0].id
            except ObjectDoesNotExist:
                return None
        elif self.custom_metric.split(':')[0] == 'customg':
            try:
                return self.selectors[0][0]
            except IndexError:
                return None

    @memoized_property
    def series_data(self):
        series_data = []
        if not self.selector:
            self.selector = self.default_selector
        if self.selector is None:
            return None
        for job_obj in self.job_objects:
            serie_data = self.fetch_data(job_obj)
            serie = {
                'marker': {
                    'enabled': False,
                    'states': {
                        'hover': {
                            'enabled': False,
                        },
                    },
                },
                'color': self.color_mapping(self.job_objects.index(job_obj)),
                'name': str(job_obj.n) + ' ' + str(job_obj.name),
                'data': serie_data,
                'label': self.yaxis_label,
            }
            series_data.append(serie)
            if not job_obj.monitoring_only:
                scheme_serie = {
                    'yAxis': 1,
                    'marker': {
                        'enabled': False,
                        'states': {
                            'hover': {
                                'enabled': False,
                            },
                        },
                    },
                    'color': self.color_mapping(self.job_objects.index(job_obj)),
                    'connectNulls': self.scheme_types[job_obj.n] == 'threads',
                    'name': self.scheme_types[job_obj.n],
                    'data': self.scheme_data[job_obj.n],
                    'dashStyle': 'ShortDot',
                    'label': '',
                }
                series_data.append(scheme_serie)
        return series_data

    @property
    def negative_values(self):
        try:
            if not self.selector:
                self.selector = self.default_selector
            query_params = {
                'jobs': ','.join(str(job_obj.n) for job_obj in self.job_objects),
                'job_dates': format_job_dates(self.job_objects),
                'target': Server.objects.get(n=self.target).host,
                'metric': Metric.objects.get(id=int(self.selector)).code,
            }
            sql = '''
                    select any(value)
                    from loaddb.monitoring_verbose_data_buffer
                    where job_id in ({jobs})
                    and job_date in ({job_dates})
                    and target_host='{target}'
                    and metric_name='{metric}'
                    and value < 0
                    '''
            return bool(self.ch_client.select(sql, query_params=query_params))
        except:
            logging.exception('')
            return False

    @property
    def yaxis(self):
        yaxis = [{
            'plotLines': [{
                'color': '#808080',
                'value': 0,
                'width': 1
            }],
            'showFirstLabel': False,
            'showLastLabel': False,
            'labels': {
                'align': 'left',
                'x': 12,
                'y': -4,
            },
            'title': {
                'text': '',
            },
            'min': None if self.negative_values else 0,  # for negative values
            'allowDecimals': False,
            'label': self.yaxis_label,
        }]
        if not all(j.monitoring_only for j in self.job_objects):
            yaxis.append({
                'plotLines': [{
                    'color': '#808080',
                    'value': 0,
                    'width': 1
                }],
                'showFirstLabel': False,
                'showLastLabel': False,
                'labels': {
                    'align': 'right',
                    'x': -12,
                    'y': -4,
                },
                'gridLineWidth': 0,
                'opposite': True,
                'label': 'rps' if len(set(self.scheme_types.values())) == 1 and list(set(self.scheme_types.values()))[
                                                                                    0] == 'reqps' else '',
                'title': {
                    'text': '',
                },
                'min': 0,
                'allowDecimals': False,
            })
        return yaxis


class TelegrafCPUComparePlot(MonitoringCPUComparePlot):

    @property
    def selectors(self):
        try:
            selectors = []
            for metric in sorted(self.metrics, key=lambda m: m.code, reverse=True):
                if metric.code.split(':')[0] == 'custom' and metric.code.split(':')[1].split('_').startswith('cpu-cpu'):
                    selectors.append([str(metric.id), metric.code.split('_')[1]])
            return selectors
        except:
            logging.exception('Could not get selectors for {} for {} due to:'.format(self.__class__.__name__,
                              [job_obj.n for job_obj in self.job_objects]))
            return []
