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

@author: noob
"""

import logging

import requests
from django.contrib.auth.models import User
from django.core.exceptions import ObjectDoesNotExist
from django.http import HttpResponseRedirect, HttpResponseBadRequest
from django.shortcuts import render_to_response, render
from django.template import RequestContext
from django.views.decorators.csrf import csrf_exempt
from django_yauth.decorators import yalogin_required
from comparepage.models import CompareJobsManager
from common.models import Job, JobImbalance, JobTrail, \
    Component, Task, Server, Artifact, \
    RegressionComment, TankUserAgent, MobileJobDataKey
from common.util.clients import ClickhouseClient, link_and_create_task, TaskError
from common.util.decorators import memoized_property
from common.views import get_common
from monitoring.models import JobMetricTarget


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

    :param request: HTTP request
    :param job: Job NUMBER
    """
    user = request.yauser
    common_data = get_common(request)
    try:
        job_obj = Job.objects.get(n=job)
    except ObjectDoesNotExist:
        return render_to_response('error.html', {'common': common_data, 'error': 'no_job'},
                                  RequestContext(request)
                                  )
    if not job_obj.td:
        return HttpResponseRedirect('/online/{}'.format(job_obj.n))
    task = Task.objects.filter(key=job_obj.task)[0]
    navfire_a = Job.objects.filter(task=job_obj.task).order_by('-n')[:50]
    loadscheme = job_obj.loadschemes
    scheme = []
    if loadscheme:
        loadscheme = sorted(loadscheme, key=lambda s: s.load_from)
        load_from = loadscheme[0].load_from
        load_to = loadscheme[len(loadscheme) - 1].load_to
        prev_dsc = ''
        for ls in loadscheme:
            if not prev_dsc or prev_dsc != ls.dsc:
                scheme.append(ls.dsc)
            prev_dsc = ls.dsc
    else:
        load_from = 0
        load_to = 0
    scheme = ' '.join(scheme) if scheme else ''
    metainfo = Metainfo(job_obj)

    is_in_compare = bool(job_obj.n in CompareJobsManager(user.login).get_jobs())

    jenkinsbuild, jenkinsjob, jenkinsrebuild = get_jenkins_stuff(job_obj)

    return render_to_response('offlinepage.html', {'user': user,
                                                   'common': common_data,
                                                   'job': job_obj,
                                                   'task': task,
                                                   'navfire': navfire_a,
                                                   'schema': scheme,
                                                   'load_from': load_from,
                                                   'load_to': load_to,
                                                   'is_in_compare': is_in_compare,
                                                   'net_errors': metainfo.net_errors,
                                                   'http_codes': metainfo.http_codes,
                                                   'imbalance': metainfo.imbalance,
                                                   'component': metainfo.regress_data(job_obj.component),
                                                   'regression_comments': metainfo.regression_comments,
                                                   'artifacts': get_tankapi_artifacts(job_obj),
                                                   'user_agent': metainfo.user_agent,
                                                   'jenkinsbuild': jenkinsbuild,
                                                   'jenkinsjob': jenkinsjob,
                                                   'jenkinsrebuild': jenkinsrebuild,
                                                   'multitag': job_obj.multitag,
                                                   'has_monitoring': job_obj.monitoring_exists,
                                                   'configinitial': bool(job_obj.configinitial),
                                                   'pagename': 'offlinepage',
                                                   }, RequestContext(request))


def get_tankapi_artifacts(job_obj):
    """
    returns two-tuple of filenames and proxy links
    :param job_obj:
    """
    try:
        config = job_obj.config
        assert job_obj.tankapi_jobno
        # finding tankapi_port
        # TODO: nanny
        meta_section = config.get('meta', {}) or config.get('uploader', {}).get('meta', {})
        tankapi_port = meta_section.get('use_tank_port', '8083')
        url = 'http://{}:{}/api/v1/tests/{}/logs.json'.format(job_obj.tank.host, tankapi_port, job_obj.tankapi_jobno)
        files = requests.get(url, timeout=2)
        files = files.json()['files']
        files = list(zip(files,
                    ['/api/auth_proxy?tank={}&file={}&tajob={}&lpjob={}&source=tankapi'.format(
                        job_obj.tank.host, f, job_obj.tankapi_jobno, job_obj.n)
                     for f in files
                     ]
                    ))
        return files
    except AssertionError:
        return []
    except requests.exceptions.RequestException:
        return []
    except ValueError:
        logging.error('Could not get tankapi artifacts for job {} jobno {}:'.format(job_obj.n, job_obj.tankapi_jobno))
        return []
    except:
        logging.exception('Could not get tankapi artifacts for job {}:'.format(job_obj.n))
        return []


# TODO: Not active yet
def get_db_artifacts(job_obj):
    try:
        artifacts = Artifact.objects.filter(job_id=job_obj.n)
        files = [
            (a.storage_key.split('_', 1)[1], '/api/auth_proxy?artifact={}&source=mds'.format(a.storage_key))
            for a in artifacts]
        return files
    except AssertionError:
        return []
    except:
        logging.exception('Could not get tankapi artfacts for job {}:'.format(job_obj.n))
        return []


def get_jenkins_stuff(job_obj):
    """
    gets values from configinfo
    :param job_obj: Job OBJECT
    """
    stuff = 'jenkinsbuild', 'jenkinsjob', 'jenkinsrebuild'
    retargs = []
    for s in stuff:
        retargs.append(job_obj.config.get('meta', {}).get(s, '') or
                       job_obj.config.get('uploader', {}).get('meta', {}).get(s, ''))
    return tuple(retargs)


@yalogin_required
def render_custom_report_form(request):
    try:
        job = request.GET.get('job')
        assert job
        job_obj = Job.objects.get(n=job)
        custom_metrics = [jmt.metric
                          for jmt in JobMetricTarget(job_obj.n).objects
                          if jmt.metric.startswith('custom:')]
        custom_metrics_single = set(
            metric.split(':')[1]
            for metric in custom_metrics
            if [m.split(':')[1].split('_')[0] for m in custom_metrics].count(metric.split(':')[1].split('_')[0]) == 1
        )
        custom_metrics_groups = set(
            metric.split(':')[1].split('_')[0]
            for metric in custom_metrics
            if [m.split(':')[1].split('_')[0] for m in custom_metrics].count(metric.split(':')[1].split('_')[0]) > 1
        )

        return render(request, 'custom_report_form.html', {
            'job': job_obj.n,
            'custom_metrics_single': custom_metrics_single,
            'custom_metrics_groups': custom_metrics_groups,
            'targets': job_obj.targets,
        })
    except (ObjectDoesNotExist, AssertionError):
        return HttpResponseBadRequest('invalid job specified')


@yalogin_required
@csrf_exempt
def job_edit(request, job):
    """

    :param request: HTTP request
    :param job: Job NUMBER
    """
    user = request.yauser
    common = get_common(request)

    try:
        job_obj = Job.objects.get(n=job)
    except ObjectDoesNotExist:
        return render_to_response('error.html', {'common': common, 'error': 'no_job'},
                                  RequestContext(request)
                                  )

    if request.method == 'GET':

        targets = Server.objects.filter(is_test=0).order_by('host')
        components = Component.objects.filter(tag=job_obj.task.split('-')[0])

        return render_to_response('edit.html', {'common': common,
                                                'user': user,
                                                'job': job_obj,
                                                'targets': targets,
                                                'components': components,
                                                'pagename': 'offlinepage',
                                                }, RequestContext(request))
    elif request.method == 'POST':
        try:
            job_obj.ver = request.POST.get('ver_new')
            job_obj.name = request.POST.get('name_new')
            job_obj.dsc = request.POST.get('dsc_new')
            job_obj.srv_port = request.POST.get('port_new')

            mobile_data_key = request.POST.get('mobile_data_key_new')
            mjdk = MobileJobDataKey.objects.get_or_create(job=job_obj)[0]
            mjdk.mobile_data_key = mobile_data_key
            mjdk.save()

            person_new = request.POST.get('person_new')
            try:
                person_new = User.objects.get(username=person_new)
                job_obj.person = person_new.username
            except ObjectDoesNotExist:
                logging.exception('No such person {}: \n'.format(person_new))
            try:
                task_new = link_and_create_task(request, request.POST.get('task_new'))
                job_obj.task = task_new.key.upper()
            except TaskError:
                logging.exception('No such Task {}: \n'.format(request.POST.get('task_new')))
            try:
                srv_new = Server.objects.get(n=request.POST.get('srv_new'))
                job_obj.srv = srv_new
            except ObjectDoesNotExist:
                logging.exception('No such Server {}: \n'.format(request.POST.get('srv_new')))

            component_new = request.POST.get('component_new')
            try:
                if component_new:
                    component_new = Component.objects.get(n=component_new)
                    component_new = component_new.n
                else:
                    component_new = 0
                job_obj.component = component_new
            except ObjectDoesNotExist:
                logging.exception('No such Component {}: \n'.format(component_new))

            job_obj.save()

        except Exception:
            logging.exception('Could not submit job {} editing, due to:'.format(job))

        return HttpResponseRedirect('/{}'.format(job_obj.n))


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

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

    @memoized_property
    def job_trail(self):
        """
        gets jobtrail object
        """
        try:
            job_trail = JobTrail.objects.get(up=self.job_obj)
            return job_trail
        except ObjectDoesNotExist:
            return None
        except Exception as exc:
            logging.exception('Could not get job_trail for job {} due to:'.format(self.job_obj.n))
            raise exc

    @property
    def http_codes(self):
        """
        returns dict of http codes and their presence
        """
        try:
            assert self.job_trail
            http_codes = [http_code[0] for http_code in self.job_trail.http.split(',') if http_code]
            data = {2: ('2' in http_codes),
                    3: ('3' in http_codes),
                    4: ('4' in http_codes),
                    5: ('5' in http_codes)}
            return data
        except Exception as exc:
            if not isinstance(exc, AssertionError):
                logging.exception('Could not get http_codes job_obj {} due to:'.format(self.job_obj.n))
            try:
                sql = '''SELECT DISTINCT code
                        FROM loaddb.proto_codes_buffer
                        WHERE job_id={job}
                        AND job_date=toDate({job_date})
                        AND tag='' '''
                data = self.ch_client.select(sql, query_params=self.job_obj.basic_query_params)
                http_codes = [value[0] // 100 for value in data]
                data = {2: (2 in http_codes),
                        3: (3 in http_codes),
                        4: (4 in http_codes),
                        5: (5 in http_codes)}
                return data
            except:
                logging.exception('Could not get http_codes for job {} due to:'.format(self.job_obj.n))
                return {2: False,
                        3: False,
                        4: False,
                        5: False}

    @property
    def net_errors(self):
        """
        tells if there were net errors during the job
        """
        try:
            if self.job_trail:
                return self.job_trail.net
            else:
                sql = '''SELECT DISTINCT code
                        FROM loaddb.net_codes_buffer
                        WHERE job_id={job}
                        AND job_date=toDate({job_date})
                        '''
                data = self.ch_client.select(sql, query_params=self.job_obj.basic_query_params)
                net_codes = [value[0] for value in data]
                return net_codes != [0]
        except:
            logging.exception('Could not get net_codes for job {} due to:'.format(self.job_obj.n))
            return False

    @property
    def regression_comments(self):
        return RegressionComment.objects.filter(job=self.job_obj)

    @memoized_property
    def imbalance(self):
        try:
            imbalance = JobImbalance.objects.filter(up=self.job_obj).order_by('rob_isimbalance', '-hum_isimbalance',
                                                                              '-n')[0:1].get()
            return {'rob_isimbalance': imbalance.rob_isimbalance,
                    'rob_imbalance': imbalance.rob_imbalance,
                    'rob_warning_sec': imbalance.rob_warning_sec,
                    'hum_isimbalance': imbalance.hum_isimbalance,
                    'hum_imbalance': imbalance.hum_imbalance,
                    'hum_processed': imbalance.hum_processed,
                    'user': imbalance.user}
        except ObjectDoesNotExist:
            return None

    def regress_data(self, comp_id):
        try:
            comp = Component.objects.filter(n=comp_id)
            if comp:
                return comp[0]
            else:
                return None
        except:
            logging.exception('Could not get component for job {} due to:'.format(self.job_obj.n))
            return None

    @property
    def user_agent(self):
        agents = TankUserAgent.objects.filter(job=self.job_obj)
        if agents:
            return agents[0].user_agent
        else:
            return ''
