# -*- coding: utf-8 -*-
"""
Created on Feb 24, 2014

@author: noob
"""

import itertools
import logging
import re
from json import dumps

from django.http import HttpResponse

from common.util.meta import escape_string
from common.util.clients import ClickhouseClient, CacheClient
from common.util.decorators import memoized_property
from ..util import get_job_objects


def get_layout(request):
    """

    :param request: HTTP request
    """
    jobs = request.GET.get('jobs', '').split(',')
    job_objects = get_job_objects(jobs)
    cache = CacheClient()
    cache_key = 'jobs_{}_comparelayout'.format([
        ','.join(str(job.n) for job in sorted(job_objects, key=lambda j: j.n))])
    try:
        layout = cache.get(cache_key)
        assert layout
    except AssertionError:
        layout = ComparePageLayout(job_objects).layout
        if layout:
            cache.set(cache_key, layout)

    return HttpResponse(dumps(layout), content_type='application/json')


class ComparePageLayout(object):
    def __init__(self, jobs):
        """

        :param jobs: Job OBJECTS
        """
        # input params
        self.jobs = jobs

    @property
    def layout(self):
        try:
            layout = [
                {
                    'name': 'meta_info',
                    'title': 'Мета информация',
                    'controls': [
                        {
                            'name': 'helpers',
                            'helpers': list(zip(
                                ['all'] + ['{}|{}|{}'.format(h['start'], h['end'], h['name'].replace(' ', '')) for h in
                                           self.helpers], ['Весь тест'] + [h['title'] for h in self.helpers]))
                        },
                    ],
                }
            ]

            if not all(j.monitoring_only for j in self.jobs):
                layout.insert(0, {'name': 'test_data',
                                  'title': 'Данные теста',
                                  'controls': [
                                      {
                                          'name': 'helpers',
                                          'helpers': list(zip(
                                              ['all'] + ['{}|{}|{}'
                                                         .format(h['start'], h['end'], h['name'].replace(' ', ''))
                                                         for h in self.helpers],
                                              ['Весь тест'] + [h['title'] for h in self.helpers]))
                                      },
                                      {
                                          'name': 'plot_groups',
                                          'default': 'main',
                                          'values': (
                                              ('additional', 'Распределения и сводные'),
                                              ('main', 'Обзор теста'),
                                              ('extended', 'Расширенный анализ'),
                                              ('tables', 'Таблицы'),
                                          )
                                      },
                                      {
                                          'name': 'cases',
                                          'values': list(zip([''] + self.tags, ['Все'] + self.tags))
                                      },
                                  ]
                                  })

            if self.targets:
                layout.insert(-1,
                              {'name': 'monitoring',
                               'title': 'Мониторинг',
                               'controls': [
                                   {'name': 'helpers',
                                    'helpers': list(zip(
                                        ['all'] + ['{}|{}|{}'.format(h['start'], h['end'], h['name'].replace(' ', ''))
                                                   for h in self.helpers],
                                        ['Весь тест'] + [h['title'] for h in self.helpers]))
                                    },
                                   {'name': 'targets',
                                    'default': self.targets[0].n,
                                    'values': list(zip((str(target.n) for target in self.targets),
                                                  (target.host for target in self.targets)))
                                    },
                                   {
                                       'name': 'metric_groups',
                                       'values': list(zip(self.metric_groups + ['aggregates'],
                                                     self.metric_groups + ['Агрегаты']))
                                   },
                               ]
                               }
                              )
        except:
            logging.exception('Could not get comparepage layout for jobs {} due to:'
                              .format([job.n for job in self.jobs]))
            layout = []
        return layout

    def get_config_helpers(self):
        """

        #FIXME: INACTIVE YET

        warmup
        cooldown
        main_part
        from meta section in job configinfo
        """
        helpers = []
        warmups = []
        cooldowns = []
        for job in self.jobs:
            warmups.append(job.config.get('uploader', {}).get('meta', {}).get('warmup'))
            cooldowns.append(job.config.get('uploader', {}).get('meta', {}).get('cooldown'))

        if any(warmups):
            helpers.append({
                'name': 'warmup',
                'title': 'Прогрев',
                'start': '',
                'end': '',
            })
        if any(warmups) or any(cooldowns):
            helpers.append({
                'name': 'main_part',
                'title': 'Основной участок',
                'start': '',
                'end': '',
            })
        if any(cooldowns):
            helpers.append({
                'name': 'cooldown',
                'title': 'Кулдаун',
                'start': '',
                'end': '',
            })
        return helpers

    @staticmethod
    def get_loadscheme_helpers(loadschemes):
        ls_helpers = []
        step_ls = []  # Хелперы для целых ступенчатых участков, указанных в схеме нагрузки.
        i = 0
        while i < len(loadschemes):
            loadscheme = loadschemes[i]
            if loadscheme.dsc.startswith('step'):
                try:
                    step_params = [
                        int(re.sub('[^0-9]', '', p))  # keep digits only
                        for p in loadscheme.dsc.split(',')
                    ]
                    # Обработка крайне редкого случая,
                    # когда в схеме могут быть указаны два одинаковых ступенчатых участка.
                    possible_step_count = ((step_params[1] - step_params[0]) / step_params[
                        2]) + 1  # (final rps minus initial rps) divided by leap + 1
                    st_ls = next((item for item in step_ls if
                             item['dsc'] == loadscheme.dsc and item['step_count'] < possible_step_count))
                    st_ls['end'] = loadscheme.sec_to - 1  # reset end of helper
                    st_ls['step_count'] += 1
                except StopIteration:
                    step_ls.append({
                        'step_count': 1,
                        'position': len(step_ls) + i,
                        # ступенчатые участки, которые уже учтены,
                        # будут вставлены в общий список хелперов, смещая индекс.
                        'dsc': loadscheme.dsc,
                        'start': loadscheme.sec_from,
                        'end': loadscheme.sec_to - 1,
                    })
                ls_helpers.append({
                    'name': str(int(loadscheme.load_from)),
                    'title': str(int(loadscheme.load_from)),
                    'start': loadscheme.sec_from,
                    'end': loadscheme.sec_to - 1,
                })
            else:
                ls_helpers.append({
                    'name': loadscheme.dsc,
                    'title': loadscheme.dsc,
                    'start': loadscheme.sec_from,
                    'end': loadscheme.sec_to - 1,
                })
            i += 1
        # Вставляем хелпер для всего ступенчатого участка перед хелперами для отдельных ступенек из этого участка.
        for ls in step_ls:
            ls_helpers.insert(ls['position'], {
                'name': ls['dsc'],
                'title': ls['dsc'],
                'start': ls['start'],
                'end': ls['end'],
            })
        return ls_helpers

    @memoized_property
    def common_loadschemes(self):
        loadschemes_dict = {job.n: job.loadschemes for job in self.jobs}
        loadschemes = list(loadschemes_dict.values())
        common_loadschemes = []
        for scheme in itertools.chain(*loadschemes):
            if (scheme.dsc, scheme.sec_from) not in ((l.dsc, l.sec_from) for l in common_loadschemes) \
                    and all(
                        [(scheme.dsc, scheme.sec_from) in [(l.dsc, l.sec_from) for l in j_ls] for j_ls in loadschemes]):
                common_loadschemes.append(scheme)
        return sorted(common_loadschemes, key=lambda s: s.sec_from)

    @memoized_property
    def helpers(self):
        """
        returns list of common helper names
        """
        try:
            helpers = self.get_loadscheme_helpers(self.common_loadschemes)  # + self.get_config_helpers()
            try:
                # https://st.yandex-team.ru/LUNAPARK-2379
                uncommon_step_loadschemes_dict = {job.n: [ls
                                                          for ls in job.loadschemes
                                                          if ls.dsc.startswith('step')
                                                          and ls not in self.common_loadschemes
                                                          ]
                                                  for job in self.jobs}
                steps_dict = {job.n: [ls
                                      for ls in self.get_loadscheme_helpers(uncommon_step_loadschemes_dict[job.n])
                                      if ls['name'].isdigit()
                                      ]
                              for job in self.jobs}
                steps = []
                for step in itertools.chain(*list(steps_dict.values())):
                    if all([(step['start'], step['end'], step['name'])
                            in [(s['start'], s['end'], s['name'])
                                for s in steps_dict[job.n]
                                ]
                            for job in self.jobs]):
                        steps.append(step)
                helpers.extend(list({obj['name']: obj for obj in steps}.values()))  # making steps unique
            except:
                logging.exception('')
            return sorted(helpers, key=lambda h: h['start'])
        except:
            logging.exception('Could not get common helpers for jobs {} due to:'.format([job.n for job in self.jobs]))
            return []

    @memoized_property
    def tags(self):
        """
        returns list of cases disjunction
        """
        # TODO: multitags
        try:
            cases = [set(job.cases) for job in self.jobs]
            all_cases = cases.pop(0)
            while cases:
                all_cases |= cases.pop(0)
            all_cases = sorted(all_cases)
            return all_cases
        except:
            logging.exception("Could not get common cases for jobs {} due to:".format([job.n for job in self.jobs]))
            return []

    @memoized_property
    def targets(self):
        """
        returns list of common target OBJECTS
        """
        try:
            targets = [set(job.targets) for job in [job for job in self.jobs if job.monitoring_exists]]
            assert targets
            common_targets = targets.pop(0)
            while targets:
                common_targets &= targets.pop(0)
            common_targets = list(common_targets)
            return sorted(common_targets)
        except AssertionError:
            return []
        except:
            logging.exception('Could not get common targets for jobs {} due to:'.format([job.n for job in self.jobs]))
            return []

    @property
    def metrics(self):
        """
        returns list of common metric CODES
        gets metric_ids from clickhouse and their codes from mysql
        """
        metrics = []
        ch_client = ClickhouseClient()
        for job in [job for job in self.jobs if job.monitoring_exists]:
            query_params = job.basic_query_params.copy()
            sql = '''select distinct metric_name
                    from loaddb.monitoring_verbose_data_buffer
                    where job_id={job} 
                    and job_date=toDate({job_date})
                    '''
            try:
                assert (len(self.targets) > 1)
                sql += 'and target_host in ({targets})'
                query_params['targets'] = ','.join("'{}'".format(escape_string(target.host))
                                                   for target in self.targets)
            except AssertionError:
                sql += "and target_host = '{target}'"
                query_params['target'] = self.targets[0].host
            metrics.append([m[0] for m in ch_client.select(sql, query_params=query_params)])
        common_metrics = set(metrics.pop(0))
        while metrics:
            common_metrics &= set(metrics.pop(0))

        return common_metrics

    @memoized_property
    def metric_groups(self):
        """
        returns list of common metric_groups
        """
        common_metric_groups = list(set(metric.split(':')[0].split('_')[0] for metric in self.metrics))
        logging.error('Common metric_groups for jobs {} are {}'
                      .format([job.n for job in self.jobs], common_metric_groups))
        return sorted(common_metric_groups)
