# -*- coding: utf-8 -*-
"""
Created on Apr 29, 2013

@author: noob
"""

from collections import namedtuple
from common.models import Ammo, Server, UISettings, Job, PandoraBinary
from common.views import get_common
from common.util.clients import CacheClient
from common.util.decorators import memoized_property
from datetime import datetime, timedelta
from django.contrib.auth.models import User
from django.db import connections
from django.http import HttpResponse, HttpResponseBadRequest
from django.shortcuts import render_to_response
from django.template import RequestContext
from django_yauth.decorators import yalogin_required
from regression.views.sla_processor import ProjectSLAProcessor
from json import dumps
import calendar
import logging
import time
import re


@yalogin_required
def mainpage(request):
    """

    :param request: HTTP request
    """
    user = request.yauser
    common_data = get_common(request)
    try:
        ui_settings = UISettings.objects.filter(person=user.login)
        ui__show_user_jobs = ui_settings.filter(param='show_user_jobs')
        ui__show_user_jobs = ui__show_user_jobs[0].onoff if ui__show_user_jobs else False
        ui__show_tanks = ui_settings.filter(param='show_tanks')
        ui__show_tanks = ui__show_tanks[0].onoff if ui__show_tanks else False

        ui__show_mobile_jobs = ui_settings.filter(param='show_mobile_jobs')
        ui__show_mobile_jobs = ui__show_mobile_jobs[0].onoff if ui__show_mobile_jobs else False
    except:
        ui__show_user_jobs = False
        ui__show_mobile_jobs = False
        ui__show_tanks = True
    user_ammo = Ammo.objects.filter(author=user.login).order_by('-last_used')
    user_pandora_binaries = PandoraBinary.objects.filter(author=user.login).order_by('-last_used')

    return render_to_response(
        'mainpage.html',
        {'common': common_data,
         'pagename': 'mainpage',
         'user': user,
         'user_ammo': user_ammo,
         'user_pandora_binaries': user_pandora_binaries,
         'ui__show_user_jobs': ui__show_user_jobs,
         'ui__show_tanks': ui__show_tanks,
         'ui__show_mobile_jobs': ui__show_mobile_jobs,
         },
        RequestContext(request)
    )


@yalogin_required
def get_logins(request):
    starts_with = request.GET.get('startswith', '')
    logins = [u.username for u in User.objects.filter(username__startswith=starts_with)[:5]] if starts_with else []
    return HttpResponse(dumps(logins), content_type='application/json')


@yalogin_required
def projects_request_handler(request):
    user = request.yauser
    projects = ProjectsProcessor(user)
    sla = {project_obj: ProjectSLAProcessor(project_obj).check() for project_obj in projects.user_active_projects}
    return render_to_response('mainpage_projects_popup.html', {'sla': sla,
                                                               'user_active_projects': projects.user_active_projects,
                                                               'user_old_projects': projects.user_old_projects
                                                               }, RequestContext(request))

@yalogin_required
def stream_jobs(request):
    def shrink_host(host):
        return host.replace('.yandex.ru', '').replace('.yandex.net', '').replace('.yandex-team.ru', '').replace(
            '.yndx.net', '')

    ids = request.GET.get('ids', '')  # job ids
    ids = [i for i in ids.split(',') if i.isdigit()]
    limit = request.GET.get('limit', 100)
    try:
        if int(limit) > 1000:
            limit = 1000
    except ValueError:
        logging.warning("Limit for online jobs passed in GET parameters is not digital. Limit remains 10")
    try:
        assert ids
        jobs_online = Job.objects.filter(n__in=ids).values(
            *['fd', 'person', 'n', 'name', 'task', 'tank', 'srv', 'srv_port', 'status']
        )[:limit]

        resp = []

        for job in jobs_online:
            try:
                tank = shrink_host(Server.objects.get(n=int(job['tank'])).host)
            except (TypeError, Server.DoesNotExist):
                tank = ''
            try:
                target = shrink_host(Server.objects.get(n=int(job['srv'])).host)
            except (TypeError, Server.DoesNotExist):
                target = ''
            try:
                resp.append({'person': job['person'],
                             'n': int(job['n']),
                             'name': job['name'],
                             'task': job['task'],
                             'tank': tank,
                             'started': job['fd'].strftime('%Y-%m-%d %H:%M:%S'),
                             'target': target,
                             'port': job['srv_port'],
                             'status': job['status'],
                             })
            except Exception:
                logging.error('online jobs Job %s error', job['n'], exc_info=True)
                continue
        return HttpResponse(dumps(resp), content_type='application/json')
    except AssertionError:
        return HttpResponse(dumps([]), content_type='application/json')
    except Exception as exc:
        logging.exception('could not retrieve online jobs due to:')
        return HttpResponse(dumps([{'success': 0, 'error': exc.__class__.__name__}]), content_type='application/json')


# AMMO


@yalogin_required
def ammo_share(request):
    """
    Клонирует запись в базе с патронами для того юзера, с кем делятся патронами.
    :param request:
    :return:
    """
    ammo_id = request.POST.get('ammo_id')
    if not ammo_id or not ammo_id.isdigit():
        return HttpResponseBadRequest('Invalid ammo')
    ammo_to_share = Ammo.objects.get(id=ammo_id)

    persons = request.POST.get('persons')
    persons = [p.strip() for p in persons.split(',')]

    for person in persons:
        if User.objects.filter(username=person) and not Ammo.objects.filter(path=ammo_to_share.path, author=person):
            new_ammo = Ammo.objects.create(
                author=person,
                created_at=ammo_to_share.created_at,
                dsc=ammo_to_share.dsc,
                flag=ammo_to_share.flag,
                hidden=0,
                last_used=ammo_to_share.last_used,
                mdsum=ammo_to_share.mdsum,
                path=ammo_to_share.path,
                private=ammo_to_share.private,
                size=ammo_to_share.size,
            )
            new_ammo.save()
    return HttpResponse('')


@yalogin_required
def ammo_action(request):
    ammo_id = request.POST.get('ammo_id')
    action = request.POST.get('action')
    if not ammo_id or not ammo_id.isdigit() or not action:
        return HttpResponseBadRequest('')
    ammo = Ammo.objects.get(id=ammo_id)
    if action == 'flag':
        ammo.flag = 0 if ammo.flag else 1
    elif action == 'hidden':
        ammo.hidden = 0 if ammo.hidden else 1
    elif action == 'private':
        ammo.private = 0 if ammo.private else 1
    ammo.save()
    return HttpResponse('')


@yalogin_required
def ammo_edit_dsc(request):
    ammo_id = request.POST.get('ammo_id')
    description = request.POST.get('description')
    if not ammo_id or not ammo_id.isdigit() or description is None:
        return HttpResponseBadRequest('')
    ammo = Ammo.objects.get(id=ammo_id)
    ammo.dsc = description
    ammo.save()
    return HttpResponse('')


@yalogin_required
def ammo_shared_with(request):
    ammo_path = request.GET.get('path')
    user = request.yauser
    if ammo_path:
        return HttpResponse(dumps([a.author for a in Ammo.objects.filter(path=ammo_path).exclude(author=user.login)]),
                            content_type='application/json')
    else:
        return HttpResponse(dumps([]), content_type='application/json')

# PANDORA


@yalogin_required
def pandora_binary_share(request):
    """
    Клонирует запись в базе для того юзера, с кем делятся патронами.
    :param request:
    :return:
    """
    pandora_binary_id = request.POST.get('pandora_binary_id')
    if not pandora_binary_id or not pandora_binary_id.isdigit():
        return HttpResponseBadRequest('Invalid ammo')
    pandora_binary_to_share = PandoraBinary.objects.get(id=pandora_binary_id)

    persons = request.POST.get('persons')
    persons = [p.strip() for p in persons.split(',')]

    for person in persons:
        if User.objects.filter(username=person) and not PandoraBinary.objects.filter(path=pandora_binary_to_share.path, author=person):
            new_ammo = PandoraBinary.objects.create(
                author=person,
                created_at=pandora_binary_to_share.created_at,
                dsc=pandora_binary_to_share.dsc,
                flag=pandora_binary_to_share.flag,
                hidden=0,
                last_used=pandora_binary_to_share.last_used,
                mdsum=pandora_binary_to_share.mdsum,
                path=pandora_binary_to_share.path,
                private=pandora_binary_to_share.private,
                size=pandora_binary_to_share.size,
            )
            new_ammo.save()
    return HttpResponse('')


@yalogin_required
def pandora_binary_action(request):
    pandora_binary_id = request.POST.get('pandora_binary_id')
    action = request.POST.get('action')
    if not pandora_binary_id or not pandora_binary_id.isdigit() or not action:
        return HttpResponseBadRequest('')
    pandora_binary = PandoraBinary.objects.get(id=pandora_binary_id)
    if action == 'flag':
        pandora_binary.flag = 0 if pandora_binary.flag else 1
    elif action == 'hidden':
        pandora_binary.hidden = 0 if pandora_binary.hidden else 1
    elif action == 'private':
        pandora_binary.private = 0 if pandora_binary.private else 1
    pandora_binary.save()
    return HttpResponse('')


@yalogin_required
def pandora_binary_edit_dsc(request):
    pandora_binary_id = request.POST.get('pandora_binary_id')
    description = request.POST.get('description')
    if not pandora_binary_id or not pandora_binary_id.isdigit() or description is None:
        return HttpResponseBadRequest('')
    pandora_binary = PandoraBinary.objects.get(id=pandora_binary_id)
    pandora_binary.dsc = description
    pandora_binary.save()
    return HttpResponse('')


@yalogin_required
def pandora_binary_shared_with(request):
    pandora_binary_path = request.GET.get('path')
    user = request.yauser
    if pandora_binary_path:
        return HttpResponse(
            dumps(
                [pb.author for pb in PandoraBinary.objects.filter(path=pandora_binary_path).exclude(author=user.login)]
            ),
            content_type='application/json'
        )
    else:
        return HttpResponse(dumps([]), content_type='application/json')


class ProjectsProcessor(object):
    def __init__(self, user):
        """

        :param user:
        """
        self.user = user
        self.now = datetime.now()

    @memoized_property
    def user_projects(self):
        """
        returns list of unique st queues names

        """
        try:
            cache_key = '{}_projects_new2'.format(self.user.login)
            cache = CacheClient()
            cache_data = cache.get(cache_key) or {}

            user_projects_names_known = set(cache_data.get('user_projects_names', set()))  # already known projects
            last_check = cache_data.get('last_check')  # момент поседней проверки проектов

            if last_check:
                last_check = int(last_check)
                last_check = datetime.fromtimestamp(last_check)

            # TODO: выцеплять из стартрека ключи очередей тасков, где юзер исполнитель, подписчик или создатель

            sql_projects_jobs = '''select distinct task
                                    from job
                                    where person=%(login)s '''
            if last_check:
                sql_projects_jobs += 'and fd > %(last_check)s '

            cursor = connections['default'].cursor()
            try:
                cursor.execute(sql_projects_jobs, {'login': self.user.login, 'last_check': last_check})
                user_tasks = cursor.fetchall()
                user_task_queue_keys = set(task[0].split('-')[0]
                                           for task in user_tasks
                                           if re.match(r'[a-zA-Z]+-\d+', task[0]))
                last_check = time.time()
            except:
                logging.exception('')
                user_task_queue_keys = set()
            finally:
                cursor.close()
            user_projects = user_projects_names_known | user_task_queue_keys
            cache.set(cache_key, {'last_check': last_check, 'user_projects_names': list(user_projects)})

            return list(set(user_projects))
        except:
            logging.exception("Could not get %s's projects due to:", self.user.login)
            return []

    def get_project_age(self, project):
        """
        checks if project is old
        returns False even if there are no jobs in project
        :param project: String (startrek queue key)
        """
        try:
            sql = 'select max(fd) from job where task like %(project)s;'
            cursor = connections['default'].cursor()
            try:
                cursor.execute(sql, {'project': '{}-%'.format(project)})
                latest_job_fd = cursor.fetchall()[0][0]
            except:
                logging.exception('')
                latest_job_fd = 0
            finally:
                cursor.close()
            if self.now.month > 1:
                month_ago = self.now - timedelta(days=calendar.monthrange(self.now.year, self.now.month - 1)[1])
            else:
                month_ago = self.now - timedelta(days=31)
            if latest_job_fd and latest_job_fd > month_ago:
                return True
            else:
                return False
        except:
            logging.exception("Could not get project {} age, due to:".format(project.value))
            return False

    @memoized_property
    def user_active_projects(self):
        """
        :return: list of unique st queues names
        """
        try:
            user_active_projects = [project
                                    for project in self.user_projects
                                    if self.get_project_age(project)]
            return sorted(user_active_projects, reverse=False)
        except:
            logging.exception("Could not get {}'s projects due to:".format(self.user.login))
            return []

    @property
    def user_old_projects(self):
        """
        :return: list of unique st queues names
        """
        try:
            user_old_projects = [project for project in self.user_projects if project not in self.user_active_projects]
            return sorted(user_old_projects, reverse=False)
        except:
            logging.exception("Could not get {}'s old projects due to:".format(self.user.login))
            return []
