import json
import logging

from rest_framework import viewsets, response
from rest_framework.renderers import JSONRenderer

from api.util.config import transform_config, extract_tanks
from api.views.v2.serializers import AmmoSerializer, ServerSerializer, TaskSerializer, \
    ComponentSerializer, KpiSerializer, RegressionCommentSerializer, JobSerializer, MobileJobSerializer
from common.models import Ammo, Server, Task, Component, KPI, RegressionComment, Job, MobileJobDataKey
from mobilepage.models import MobileJob


class NoDeleteViewSet(viewsets.ModelViewSet):

    def delete(self):
        return response.Response(status=403, data='Sorry, no deletes.')


class AmmoViewSet(viewsets.ReadOnlyModelViewSet):
    queryset = Ammo.objects.all().order_by('id')
    serializer_class = AmmoSerializer


class ServerViewSet(viewsets.ReadOnlyModelViewSet):
    serializer_class = ServerSerializer

    def get_queryset(self):
        queryset = Server.objects.all().order_by('host')

        for q_key in self.request.query_params:
            if q_key == 'host':
                queryset = Server.objects.filter(host=self.request.query_params[q_key]).order_by('n')
            elif q_key == 'host__startswith':
                queryset = Server.objects.filter(host__startswith=self.request.query_params[q_key]).order_by('n')
            elif q_key == 'host__regex':
                queryset = Server.objects.filter(host__regex=self.request.query_params[q_key]).order_by('n')
        return queryset


class TaskViewSet(viewsets.ReadOnlyModelViewSet):
    serializer_class = TaskSerializer

    def get_queryset(self):
        queryset = Task.objects.all().order_by('n')
        for q_key in self.request.query_params:
            if q_key == 'key':
                queryset = Task.objects.filter(key=self.request.query_params[q_key]).order_by('n')
            elif q_key == 'key__startswith':
                queryset = Task.objects.filter(key__startswith=self.request.query_params[q_key]).order_by('n')
        return queryset


class ComponentViewSet(viewsets.ReadOnlyModelViewSet):
    serializer_class = ComponentSerializer

    def get_queryset(self):
        queryset = Component.objects.all().order_by('n')
        for q_key in self.request.query_params:
            if q_key == 'name':
                queryset = Component.objects.filter(name=self.request.query_params[q_key]).order_by('n')
            elif q_key == 'name__startswith':
                queryset = Component.objects.filter(name__startswith=self.request.query_params[q_key]).order_by('n')
        return queryset


class KpiViewSet(viewsets.ReadOnlyModelViewSet):
    serializer_class = KpiSerializer

    def get_queryset(self):
        queryset = KPI.objects.all().order_by('n')
        filters = {q_key: self.request.query_params[q_key] for q_key in self.request.query_params}
        if filters:
            queryset = KPI.objects.filter(**filters).order_by('n')

        return queryset


class RegressionCommentViewSet(NoDeleteViewSet):
    serializer_class = RegressionCommentSerializer

    def get_queryset(self):
        queryset = RegressionComment.objects.all().order_by('id')

        for q_key in self.request.query_params:
            if q_key == 'author':
                queryset = RegressionComment.objects.filter(author=self.request.query_params[q_key]).order_by('id')
            elif q_key == 'author__startswith':
                queryset = RegressionComment.objects.filter(author__startswith=self.request.query_params[q_key])\
                    .order_by('id')
        return queryset


class JobViewSet(NoDeleteViewSet):
    serializer_class = JobSerializer
    renderer_classes = [JSONRenderer]

    def get_queryset(self):
        filters = {
            q_key: self.request.query_params[q_key]
            for q_key in self.request.query_params
            if q_key not in ['order_by', 'limit']
        }
        if 'order_by' in self.request.query_params:
            queryset = Job.objects.filter(**filters).order_by(self.request.query_params['order_by'])
        else:
            queryset = Job.objects.filter(**filters).order_by('n')
        return queryset

    def create(self, request, *args, **kwargs):
        try:
            request_data = request.body.decode('utf-8')
            if not request_data:
                return response.Response(status=400, data='Config is empty')

            parsed_config, tanks = transform_config(request_data)
            job = Job(**parsed_config)
            job.save()

            if not tanks:
                logging.warning('Failed to extract tanks from config %s', request_data)
            else:
                extract_tanks(tanks, job.n)
            logging.info('Job %s created via API', job.n)
            json_data = JobSerializer(job).data
            return response.Response(data=json_data, content_type='application/json')
        except Exception:
            logging.error('Failed to create job', exc_info=True)
            return response.Response(status=400)

    def update(self, request, *args, **kwargs):
        config_initial = request.body.get('config')
        if not config_initial:
            return response.Response(status=400, data='Config is empty')
        try:
            job_id = request.path.split('/')[-2]
            job = Job.objects.get(n=job_id)
            parsed_config, tanks = transform_config(config_initial)
            job.save(**parsed_config)
            if not tanks:
                logging.warning('Failed to extract tanks from config %s', config_initial)
            else:
                extract_tanks(tanks, job.n)
            logging.info('Job %s updated via API', job.n)
            return response.Response(data=job)
        except Exception:
            logging.error('Failed to update job', exc_info=True)

    def partial_update(self, request, *args, **kwargs):
        try:
            job_id = request.path.split('/')[-2]
            job = Job.objects.get(n=job_id)
            data = json.loads(request.body.decode('utf-8'))

            if 'status' in data:
                if not job.td:
                    job.status = data['status']
                    job.save()
                    return response.Response(status=206)
                else:
                    return response.Response(data='Job gone offline', status=410)
            return response.Response()
        except Exception:
            logging.error('Problems with job PATCH request', exc_info=True)
            return response.Response(data=request.body, status=400)


class MobileJobViewSet(viewsets.ReadOnlyModelViewSet):
    serializer_class = MobileJobSerializer

    def get_queryset(self):
        queryset = MobileJob.objects.all().order_by('n')
        filters = {}
        for q_key in self.request.query_params:
            if q_key == 'n':
                filters['n'] = self.request.query_params[q_key]
            if q_key == 'person':
                filters['person'] = self.request.query_params[q_key]
            if q_key == 'task':
                filters['task'] = self.request.query_params[q_key]
            if q_key == 'td':
                filters['td__isnull'] = self.request.query_params[q_key]
            if q_key == 'test_id':
                mdk = MobileJobDataKey.objects.filter(
                    mobile_data_key__in=[self.request.query_params[q_key], filters.get('n', 0)])
                if mdk.count():
                    filters['job'] = mdk[0].job.n
        if filters:
            queryset = MobileJob.objects.filter(**filters)
        return queryset


