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

@author: noob
"""

import logging
from hashlib import md5
from json import dumps

from django.core.exceptions import ObjectDoesNotExist
from django.db import connections
from django.http import HttpResponse, HttpResponseBadRequest, HttpResponseServerError
from django_yauth.decorators import yalogin_required

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


@yalogin_required
def get_layout(request, job):
    user = request.yauser
    job_obj = Job.objects.get(n=job)
    custom_reports = CustomUserReport.objects.filter(user=user.login, active=1)
    try:
        test_data_controls = get_test_data_controls(job_obj)
        monitoring_controls = get_monitoring_controls(job_obj)

        layout = []

        if not job_obj.monitoring_only:
            layout.append({'name': 'test_data',
                           'title': 'Данные теста',
                           'controls': test_data_controls
                           })
        if job_obj.monitoring_exists:
            layout.append({'name': 'monitoring',
                           'title': 'Мониторинг',
                           'controls': monitoring_controls
                           })
        if custom_reports:
            for cur in custom_reports:
                layout.append({'name': 'custom__{}'.format(cur.n),
                               'title': cur.name.split(':')[1].strip() or 'custom:{}'.format(cur.n),
                               'controls': get_custom_controls(job_obj, cur)
                               })
        return HttpResponse(dumps(layout), content_type='application/json')
    except ObjectDoesNotExist:
        logging.warning('No such Job: {}'.format(job))
        return HttpResponseBadRequest
    except:
        logging.exception('Could not get layout for job {} due to:'.format(job))
        return HttpResponseServerError


def get_test_data_controls(job_obj):
    """
    returns list of dicts with controls for monitoring tab layout
    slider, targers, metric_groups
    :param job_obj: Job OBJECT
    """
    try:
        if job_obj.multitag:
            delimiter = '|'
            tags = []
            for case in job_obj.cases:
                tags.extend(case.split(delimiter))
            cases = sorted(set(tags))
        else:
            cases = job_obj.cases
        test_data_layout = [
            {'name': 'plot_groups',
             'type': 'radio',
             'default': 'main',
             'values': (('main', 'Обзор теста'),
                        ('extended', 'Расширенный анализ'),
                        ('additional', 'Распределения и сводные'),
                        ('tables', 'Таблицы')
                        )
             },
            {'name': 'tags',
             'type': 'radio',
             'default': '',
             'values': list(zip([''] + [md5(c.encode('utf-8')).hexdigest() for c in cases], ['Все теги'] + cases))
             },
        ]
        return test_data_layout
    except:
        logging.exception('Could not get test_data_controls for job {} due to:'.format(job_obj.n))
        return None


def get_monitoring_controls(job_obj):
    """
    returns list of dicts with controls for monitoring tab layout
    slider, targers, metric_groups
    :param job_obj: Job OBJECT
    """
    try:
        monitoring_controls_processor = MonitoringControlsProcessor(job_obj)
        if not job_obj.monitoring_exists:
            return None
        else:
            monitoring_controls = [
                {'name': 'machines',
                 'type': 'radio',
                 'default': monitoring_controls_processor.default_target,
                 'values': monitoring_controls_processor.targets
                 },
                {'name': 'metrics',
                 'type': 'radio',
                 'default': monitoring_controls_processor.default_metrics_group,
                 'values': list(zip([''] + monitoring_controls_processor.metrics_groups,
                               ['Все метрики'] + monitoring_controls_processor.metrics_groups))
                 }]
            return monitoring_controls
    except:
        logging.exception('Could not get monitoring_controls for job {} due to:'.format(job_obj.n))
        return None


def get_custom_controls(job_obj, cur):
    """

    :param job_obj: Job OBJECT
    :param cur: CustomUserReport OBJECT
    """
    if job_obj.multitag:
        delimiter = '|'
        tags = []
        for case in job_obj.cases:
            tags.extend(case.split(delimiter))
        cases = sorted(set(tags))
    else:
        cases = job_obj.cases
    custom_report_layout = [
        {'name': 'plot_groups',
         'type': 'radio',
         'default': 'main',
         'values': (('main', cur.name.split(':')[1].strip() or 'custom:{}'.format(cur.n)),)
         },
        {'name': 'tags',
         'type': 'radio',
         'default': '',
         'values': list(zip([''] + [md5(c.encode('utf-8')).hexdigest() for c in cases], ['Все теги'] + cases)),
         },
    ]
    if job_obj.monitoring_exists and [p for p in cur.plots if p.startswith('monitoring_')]:
        monitoring_controls_processor = MonitoringControlsProcessor(job_obj)
        custom_report_layout.append({
            'name': 'machines',
            'type': 'radio',
            'default': monitoring_controls_processor.default_target,
            'values': monitoring_controls_processor.targets,
        })
    return custom_report_layout


@yalogin_required
def get_single_report_controls(request, job):
    """

    :param request: HTTP Request
    :param job: Job NUMBER
    """
    try:
        job_obj = Job.objects.get(n=job)
        cur_n = request.GET.get('cur', '0')
        cur = CustomUserReport.objects.get(n=cur_n)
        controls = {'name': 'custom__{}'.format(cur.n),
                    'title': cur.name.split(':')[1].strip() or 'custom:{}'.format(cur.n),
                    'controls': get_custom_controls(job_obj, cur)
                    }
        return HttpResponse(dumps(controls), content_type='application/json')
    except:
        logging.exception('')
        return HttpResponseBadRequest()


class MonitoringControlsProcessor(object):
    def __init__(self, job_obj):
        """

        :param job_obj: Job OBJECT
        """
        self.job_obj = job_obj
        self.ch_client = ClickhouseClient()

    @memoized_property
    def targets(self):
        """
        returns tuple of tuples
        """
        try:
            targets = tuple([(target.n, target.host, target.dsc) for target in self.job_obj.targets])
            # TODO:        if len(targets) > 1:  # adding all targets tab
            #                 targets = tuple(list(targets) + [(-1, u'All targets', u'All targets')])
            return targets
        except:
            logging.exception('Could not get targets for job {}. Taking job srv as target'.format(self.job_obj.n))
            targets = ((self.job_obj.srv.n, self.job_obj.srv.host, self.job_obj.srv.dsc),)
            return targets

    @property
    def default_target(self):
        """
        returns target's number
        """
        if self.job_obj.srv in self.job_obj.targets:
            try:
                default_target = self.job_obj.srv.n
                return default_target
            except:
                logging.exception('Could not get default_target for job {} due to:'.format(self.job_obj.n))
                return None
        else:
            default_target = self.targets[0][0]
            return default_target

    @memoized_property
    def metrics_groups(self):
        """
        returns list of unique metric groups for all targets
        """
        try:
            metrics = []
            for target in self.targets:
                metrics += set(self.get_target_metrics(target[1]).keys())
            metrics_groups = []
            for metric in metrics:
                group = metric.split(':')[0].split('_')[0]
                if group == 'custom':
                    # looking for telegraf metrics
                    try:
                        if metric.split(':')[1].split('_')[0].startswith('cpu-cpu'):
                            group = 'CPU'
                        elif metric.split(':')[1].split('_')[0].startswith('diskio-'):
                            group = 'Disk'
                        elif metric.split(':')[1].split('_')[0].startswith('net-'):
                            group = 'Net'
                    except:
                        logging.exception('')
                metrics_groups.append(group)
            return ['Агрегаты'] + sorted(set(metrics_groups))
        except:
            logging.exception('Could not get metrics_groups for job {} due to:'.format(self.job_obj.n))
            return None

    @property
    def default_metrics_group(self):
        try:
            default_metrics_group = self.metrics_groups[0]
            return default_metrics_group
        except:
            logging.exception('Could not get default_metrics_group for job {} due to:'.format(self.job_obj.n))
            return None

    def get_target_metrics(self, target):
        """
        memcached for all monitoring plots for this job
        returns dict where metric codes are keys, and ids are values
        :target: Server number or 'Server.host' if the Job is new (verbose monitoring table)
        """
        try:
            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}'
                '''
            query_params = self.job_obj.basic_query_params.copy()
            query_params['target'] = target
            metric_ids = self.ch_client.select(sql, query_params=query_params)
            if metric_ids:
                sql = '''select code, id
                        from monitoring_metric
                        where code in %(metrics)s'''
                cursor = connections['default'].cursor()
                cursor.execute(sql, {'metrics': tuple('"{}"'.format(i[0]) for i in metric_ids)})
                fetched_data = cursor.fetchall()
                cursor.close()
                metrics = dict(fetched_data)
            else:
                metrics = {}
            return metrics
        except:
            logging.exception('Could not get monitoring metrics for job {} due to:'.format(self.job_obj.n))
            return {}
