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

@author: noob
"""

from api.views.jobclose import JobClose
from common.models import Job, Server, Project, DeletedJob, LoadScheme
from common.util.clients import ClickhouseClient
from common.util import uncoolify
from common.views import get_common
from common.util.decorators import Memoize, approve_required
from django.contrib.auth.decorators import login_required
from django.contrib.auth.models import User
from django.core.exceptions import ObjectDoesNotExist
from django.db import connections
from django.http import HttpResponseRedirect, HttpResponseBadRequest, HttpResponse, HttpRequest
from django.shortcuts import render_to_response, render
from django.template import RequestContext
from monitoring.models import JobMetricTarget
from social_django.models import UserSocialAuth
import logging
import json


# @login_required # let offlinepage be viewed by unauthorized users
def offlinepage(request, job):
    """

    :param request: HTTP request
    :param job: Job NUMBER
    """
    user = request.user
    common_data = get_common(request, user.id)

    job = uncoolify(job)

    try:
        job_obj = Job.check_job(Job.objects.get(n=job))

    except Job.Deleted:
        return HttpResponse(json.dumps([{'success': 0, 'msg': 'Job had been deleted'}]), content_type='application/json')
    except Job.DoesNotExist:
        return render_to_response('error.html', {'common': common_data, 'error': 'no_job'},
                                  RequestContext(request))
    if not job_obj.td:
        return HttpResponseRedirect("/online/%s" % job_obj.n)
    try:
        navfire_a = []
        loadscheme = job_obj.loadscheme_set.all()
        scheme = []
        if loadscheme:
            loadscheme = loadscheme.order_by('load_from')
            load_from = loadscheme[0].load_from
            load_to = loadscheme[loadscheme.count() - 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)

        owner = bool(user == job_obj.user)

        return render_to_response('offlinepage.html', {'user': user,
                                                       'common': common_data,
                                                       'job': job_obj,
                                                       'navfire': navfire_a,
                                                       'schema': scheme,
                                                       'load_from': load_from,
                                                       'load_to': load_to,
                                                       'net_errors': metainfo.net_errors,
                                                       'http_codes': metainfo.http_codes,
                                                       'user_avatar_link': metainfo.avatar_link,
                                                       'user_profile_link': metainfo.profile_link,
                                                       'multitag': job_obj.multitag,
                                                       'owner': owner,
                                                       }, RequestContext(request))
    except:
        logging.exception('Could not render offlinepage for job %s due to:', job)
        return render_to_response('error.html', {'common': common_data, 'error': 'no_job'},
                                  RequestContext(request))


# @login_required
def render_custom_report_form(request):
    try:
        job = request.GET.get('job')
        assert job
        job_obj = Job.check_job(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 Job.Deleted:
        return HttpResponse(json.dumps([{'success': 0, 'msg': 'Job had been deleted'}]), content_type='application/json')
    except (Job.DoesNotExist, AssertionError):
        return HttpResponseBadRequest('invalid job specified')


@login_required
@approve_required
def job_edit(request, job):
    """

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

    job = uncoolify(job)

    try:
        job_obj = Job.check_job(Job.objects.get(n=job))

    except Job.Deleted:
        return HttpResponse(json.dumps([{'success': 0, 'msg': 'Job had been deleted'}]), content_type='application/json')
    except Job.DoesNotExist:
        return render_to_response('error.html', {'common': common, 'error': 'no_job'},
                                  RequestContext(request))

    if request.method == 'GET':

        projects = Project.objects.filter(owner=user.username)

        return render_to_response('edit.html', {'common': common,
                                                'user': user,
                                                'job': job_obj,
                                                'projects': projects,
                                                }, RequestContext(request))
    elif request.method == 'POST':
        logging.debug('JOB %s EDIT POST %s', job, request.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', 0)
            job_obj.instances = request.POST.get('instances_new', 0)
            try:
                srv_new = Server.objects.get(n=request.POST.get('srv_new'))
                job_obj.srv = srv_new
            except ObjectDoesNotExist:
                logging.exception('No such Server %s: \n', request.POST.get('srv_new'))
            job_obj.save()
            logging.info('Successfully updated job %s', job)

        except:
            logging.exception('Could not submit job %s editing, due to:', job)

        return HttpResponseRedirect('/%s' % job_obj.n)


@login_required
@approve_required
def addtofav(request):
    job_n = request.POST.get('job_id')
    try:
        assert job_n
        job_obj = Job.check_job(Job.objects.get(n=job_n))

    except Job.Deleted:
        return HttpResponse(json.dumps([{'success': 0, 'msg': 'Job had been deleted'}]), content_type='application/json')
    except (AssertionError, Job.DoesNotExist, ValueError):
        return HttpResponseBadRequest('Invalid job')
    job_obj.flag = 0 if job_obj.flag else 1
    job_obj.save()
    return HttpResponse(json.dumps({'status': 1, 'error': ''}), content_type='application/json')


@login_required
@approve_required
def deletejob(request):
    job_n = request.POST.get('job_id')
    job = Job.objects.filter(n=job_n)
    if not job:
        return HttpResponse(json.dumps({'status': 0, 'error': 'There is no such job!'}),
                            content_type='application/json')
    if job[0].is_deleted:
        return HttpResponse(json.dumps({'status': 0, 'error': 'Job had been deleted'}), content_type='application/json')
    if not job[0].user == request.user:
        return HttpResponse(json.dumps({'status': 0, 'error': "It's not your job!"}), content_type='application/json')
    assert _delete_job(job[0], user=request.user.username)
    return HttpResponse(json.dumps({'status': 1, 'error': ''}), content_type='application/json')


def _delete_job(job, user='overload'):
    """

    :param job: Job OBJECT
    """
    try:
        if not job.td:  # close online job before deleting;
            request = HttpRequest()
            request.method = 'get'
            user_overload = User.objects.get(username=user)
            request.user = user_overload
            riper = JobClose()
            riper.get(request, job)

        d = DeletedJob(n=job.n,
                       fd=job.fd,
                       td=job.td,
                       user=job.user,
                       name=job.name,
                       dsc=job.dsc,
                       type=job.type,
                       tank=job.tank,
                       command_line=job.command_line,
                       ammo_path=job.ammo_path,
                       loop_cnt=job.loop_cnt,
                       quit_status=job.quit_status,
                       test_type=job.test_type,
                       srv=job.srv,
                       srv_port=job.srv_port,
                       instances=job.instances,
                       flag=job.flag,
                       ver=job.ver,
                       configinfo=job.configinfo,
                       finalized=job.finalized)
        d.save()

        cursor = connections['default'].cursor()
        sql = '''
            delete from common_tankuseragent where job_id=%(job)s;
            delete from monitoring_jobmetrictarget where job_id=%(job)s;
        '''
        cursor.execute(sql, {'job': job.n})
        cursor.close()

        LoadScheme.objects.filter(up=job).delete()

        job.delete()

        return 1
    except:
        logging.exception('')
        return 0


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

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

    @property
    def avatar_link(self):
        try:
            user_extras = UserSocialAuth.objects.get(user_id=self.job_obj.user.id)
            if user_extras.provider == 'github':
                return 'https://avatars.githubusercontent.com/u/%s' % user_extras.uid
            elif user_extras.provider == 'yandex-oauth2':
                return 'https://avatars.yandex.net/get-yapic/%s/islands-150' % user_extras.uid
        except ObjectDoesNotExist:
            return ''
        except:
            logging.exception('')
            return ''

    @property
    def profile_link(self):
        try:
            user_extras = UserSocialAuth.objects.get(user_id=self.job_obj.user.id)
            if user_extras.provider == 'github' and user_extras.extra_data:
                return 'https://github.com/%s' % user_extras.extra_data.get('login', '')
        except ObjectDoesNotExist:
            return ''
        except Exception as exc:
            logging.error('profile_link: ' + repr(exc))
            return ''

    @property
    @Memoize
    def http_codes(self):
        """
        returns dict of http codes and their presence
        """
        try:
            sql = '''select distinct code
                    from loaddb.proto_codes_buffer
                    where job_id=%(job)s
                    and tag='' '''
            data = self.ch_client.select(sql, query_params=self.job_obj.basic_query_params.copy())
            http_codes = [value[0] / 100 for value in data]
            logging.debug("Got http_codes job %s %s", self.job_obj.n, http_codes)
            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 %s due to:", self.job_obj.n)
            return {2: False,
                    3: False,
                    4: False,
                    5: False}

    @property
    @Memoize
    def net_errors(self):
        """
        tells if there were net errors during the job
        """
        try:
            sql = '''select distinct code
                    from loaddb.net_codes_buffer
                    where job_id=%(job)s
                    '''
            data = self.ch_client.select(sql, query_params=self.job_obj.basic_query_params.copy())
            logging.info("Got net_codes for job %s", self.job_obj.n)
            net_codes = [value[0] for value in data]
            return net_codes != [0]
        except:
            logging.exception("Could not get net_codes for job %s due to:", self.job_obj.n)
            return False
