"""Establishment app views."""
from django.conf import settings
from django.contrib.contenttypes.models import ContentType
from django.db import transaction
from django.db.models.query_utils import Q
from django.http import Http404
from django.shortcuts import get_object_or_404
from django_filters.rest_framework import DjangoFilterBackend
from rest_framework import generics, response, status
from rest_framework.response import Response

from account.models import User, Role, UserRole
from collection.models import Guide
from establishment import filters, models, serializers
from establishment.models import EstablishmentEmployee, Menu
from timetable.models import Timetable
from timetable.serialziers import ScheduleCreateSerializer, ScheduleRUDSerializer
from utils.methods import get_permission_classes
from utils.permissions import (IsEstablishmentAdministrator, IsEstablishmentManager)
from utils.views import CreateDestroyGalleryViewMixin
from main import models as main_models
from main import serializers as main_serializers


class MenuRUDMixinViews:
    """Menu mixin"""

    def get_object(self):
        instance = self.get_queryset().filter(
            Q(establishment__slug=self.kwargs.get('slug')) | Q(establishment__id=self.kwargs.get('pk'))
        ).first()

        if instance is None:
            raise Http404

        return instance


class EstablishmentMixinViews:
    """Establishment mixin."""

    def get_queryset(self):
        """Overridden method 'get_queryset'."""
        queryset = models.Establishment.objects.with_base_related
        if hasattr(self, 'request') and \
                (hasattr(self.request, 'user') and hasattr(self.request, 'country_code')):
            return queryset().available_establishments(self.request.user, self.request.country_code)
        return queryset().none()


class EstablishmentListCreateView(EstablishmentMixinViews, generics.ListCreateAPIView):
    """Establishment list/create view."""

    filter_class = filters.EstablishmentFilter
    serializer_class = serializers.EstablishmentListCreateSerializer
    permission_classes = get_permission_classes(
        IsEstablishmentManager,
        IsEstablishmentAdministrator,
    )


class EmployeeEstablishmentPositionsView(generics.ListAPIView):
    """Establishment by employee view."""

    queryset = models.EstablishmentEmployee.objects.all()
    serializer_class = serializers.EstablishmentEmployeePositionsSerializer
    permission_classes = get_permission_classes(
        IsEstablishmentManager,
        IsEstablishmentAdministrator
    )

    def get_queryset(self):
        employee_pk = self.kwargs.get('pk')
        return super().get_queryset().filter(employee__id=employee_pk).all().prefetch_related(
            'establishment').select_related('position')


class EmployeeEstablishmentsListView(generics.ListAPIView):
    """Establishment by employee list view."""
    serializer_class = serializers.EstablishmentListCreateSerializer
    permission_classes = get_permission_classes(
        IsEstablishmentManager,
        IsEstablishmentAdministrator,
    )

    def get_queryset(self):
        pk = self.kwargs.get('pk')
        employee = get_object_or_404(models.Employee, pk=pk)
        return employee.establishments.with_extended_related()


class EmployeePositionsListView(generics.ListAPIView):
    """Establishment position by employee list view."""

    queryset = models.Establishment.objects.all()
    serializer_class = serializers.EstablishmentPositionListSerializer
    permission_classes = get_permission_classes(
        IsEstablishmentManager,
        IsEstablishmentAdministrator
    )

    def get_queryset(self):
        pk = self.kwargs.get('pk')
        employee = get_object_or_404(models.Employee, pk=pk)
        return employee.establishments.with_extended_related()


class EstablishmentRUDView(EstablishmentMixinViews, generics.RetrieveUpdateDestroyAPIView):
    lookup_field = 'slug'
    serializer_class = serializers.EstablishmentRUDSerializer
    permission_classes = get_permission_classes(
        IsEstablishmentManager,
        IsEstablishmentAdministrator,
    )

    def get_queryset(self):
        """Overridden get_queryset method."""
        qs = super(EstablishmentRUDView, self).get_queryset()
        return qs.prefetch_related(
            'establishmentemployee_set',
            'establishmentemployee_set__establishment',
        )


class EstablishmentScheduleRUDView(EstablishmentMixinViews, generics.RetrieveUpdateDestroyAPIView):
    """Establishment schedule RUD view"""
    lookup_field = 'slug'
    serializer_class = ScheduleRUDSerializer
    permission_classes = get_permission_classes(
        IsEstablishmentManager,
        IsEstablishmentAdministrator,
    )

    def get_object(self):
        """
        Returns the object the view is displaying.
        """
        establishment_slug = self.kwargs['slug']
        schedule_id = self.kwargs['schedule_id']

        establishment = get_object_or_404(klass=super(EstablishmentScheduleRUDView, self).get_queryset(),
                                          slug=establishment_slug)
        schedule = get_object_or_404(klass=establishment.schedule,
                                     id=schedule_id)

        # May raise a permission denied
        self.check_object_permissions(self.request, establishment)
        self.check_object_permissions(self.request, schedule)

        return schedule


class EstablishmentScheduleCreateView(generics.CreateAPIView):
    """Establishment schedule Create view"""
    lookup_field = 'slug'
    serializer_class = ScheduleCreateSerializer
    queryset = Timetable.objects.all()
    permission_classes = get_permission_classes(
        IsEstablishmentManager,
        IsEstablishmentAdministrator,
    )


class CardAndWinesListView(generics.RetrieveAPIView):
    permission_classes = get_permission_classes(
        IsEstablishmentManager,
        IsEstablishmentAdministrator,
    )
    serializer_class = serializers.CardAndWinesSerializer
    queryset = models.Establishment.objects.with_base_related()

    def get_object(self):
        establishment = models.Establishment.objects.prefetch_plates()\
            .filter(pk=self.kwargs['establishment_id'])\
            .first()
        if establishment is None:
            raise Http404
        plates = []
        for menu in establishment.menu_set.prefetch_related('plates').exclude(type=Menu.FORMULAS):
            plates += list(menu.plates.all().prefetch_related('menu'))
        setattr(establishment, 'card_and_wine_plates', plates)
        return establishment


class CreateMenuForCertainEstablishmentView(generics.CreateAPIView):
    """Creates formulas menu for certain establishment"""
    permission_classes = get_permission_classes(
        IsEstablishmentManager,
        IsEstablishmentAdministrator,
    )
    # from rest_framework.permissions import AllowAny
    # permission_classes = (AllowAny, )
    queryset = models.Menu.objects.with_base_related().by_type(menu_type=models.Menu.FORMULAS)
    serializer_class = serializers.MenuBackOfficeSerializer

    def create(self, request, *args, **kwargs):
        data = request.data.copy()
        data['establishment_id'] = kwargs['establishment_id']
        data['type'] = models.Menu.FORMULAS
        serializer = self.get_serializer(data=data)
        serializer.is_valid(raise_exception=True)
        with transaction.atomic():
            self.perform_create(serializer)
        headers = self.get_success_headers(serializer.data)
        return Response(serializer.data, status=status.HTTP_201_CREATED, headers=headers)


class MenuListCreateView(generics.ListCreateAPIView):
    """Menu list create view. This view works only with menu formulas type"""
    serializer_class = serializers.MenuBackOfficeSerializer
    queryset = models.Menu.objects.with_base_related().by_type(menu_type=models.Menu.FORMULAS)
    permission_classes = get_permission_classes(
        IsEstablishmentManager,
        IsEstablishmentAdministrator,
    )

    filter_backends = (DjangoFilterBackend,)
    filterset_fields = (
        'establishment',
        'establishment__slug',
    )

    def create(self, request, *args, **kwargs):
        response_data = []
        with transaction.atomic():
            establishment = get_object_or_404(models.Establishment, pk=next(iter(request.data))['establishment_id'])
            update_ids = [menu['id'] for menu in request.data if 'id' in menu]
            for menu_instance in establishment.menu_set.by_type(models.Menu.FORMULAS):
                if menu_instance.pk not in update_ids:
                    menu_instance.delete()
            for menu in request.data:
                menu.update({'type': models.Menu.FORMULAS})
                if 'id' in menu:

                    instance = get_object_or_404(Menu, pk=menu['id'])
                    serializer = self.get_serializer(instance, data=menu)
                else:

                    serializer = self.get_serializer(data=menu)

                serializer.is_valid(raise_exception=True)
                serializer.save()
                response_data.append(serializer.data)

        return Response(response_data, status=status.HTTP_201_CREATED)


class MenuRUDView(generics.UpdateAPIView, generics.DestroyAPIView):
    """Menu RUD view."""
    serializer_class = serializers.MenuBackOfficeSerializer
    queryset = models.Menu.objects.all()
    permission_classes = get_permission_classes(
        IsEstablishmentManager,
        IsEstablishmentAdministrator,
    )

    def get_object(self):
        return get_object_or_404(klass=models.Menu, pk=self.kwargs['menu_id'])

    def update(self, request, *args, **kwargs):
        partial = kwargs.pop('partial', False)
        instance = self.get_object()
        data = request.data.copy()
        data['establishment_id'] = instance.establishment.pk
        serializer = self.get_serializer(instance, data=data, partial=partial)
        serializer.is_valid(raise_exception=True)
        with transaction.atomic():
            self.perform_update(serializer)

        if getattr(instance, '_prefetched_objects_cache', None):
            # If 'prefetch_related' has been applied to a queryset, we need to
            # forcibly invalidate the prefetch cache on the instance.
            instance._prefetched_objects_cache = {}

        return Response(serializer.data)


class MenuUploadsRUDView(generics.RetrieveDestroyAPIView):
    """Menu uploads RUD view."""
    serializer_class = serializers.MenuFilesSerializers
    queryset = models.MenuFiles.objects.all()
    permission_classes = get_permission_classes(
        IsEstablishmentManager,
        IsEstablishmentAdministrator,
    )


class EstablishmentWineView(generics.CreateAPIView):
    """Create and update establishment wine"""
    serializer_class = serializers.EstablishmentBackOfficeWineSerializer
    queryset = models.EstablishmentBackOfficeWine.objects.all()

    permission_classes = get_permission_classes(
        IsEstablishmentManager,
        IsEstablishmentAdministrator,
    )

    def create(self, request, *args, **kwargs):
        data = request.data.copy()
        data.update({'establishment_id': self.kwargs['establishment_id']})
        if 'id' in data:
            """Update"""
            instance = get_object_or_404(models.EstablishmentBackOfficeWine, pk=data['id'])
            serializer = self.get_serializer(instance, data=data)
        else:
            """create"""
            serializer = self.get_serializer(data=data)
        serializer.is_valid(raise_exception=True)
        serializer.save()

        return Response(serializer.data, status=status.HTTP_201_CREATED)


class DishCreateView(generics.CreateAPIView):
    """View for creating and binding dish to establishment"""
    serializer_class = serializers.CardAndWinesPlatesSerializer
    queryset = models.Plate.objects.all()
    permission_classes = get_permission_classes(
        IsEstablishmentManager,
        IsEstablishmentAdministrator,
    )


class DishUpdateDeleteView(generics.UpdateAPIView, generics.DestroyAPIView):
    serializer_class = serializers.CardAndWinesPlatesSerializer
    queryset = models.Plate.objects.all()
    permission_classes = get_permission_classes(
        IsEstablishmentManager,
        IsEstablishmentAdministrator,
    )

    def get_object(self):
        return get_object_or_404(models.Plate, pk=self.kwargs['dish_id'])

    def update(self, request, *args, **kwargs):
        with transaction.atomic():
            return super().update(request, *args, **kwargs)


class MenuUploadsCreateView(generics.CreateAPIView):
    """Menu uploads list view"""
    serializer_class = serializers.MenuFilesSerializers
    queryset = models.MenuFiles.objects.all()
    permission_classes = get_permission_classes(
        IsEstablishmentManager,
        IsEstablishmentAdministrator,
    )


class SocialChoiceListCreateView(generics.ListCreateAPIView):
    """SocialChoice list create view."""
    serializer_class = serializers.SocialChoiceSerializers
    queryset = models.SocialChoice.objects.all()
    pagination_class = None
    permission_classes = get_permission_classes(
        IsEstablishmentManager,
        IsEstablishmentAdministrator,
    )


class SocialChoiceRUDView(generics.RetrieveUpdateDestroyAPIView):
    """SocialChoice RUD view."""
    serializer_class = serializers.SocialChoiceSerializers
    queryset = models.SocialChoice.objects.all()
    permission_classes = get_permission_classes(
        IsEstablishmentManager,
        IsEstablishmentAdministrator,
    )


class SocialListCreateView(generics.ListCreateAPIView):
    """Social list create view."""
    serializer_class = serializers.SocialNetworkSerializers
    queryset = models.SocialNetwork.objects.all()
    pagination_class = None
    permission_classes = get_permission_classes(
        IsEstablishmentManager,
        IsEstablishmentAdministrator,
    )


class SocialRUDView(generics.RetrieveUpdateDestroyAPIView):
    """Social RUD view."""
    serializer_class = serializers.SocialNetworkSerializers
    queryset = models.SocialNetwork.objects.all()
    permission_classes = get_permission_classes(
        IsEstablishmentManager,
        IsEstablishmentAdministrator,
    )


class PlateListCreateView(generics.ListCreateAPIView):
    """Plate list create view."""
    serializer_class = serializers.PlatesSerializers
    queryset = models.Plate.objects.all()
    pagination_class = None
    permission_classes = get_permission_classes(
        IsEstablishmentAdministrator,
        IsEstablishmentManager,
    )


class PlateRUDView(generics.RetrieveUpdateDestroyAPIView):
    """Plate RUD view."""
    serializer_class = serializers.PlatesSerializers
    queryset = models.Plate.objects.all()
    permission_classes = get_permission_classes(
        IsEstablishmentAdministrator,
        IsEstablishmentManager,
    )


class PhonesListCreateView(generics.ListCreateAPIView):
    """Phones list create view."""
    serializer_class = serializers.ContactPhoneBackSerializers
    queryset = models.ContactPhone.objects.all()
    pagination_class = None
    permission_classes = get_permission_classes(
        IsEstablishmentAdministrator,
        IsEstablishmentManager,
    )


class PhonesRUDView(generics.RetrieveUpdateDestroyAPIView):
    """Phones RUD view."""
    serializer_class = serializers.ContactPhoneBackSerializers
    queryset = models.ContactPhone.objects.all()
    permission_classes = get_permission_classes(
        IsEstablishmentAdministrator,
        IsEstablishmentManager,
    )


class EmailListCreateView(generics.ListCreateAPIView):
    """Email list create view."""
    serializer_class = serializers.ContactEmailBackSerializers
    queryset = models.ContactEmail.objects.all()
    pagination_class = None
    permission_classes = get_permission_classes(
        IsEstablishmentAdministrator,
        IsEstablishmentManager,
    )


class EmailRUDView(generics.RetrieveUpdateDestroyAPIView):
    """Email RUD view."""
    serializer_class = serializers.ContactEmailBackSerializers
    queryset = models.ContactEmail.objects.all()
    permission_classes = get_permission_classes(
        IsEstablishmentAdministrator,
        IsEstablishmentManager,
    )


class EmployeeListCreateView(generics.ListCreateAPIView):
    """Employee list create view."""
    filter_class = filters.EmployeeBackFilter
    serializer_class = serializers.EmployeeBackSerializers
    queryset = models.Employee.objects.all().distinct().with_back_office_related()
    permission_classes = get_permission_classes(
        IsEstablishmentManager,
        IsEstablishmentAdministrator,
    )


class EmployeesListSearchViews(generics.ListAPIView):
    """Employee search view"""
    pagination_class = None
    queryset = models.Employee.objects.all().with_back_office_related().select_related('photo')
    filter_class = filters.EmployeeBackSearchFilter
    serializer_class = serializers.EmployeeBackSerializers
    permission_classes = get_permission_classes(
        IsEstablishmentManager,
        IsEstablishmentAdministrator,
    )


class EstablishmentEmployeeListView(generics.ListCreateAPIView):
    """Establishment emplyoees list view."""
    serializer_class = serializers.EstEmployeeBackSerializer
    pagination_class = None
    permission_classes = get_permission_classes(
        IsEstablishmentManager,
        IsEstablishmentAdministrator,
    )

    def get_queryset(self):
        establishment_id = self.kwargs['establishment_id']
        establishment = get_object_or_404(models.Establishment, pk=establishment_id)
        self.kwargs['establishment_instance'] = establishment
        self.kwargs['establishment_instance_subtypes'] = list(establishment.establishment_subtypes.all())
        self.kwargs['establishment_instance_type'] = establishment.establishment_type
        return models.Employee.objects.filter(
            establishmentemployee__establishment_id=establishment_id,
        ).distinct()


class EmployeeRUDView(generics.RetrieveUpdateDestroyAPIView):
    """Employee RUD view."""
    serializer_class = serializers.EmployeeBackSerializers
    queryset = models.Employee.objects.with_back_office_related()
    permission_classes = get_permission_classes(
        IsEstablishmentManager,
        IsEstablishmentAdministrator,
    )


class RemoveAwardView(generics.DestroyAPIView):
    lookup_field = 'pk'
    serializer_class = serializers.EmployeeBackSerializers
    queryset = models.Employee.objects.with_back_office_related()
    permission_classes = get_permission_classes()

    def get_object(self):
        employee = super().get_object()
        employee.remove_award(self.kwargs['award_id'])
        return employee

    def delete(self, request, *args, **kwargs):
        instance = self.get_object()
        return Response(status=status.HTTP_204_NO_CONTENT)


class EstablishmentTypeListCreateView(generics.ListCreateAPIView):
    """Establishment type list/create view."""
    serializer_class = serializers.EstablishmentTypeBaseSerializer
    queryset = models.EstablishmentType.objects.select_related('default_image')
    pagination_class = None
    permission_classes = get_permission_classes()


class EstablishmentTypeRUDView(generics.RetrieveUpdateDestroyAPIView):
    """Establishment type retrieve/update/destroy view."""
    serializer_class = serializers.EstablishmentTypeBaseSerializer
    queryset = models.EstablishmentType.objects.select_related('default_image')
    permission_classes = get_permission_classes()


class EstablishmentSubtypeListCreateView(generics.ListCreateAPIView):
    """Establishment subtype list/create view."""
    serializer_class = serializers.EstablishmentSubTypeBaseSerializer
    queryset = models.EstablishmentSubType.objects.select_related('default_image')
    pagination_class = None
    permission_classes = get_permission_classes()


class EstablishmentSubtypeRUDView(generics.RetrieveUpdateDestroyAPIView):
    """Establishment subtype retrieve/update/destroy view."""
    serializer_class = serializers.EstablishmentSubTypeBaseSerializer
    queryset = models.EstablishmentSubType.objects.select_related('default_image')
    permission_classes = get_permission_classes()


class EstablishmentGalleryCreateDestroyView(EstablishmentMixinViews,
                                            CreateDestroyGalleryViewMixin):
    """Resource for a create|destroy gallery for establishment for back-office users."""
    lookup_field = 'slug'
    serializer_class = serializers.EstablishmentBackOfficeGallerySerializer
    permission_classes = get_permission_classes()

    def get_object(self):
        """
        Returns the object the view is displaying.
        """
        establishment_qs = self.filter_queryset(self.get_queryset())

        establishment = get_object_or_404(establishment_qs, slug=self.kwargs.get('slug'))
        gallery = get_object_or_404(establishment.establishment_gallery,
                                    image_id=self.kwargs.get('image_id'))

        # May raise a permission denied
        self.check_object_permissions(self.request, gallery)

        return gallery


class EstablishmentGalleryListView(EstablishmentMixinViews,
                                   generics.ListAPIView):
    """Resource for returning gallery for establishment for back-office users."""
    lookup_field = 'slug'
    serializer_class = serializers.ImageBaseSerializer
    permission_classes = get_permission_classes()

    def get_object(self):
        """Override get_object method."""
        qs = super(EstablishmentGalleryListView, self).get_queryset()
        establishment = get_object_or_404(qs, slug=self.kwargs.get('slug'))

        # May raise a permission denied
        self.check_object_permissions(self.request, establishment)

        return establishment

    def get_queryset(self):
        """Override get_queryset method."""
        return self.get_object().crop_gallery


class EstablishmentCompanyListCreateView(EstablishmentMixinViews,
                                         generics.ListCreateAPIView):
    """List|Create establishment company view."""

    lookup_field = 'slug'
    serializer_class = serializers.EstablishmentCompanyListCreateSerializer
    permission_classes = get_permission_classes()

    def get_object(self):
        """Returns the object the view is displaying."""
        establishment_qs = super(EstablishmentCompanyListCreateView, self).get_queryset()
        filtered_qs = self.filter_queryset(establishment_qs)

        establishment = get_object_or_404(filtered_qs, slug=self.kwargs.get('slug'))

        # May raise a permission denied
        self.check_object_permissions(self.request, establishment)

        return establishment

    def get_queryset(self):
        """Overridden get_queryset method."""
        return self.get_object().companies.all()


class EstablishmentCompanyRUDView(EstablishmentMixinViews,
                                  generics.RetrieveUpdateDestroyAPIView):
    """Create|Retrieve|Update|Destroy establishment company view."""

    lookup_field = 'slug'
    serializer_class = serializers.CompanyBaseSerializer
    permission_classes = get_permission_classes()

    def get_object(self):
        """Returns the object the view is displaying."""
        establishment_qs = super(EstablishmentCompanyRUDView, self).get_queryset()
        filtered_ad_qs = self.filter_queryset(establishment_qs)

        establishment = get_object_or_404(filtered_ad_qs, slug=self.kwargs.get('slug'))
        company = get_object_or_404(establishment.companies.all(), pk=self.kwargs.get('company_pk'))

        # May raise a permission denied
        self.check_object_permissions(self.request, company)

        return company


class EstablishmentNoteListCreateView(EstablishmentMixinViews,
                                      generics.ListCreateAPIView):
    """Retrieve|Update|Destroy establishment note view."""

    lookup_field = 'slug'
    serializer_class = serializers.EstablishmentNoteListCreateSerializer
    permission_classes = get_permission_classes(
        IsEstablishmentAdministrator,
        IsEstablishmentManager,
    )

    def get_object(self):
        """Returns the object the view is displaying."""
        establishment_qs = super(EstablishmentNoteListCreateView, self).get_queryset()
        filtered_establishment_qs = self.filter_queryset(establishment_qs)

        establishment = get_object_or_404(filtered_establishment_qs, slug=self.kwargs.get('slug'))

        # May raise a permission denied
        self.check_object_permissions(self.request, establishment)

        return establishment

    def get_queryset(self):
        """Overridden get_queryset method."""
        return self.get_object().notes.all()


class EstablishmentNoteRUDView(EstablishmentMixinViews,
                               generics.RetrieveUpdateDestroyAPIView):
    """Create|Retrieve|Update|Destroy establishment note view."""

    lookup_field = 'slug'
    serializer_class = serializers.EstablishmentNoteBaseSerializer
    permission_classes = get_permission_classes(
        IsEstablishmentAdministrator,
        IsEstablishmentManager,
    )

    def get_object(self):
        """Returns the object the view is displaying."""
        establishment_qs = super(EstablishmentNoteRUDView, self).get_queryset()
        filtered_establishment_qs = self.filter_queryset(establishment_qs)

        establishment = get_object_or_404(filtered_establishment_qs, slug=self.kwargs.get('slug'))
        note = get_object_or_404(establishment.notes.all(), pk=self.kwargs['note_pk'])

        # May raise a permission denied
        self.check_object_permissions(self.request, note)

        return note


class EstablishmentEmployeeCreateView(generics.CreateAPIView):
    serializer_class = serializers.EstablishmentEmployeeCreateSerializer
    queryset = models.EstablishmentEmployee.objects.all()
    permission_classes = get_permission_classes(
        IsEstablishmentManager,
        IsEstablishmentAdministrator
    )


class EstablishmentEmployeeDeleteView(generics.DestroyAPIView):
    queryset = EstablishmentEmployee.objects.all()
    permission_classes = get_permission_classes(
        IsEstablishmentManager,
        IsEstablishmentAdministrator
    )


class EstablishmentPositionListView(generics.ListAPIView):
    """Establishment positions list view."""

    pagination_class = None
    queryset = models.Position.objects.all()
    serializer_class = serializers.PositionBackSerializer
    filter_class = filters.PositionsByEstablishmentFilter
    permission_classes = get_permission_classes(
        IsEstablishmentManager,
        IsEstablishmentAdministrator
    )

    def get_queryset(self):
        return super().get_queryset().order_by('priority')


class EstablishmentAdminView(generics.ListAPIView):
    """Establishment admin list view."""
    serializer_class = serializers.EstablishmentAdminListSerializer
    permission_classes = get_permission_classes(
        IsEstablishmentManager,
        IsEstablishmentAdministrator
    )

    def get_queryset(self):
        establishment = get_object_or_404(
            models.Establishment, slug=self.kwargs['slug'])
        return User.objects.establishment_admin(establishment).distinct()


class EstablishmentGuideCreateDestroyView(generics.GenericAPIView):
    """Add/Remove establishment from guide."""
    establishment_lookup_url_kwarg = 'slug'
    guide_lookup_url_kwarg = 'guide_id'
    permission_classes = get_permission_classes(
        IsEstablishmentManager,
        IsEstablishmentAdministrator
    )

    def get_establishment_queryset(self):
        """Get Establishment queryset."""
        return EstablishmentMixinViews.get_queryset(self)

    def get_guide_queryset(self):
        """Get Guide queryset."""
        queryset = Guide.objects
        if hasattr(self, 'request') and hasattr(self.request, 'country_code'):
            return queryset.by_country_code(self.request.country_code)
        return queryset.none()

    def get_establishment(self):
        queryset = self.get_establishment_queryset()

        # Perform the lookup filtering.
        lookup_url_kwarg = getattr(self, 'establishment_lookup_url_kwarg', None)

        assert lookup_url_kwarg in self.kwargs, (
            'Expected view %s to be called with a URL keyword argument '
            'named "%s". Fix your URL conf, or set the `.lookup_field` '
            'attribute on the view correctly.' %
            (self.__class__.__name__, lookup_url_kwarg)
        )

        filters = {'klass': queryset, lookup_url_kwarg: self.kwargs.get(lookup_url_kwarg)}
        obj = get_object_or_404(**filters)

        # May raise a permission denied
        self.check_object_permissions(self.request, obj)

        return obj

    def get_guide(self):
        queryset = self.get_guide_queryset()

        # Perform the lookup filtering.
        lookup_url_kwarg = getattr(self, 'guide_lookup_url_kwarg', None)

        assert lookup_url_kwarg in self.kwargs, (
            'Expected view %s to be called with a URL keyword argument '
            'named "%s". Fix your URL conf, or set the `.lookup_field` '
            'attribute on the view correctly.' %
            (self.__class__.__name__, lookup_url_kwarg)
        )

        obj = get_object_or_404(klass=queryset, id=self.kwargs.get(lookup_url_kwarg))

        # May raise a permission denied
        self.check_object_permissions(self.request, obj)

        return obj

    def post(self, request, *args, **kwargs):
        """Implement GET-method."""
        establishment = self.get_establishment()
        guide = self.get_guide()
        guide.extend_establishment_guide(establishment.id)
        return Response(status=status.HTTP_200_OK)

    def delete(self, request, *args, **kwargs):
        """Implement DELETE-method."""
        establishment = self.get_establishment()
        guide = self.get_guide()
        guide.remove_establishment(establishment.id)
        return Response(status=status.HTTP_204_NO_CONTENT)


class MenuDishesListCreateView(generics.ListCreateAPIView):
    """Menu (dessert, main_course, starter) list create view."""
    serializer_class = serializers.MenuDishesSerializer
    queryset = models.Menu.objects.with_schedule_plates_establishment().dishes().distinct()
    permission_classes = get_permission_classes(
        IsEstablishmentManager,
        IsEstablishmentAdministrator
    )
    filter_class = filters.MenuDishesBackFilter


class MenuDishesRUDView(generics.RetrieveUpdateDestroyAPIView):
    """Menu (dessert, main_course, starter) RUD view."""
    serializer_class = serializers.MenuDishesRUDSerializers
    queryset = models.Menu.objects.dishes().distinct()
    permission_classes = get_permission_classes(
        IsEstablishmentManager,
        IsEstablishmentAdministrator
    )


class MenuGalleryListView(generics.ListAPIView):
    """Resource for returning gallery for menu for back-office users."""
    serializer_class = serializers.ImageBaseSerializer
    permission_classes = get_permission_classes(
        IsEstablishmentManager,
        IsEstablishmentAdministrator
    )
    queryset = models.Menu.objects.with_schedule_plates_establishment().with_gallery().dishes()

    def get_object(self):
        """Override get_object method."""
        qs = super(MenuGalleryListView, self).get_queryset()
        menu = get_object_or_404(qs, pk=self.kwargs.get('pk'))

        # May raise a permission denied
        # self.check_object_permissions(self.request, menu)

        return menu

    def get_queryset(self):
        """Override get_queryset method."""
        return self.get_object().crop_gallery


class MenuGalleryCreateDestroyView(CreateDestroyGalleryViewMixin):
    """Resource for a create gallery for menu for back-office users."""
    serializer_class = serializers.MenuGallerySerializer
    permission_classes = get_permission_classes(
        IsEstablishmentManager,
        IsEstablishmentAdministrator
    )

    def get_queryset(self):
        """Override get_queryset method."""
        qs = models.Menu.objects.with_schedule_plates_establishment().with_gallery().dishes()
        return qs

    def create(self, request, *args, **kwargs):
        _ = super().create(request, *args, **kwargs)
        news_qs = self.filter_queryset(self.get_queryset())
        return response.Response(
            data=serializers.MenuDishesRUDSerializers(get_object_or_404(news_qs, pk=kwargs.get('pk'))).data
        )

    def get_object(self):
        """
        Returns the object the view is displaying.
        """
        menu_qs = self.filter_queryset(self.get_queryset())

        menu = get_object_or_404(menu_qs, pk=self.kwargs.get('pk'))
        gallery = get_object_or_404(menu.menu_gallery, image_id=self.kwargs.get('image_id'))

        # May raise a permission denied
        self.check_object_permissions(self.request, gallery)

        return gallery


class StatusesListView(generics.ListAPIView):
    """Possible project establishment statuses"""
    pagination_class = None
    serializer_class = serializers.EstablishmentStatusesSerializer

    def get_queryset(self):
        return None

    def list(self, request, *args, **kwargs):
        mutated_for_serializer = [{
            'value': state[0],
            'state_translated': state[1],
        } for state in models.Establishment.STATUS_CHOICES]
        serializer = self.get_serializer(mutated_for_serializer, many=True)
        return response.Response(serializer.data)


class TeamMemberListView(generics.ListAPIView):
    """Show team for certain establishment"""
    pagination_class = None
    serializer_class = serializers.TeamMemberSerializer
    queryset = User.objects.all()
    permission_classes = get_permission_classes(
        IsEstablishmentManager,
        IsEstablishmentAdministrator
    )

    def get_queryset(self):
        establishment = get_object_or_404(klass=models.Establishment, pk=self.kwargs['establishment_id'])
        return super().get_queryset().filter(roles__role=Role.ESTABLISHMENT_ADMINISTRATOR,
                                             userrole__establishment=establishment, userrole__state=UserRole.VALIDATED)


class TeamMemberDeleteView(generics.DestroyAPIView):
    """Delete user from team"""
    permission_classes = get_permission_classes(
        IsEstablishmentManager,
        IsEstablishmentAdministrator
    )

    def get_object(self):
        return UserRole.objects.get(role__role=Role.ESTABLISHMENT_ADMINISTRATOR, user_id=self.kwargs['user_id'],
                                    establishment_id=self.kwargs['establishment_id'])

    def perform_destroy(self, instance):
        instance.delete()
        from account.tasks import team_role_revoked
        establishment = models.Establishment.objects.get(pk=self.kwargs['establishment_id'])
        if settings.USE_CELERY:
            team_role_revoked.delay(self.kwargs['user_id'], self.request.country_code, establishment.name)
        else:
            team_role_revoked(self.kwargs['user_id'], self.request.country_code, establishment.name)


class EstablishmentAwardCreateAndBind(generics.CreateAPIView, generics.DestroyAPIView):
    queryset = main_models.Award.objects.with_base_related().all()
    permission_classes = get_permission_classes()
    serializer_class = serializers.BackEstablishmentAwardCreateSerializer

    def _award_list_for_establishment(self, establishment_id: int, status: int) -> Response:
        awards = main_models.Award.objects.with_base_related().filter(
            object_id=establishment_id,
            content_type=ContentType.objects.get_for_model(models.Establishment)
        )
        response_serializer = main_serializers.AwardBaseSerializer(awards, many=True)
        headers = self.get_success_headers(response_serializer.data)
        return Response(response_serializer.data, status=status, headers=headers)

    def create(self, request, *args, **kwargs):
        """Overridden create method."""
        super().create(request, args, kwargs)
        return self._award_list_for_establishment(kwargs['establishment_id'], status.HTTP_201_CREATED)

    def delete(self, request, *args, **kwargs):
        establishment = get_object_or_404(models.Establishment, id=kwargs['establishment_id'])
        establishment.remove_award(kwargs['award_id'])
        return self._award_list_for_establishment(kwargs['establishment_id'], status.HTTP_200_OK)