import binascii
import logging

from django.conf import settings
from requests import HTTPError
from rest_framework import generics, status, viewsets, permissions
from rest_framework.decorators import api_view, permission_classes
from rest_framework.response import Response

import core.models.apartment_sensor_models
import core.models.sensor_models
import core.models.service
from core.utils.elsys import decode_elsys_payload

from . import serializers
from .. import models

log = logging.getLogger(__name__)


class ServiceViewSet(viewsets.ReadOnlyModelViewSet):
    queryset = core.models.service.Service.objects.all()
    serializer_class = serializers.ServiceSerializer


class ApartmentViewSet(viewsets.ReadOnlyModelViewSet):
    """
    Serialize Apartments current authenticated user belongs to.
    """
    serializer_class = serializers.ApartmentSerializer
    queryset = core.models.apartment_sensor_models.Apartment.objects.none()  # For inspection only; get_queryset is used live

    def get_queryset(self):
        return self.request.user.apartments.all()


class ApartmentSensorViewSet(viewsets.ModelViewSet):
    serializer_class = serializers.ApartmentSensorSerializer
    queryset = core.models.apartment_sensor_models.ApartmentSensor.objects.none()  # For inspection only; get_queryset is used live

    def get_queryset(self):
        return core.models.apartment_sensor_models.ApartmentSensor.list_for_user(self.request.user)


class AvailableServicesList(generics.ListAPIView):
    """
    Serialize all services current authenticated user could
    subscribe to considering what sensors are available and what
    requirements services have.
    """
    permission_classes = [permissions.IsAuthenticated]
    serializer_class = serializers.ServiceSerializer

    def get_queryset(self):
        return core.models.service.Service.list_available_for_user(self.request.user)


class SensorViewSet(viewsets.ModelViewSet):
    queryset = core.models.sensor_models.Sensor.objects.all()
    serializer_class = serializers.SensorSerializer


class SensorAttributeViewSet(viewsets.ModelViewSet):
    queryset = core.models.sensor_models.SensorAttribute.objects.all()
    serializer_class = serializers.SensorAttributeSerializer


class AftereffectResponse(Response):
    def __init__(self, *args, **kwargs):
        self.aftereffect = kwargs.pop('aftereffect', None)
        return super().__init__(*args, **kwargs)

    def close(self):
        super().close()
        if self.aftereffect:
            self.aftereffect()


class SubscriptionViewSet(
    viewsets.mixins.ListModelMixin,
    viewsets.mixins.CreateModelMixin,
    viewsets.mixins.DestroyModelMixin,
    viewsets.GenericViewSet,
):
    """
    Serialize all subscriptions and provide methods to create new
    subscriptions and terminate old ones.
    """
    serializer_class = serializers.SubscriptionSerializer
    queryset = models.Subscription.objects.none()  # For inspection only; get_queryset is used live

    serializer_classes = {
        'create': serializers.CreateSubscriptionSerializer
    }

    def get_queryset(self):
        return models.Subscription.objects.filter(user=self.request.user)

    def get_serializer_class(self):
        return self.serializer_classes.get(self.action, self.serializer_class)

    def destroy(self, request, *args, **kwargs):
        instance = self.get_object()

        try:
            instance.delete_in_service()
        except HTTPError:
            return Response('Could not unsubscribe in service.', status=502)

        instance.delete()
        return Response(status=status.HTTP_204_NO_CONTENT)

    def create(self, request, *args, **kwargs):
        serializer = self.get_serializer(data=request.data)
        serializer.is_valid(raise_exception=True)

        try:
            self.perform_create(serializer)
        except HTTPError:
            return Response('Could not register subscription with service.', status=502)

        headers = self.get_success_headers(serializer.data)
        return AftereffectResponse(
            serializer.data, status=status.HTTP_201_CREATED, headers=headers,
            aftereffect=lambda: serializer.submit_history())


class CustomReportSubscriptionViewSet(viewsets.mixins.ListModelMixin, viewsets.GenericViewSet):
    serializer_class = serializers.CustomReportSubscriptionSerializer
    queryset = models.CustomReportSubscription.objects.none()  # For inspection only; get_queryset is used live

    def get_queryset(self):
        return models.CustomReportSubscription.objects.filter(user=self.request.user)


@api_view(['POST'])
@permission_classes([permissions.AllowAny])
def digita_gw(request):
    """
    Digita GW endpoint implementation
    """
    identifier = request.data['DevEUI_uplink']['DevEUI']
    apsen = core.models.apartment_sensor_models.ApartmentSensor.objects.get_or_create(identifier=identifier)[0]
    payload = binascii.unhexlify(request.data['DevEUI_uplink']['payload_hex'])
    decoded_payload = decode_elsys_payload(payload)
    mapping = settings.DIGITA_GW_PAYLOAD_TO_ATTRIBUTES  # type: dict

    new_values = []
    for key, value in decoded_payload.items():
        uri = mapping.get(key, '')
        if uri:
            attr = core.models.sensor_models.SensorAttribute.objects.get_or_create(uri=uri, defaults={'description': key})[0]
        else:
            attr = core.models.sensor_models.SensorAttribute.objects.get_or_create(description=key)[0]
        apsen_attr = apsen.attributes.get_or_create(attribute=attr)[0]
        new_values.append(apsen_attr.values.create(value=value))
    models.Subscription.handle_new_values(new_values)
    return Response({"message": "Updated successfully"})

import binascii
import logging

from django.conf import settings
from requests import HTTPError
from rest_framework import generics, status, viewsets, permissions
from rest_framework.decorators import api_view, permission_classes
from rest_framework.response import Response

import core.models.apartment_sensor_models
import core.models.sensor_models
import core.models.service
from core.utils.elsys import decode_elsys_payload

from . import serializers
from .. import models

log = logging.getLogger(__name__)


class ServiceViewSet(viewsets.ReadOnlyModelViewSet):
    queryset = core.models.service.Service.objects.all()
    serializer_class = serializers.ServiceSerializer


class ApartmentViewSet(viewsets.ReadOnlyModelViewSet):
    """
    Serialize Apartments current authenticated user belongs to.
    """
    serializer_class = serializers.ApartmentSerializer
    queryset = core.models.apartment_sensor_models.Apartment.objects.none()  # For inspection only; get_queryset is used live

    def get_queryset(self):
        return self.request.user.apartments.all()


class ApartmentSensorViewSet(viewsets.ModelViewSet):
    serializer_class = serializers.ApartmentSensorSerializer
    queryset = core.models.apartment_sensor_models.ApartmentSensor.objects.none()  # For inspection only; get_queryset is used live

    def get_queryset(self):
        return core.models.apartment_sensor_models.ApartmentSensor.list_for_user(self.request.user)


class AvailableServicesList(generics.ListAPIView):
    """
    Serialize all services current authenticated user could
    subscribe to considering what sensors are available and what
    requirements services have.
    """
    permission_classes = [permissions.IsAuthenticated]
    serializer_class = serializers.ServiceSerializer

    def get_queryset(self):
        return core.models.service.Service.list_available_for_user(self.request.user)


class SensorViewSet(viewsets.ModelViewSet):
    queryset = core.models.sensor_models.Sensor.objects.all()
    serializer_class = serializers.SensorSerializer


class SensorAttributeViewSet(viewsets.ModelViewSet):
    queryset = core.models.sensor_models.SensorAttribute.objects.all()
    serializer_class = serializers.SensorAttributeSerializer


class AftereffectResponse(Response):
    def __init__(self, *args, **kwargs):
        self.aftereffect = kwargs.pop('aftereffect', None)
        return super().__init__(*args, **kwargs)

    def close(self):
        super().close()
        if self.aftereffect:
            self.aftereffect()


class SubscriptionViewSet(
    viewsets.mixins.ListModelMixin,
    viewsets.mixins.CreateModelMixin,
    viewsets.mixins.DestroyModelMixin,
    viewsets.GenericViewSet,
):
    """
    Serialize all subscriptions and provide methods to create new
    subscriptions and terminate old ones.
    """
    serializer_class = serializers.SubscriptionSerializer
    queryset = models.Subscription.objects.none()  # For inspection only; get_queryset is used live

    serializer_classes = {
        'create': serializers.CreateSubscriptionSerializer
    }

    def get_queryset(self):
        return models.Subscription.objects.filter(user=self.request.user)

    def get_serializer_class(self):
        return self.serializer_classes.get(self.action, self.serializer_class)

    def destroy(self, request, *args, **kwargs):
        instance = self.get_object()

        try:
            instance.delete_in_service()
        except HTTPError:
            return Response('Could not unsubscribe in service.', status=502)

        instance.delete()
        return Response(status=status.HTTP_204_NO_CONTENT)

    def create(self, request, *args, **kwargs):
        serializer = self.get_serializer(data=request.data)
        serializer.is_valid(raise_exception=True)

        try:
            self.perform_create(serializer)
        except HTTPError:
            return Response('Could not register subscription with service.', status=502)

        headers = self.get_success_headers(serializer.data)
        return AftereffectResponse(
            serializer.data, status=status.HTTP_201_CREATED, headers=headers,
            aftereffect=lambda: serializer.submit_history())


class CustomReportSubscriptionViewSet(viewsets.mixins.ListModelMixin, viewsets.GenericViewSet):
    serializer_class = serializers.CustomReportSubscriptionSerializer
    queryset = models.CustomReportSubscription.objects.none()  # For inspection only; get_queryset is used live

    def get_queryset(self):
        return models.CustomReportSubscription.objects.filter(user=self.request.user)


@api_view(['POST'])
@permission_classes([permissions.AllowAny])
def digita_gw(request):
    """
    Digita GW endpoint implementation
    """
    identifier = request.data['DevEUI_uplink']['DevEUI']
    apsen = core.models.apartment_sensor_models.ApartmentSensor.objects.get_or_create(identifier=identifier)[0]
    payload = binascii.unhexlify(request.data['DevEUI_uplink']['payload_hex'])
    decoded_payload = decode_elsys_payload(payload)
    mapping = settings.DIGITA_GW_PAYLOAD_TO_ATTRIBUTES  # type: dict

    new_values = []
    for key, value in decoded_payload.items():
        uri = mapping.get(key, '')
        if uri:
            attr = core.models.sensor_models.SensorAttribute.objects.get_or_create(uri=uri, defaults={'description': key})[0]
        else:
            attr = core.models.sensor_models.SensorAttribute.objects.get_or_create(description=key)[0]
        apsen_attr = apsen.attributes.get_or_create(attribute=attr)[0]
        new_values.append(apsen_attr.values.create(value=value))
    models.Subscription.handle_new_values(new_values)
    return Response({"message": "Updated successfully"})

Serialize Apartments current authenticated user belongs to. # For inspection only; get_queryset is used live # For inspection only; get_queryset is used live Serialize all services current authenticated user could     subscribe to considering what sensors are available and what     requirements services have. Serialize all subscriptions and provide methods to create new     subscriptions and terminate old ones. # For inspection only; get_queryset is used live # For inspection only; get_queryset is used live Digita GW endpoint implementation # type: dict