import requests
from django.http import Http404
from rest_framework import viewsets
from rest_framework.decorators import action
from rest_framework.response import Response
from rest_framework.status import HTTP_201_CREATED
from rest_framework.viewsets import GenericViewSet
from six.moves.urllib.parse import urljoin

from .utils.portal import registerfacility
from kolibri.core.auth.models import Facility
from kolibri.utils import conf


class KolibriDataPortalViewSet(viewsets.ViewSet):
    @action(detail=False, methods=["post"])
    def register(self, request):
        facility = Facility.objects.get(id=request.data.get("facility_id"))
        try:
            response = registerfacility(request.data.get("token"), facility)
        except requests.exceptions.RequestException as e:  # bubble up any response error
            return Response(e.response.json(), status=e.response.status_code)
        return Response(status=response.status_code)

    @action(detail=False, methods=["get"])
    def validate_token(self, request):
        PORTAL_URL = conf.OPTIONS["Urls"]["DATA_PORTAL_SYNCING_BASE_URL"]
        # token is in query params
        response = requests.get(
            urljoin(PORTAL_URL, "portal/api/public/v1/registerfacility/validate_token"),
            params=request.query_params,
        )
        return Response(response.text, status=response.status_code)


class ValuesViewset(GenericViewSet):
    """
    A viewset that uses a values call to get all model/queryset data in
    a single database query, rather than delegating serialization to a
    DRF ModelSerializer. At the moment, this is read only.
    """

    # A tuple of values to get from the queryset
    values = None
    # A map of target_key, source_key where target_key is the final target_key that will be set
    # and source_key is the key on the object retrieved from the values call.
    field_map = {}

    def __init__(self, *args, **kwargs):
        viewset = super(ValuesViewset, self).__init__(*args, **kwargs)
        if not isinstance(self.values, tuple):
            raise TypeError("values must be defined as a tuple")
        self._values = tuple(self.values)
        if not isinstance(self.field_map, dict):
            raise TypeError("field_map must be defined as a dict")
        self._field_map = self.field_map.copy()
        return viewset

    def annotate_queryset(self, queryset):
        return queryset

    def prefetch_queryset(self, queryset):
        return queryset

    def _map_fields(self, item):
        for key, value in self._field_map.iteritems():
            if callable(value):
                item[key] = value(item)
            elif value in item:
                item[key] = item.pop(value)
            else:
                item[key] = value
        return item

    def consolidate(self, items):
        return items

    def _serialize_queryset(self, queryset):
        queryset = self.annotate_queryset(queryset)
        return queryset.values(*self._values)

    def serialize(self, queryset):
        return self.consolidate(
            map(self._map_fields, self._serialize_queryset(queryset) or [])
        )

    def list(self, request, *args, **kwargs):
        queryset = self.filter_queryset(self.prefetch_queryset(self.get_queryset()))

        page = self.paginate_queryset(queryset)
        if page is not None:
            return self.get_paginated_response(self.serialize(page))

        return Response(self.serialize(queryset))

    def serialize_object(self, pk):
        queryset = self.filter_queryset(self.prefetch_queryset(self.get_queryset()))
        try:
            return self.consolidate(
                self._map_fields(self._serialize_queryset(queryset).filter(pk=pk))
            )[0]
        except IndexError:
            raise Http404(
                "No %s matches the given query." % queryset.model._meta.object_name
            )

    def retrieve(self, request, pk, *args, **kwargs):
        return Response(self.serialize_object(pk))

    def create(self, request, *args, **kwargs):
        serializer = self.get_serializer(data=request.data)
        serializer.is_valid(raise_exception=True)
        instance = serializer.save()
        return Response(self.serialize_object(instance.id), status=HTTP_201_CREATED)

    def update(self, request, *args, **kwargs):
        partial = kwargs.pop("partial", False)
        instance = self.get_object()
        serializer = self.get_serializer(instance, data=request.data, partial=partial)
        serializer.is_valid(raise_exception=True)
        self.perform_update(serializer)

        return Response(self.serialize_object(instance.id))

@@ -222,59 +222,8 @@
 TED%0A
-from rest_framework.viewsets import GenericViewSet%0A
 from
@@ -1357,15 +1357,22 @@
 set(
-Generic
+viewsets.Model
 View
@@ -3603,78 +3603,17 @@
 elf.
-consolidate(%0A                self._map_fields(self._serialize_queryset
+serialize
 (que
@@ -3617,17 +3617,16 @@
 queryset
-)
 .filter(
@@ -3632,30 +3632,16 @@
 (pk=pk))
-%0A            )
 %5B0%5D%0A    
@@ -4045,32 +4045,132 @@
 exception=True)%0A
+        self.perform_create(serializer)%0A        headers = self.get_success_headers(serializer.data)%0A
         instance
@@ -4187,14 +4187,16 @@
 zer.
-save()
+instance
 %0A   
@@ -4208,32 +4208,45 @@
 return Response(
+%0A            
 self.serialize_o
@@ -4288,16 +4288,42 @@
 _CREATED
+, headers=headers%0A        
 )%0A%0A    d

Update valuesviewset to handle PATCH.