# Copyright (C) 2018-2024 by BDE ENS Paris-Saclay
# SPDX-License-Identifier: GPL-3.0-or-later

from django.db import transaction
from django.contrib.auth.mixins import LoginRequiredMixin
from django.http import HttpResponseRedirect
from django_tables2.views import MultiTableMixin
from django.urls import reverse
from django.utils.translation import gettext_lazy as _
from django.utils import timezone
from django.views.generic import DetailView, UpdateView
from django.views.generic.list import ListView
from django.forms import HiddenInput
from permission.backends import PermissionBackend
from permission.views import ProtectQuerysetMixin, ProtectedCreateView

from .forms import AddIngredientForms, BasicFoodForms, QRCodeForms, TransformedFoodForms
from .models import BasicFood, Food, QRCode, TransformedFood
from .tables import TransformedFoodTable


class AddIngredientView(ProtectQuerysetMixin, UpdateView):
    """
    A view to add an ingredient
    """
    model = Food
    template_name = 'food/add_ingredient_form.html'
    extra_context = {"title": _("Add the ingredient")}
    form_class = AddIngredientForms

    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        context["pk"] = self.kwargs["pk"]
        return context

    @transaction.atomic
    def form_valid(self, form):
        form.instance.creater = self.request.user
        food = Food.objects.get(pk=self.kwargs['pk'])
        add_ingredient_form = AddIngredientForms(data=self.request.POST)
        if food.is_ready:
            form.add_error(None, _("The product is already prepared"))
            return self.form_invalid(form)
        if not add_ingredient_form.is_valid():
            return self.form_invalid(form)

        # We flip logic ""fully used = not is_active""
        food.is_active = not food.is_active
        # Save the aliment and the allergens associed
        for transformed_pk in self.request.POST.getlist('ingredient'):
            transformed = TransformedFood.objects.get(pk=transformed_pk)
            if not transformed.is_ready:
                transformed.ingredient.add(food)
                transformed.update()
        food.save()

        return HttpResponseRedirect(self.get_success_url())

    def get_success_url(self, **kwargs):
        return reverse('food:food_list')


class BasicFoodUpdateView(ProtectQuerysetMixin, LoginRequiredMixin, UpdateView):
    """
    A view to update a basic food
    """
    model = BasicFood
    form_class = BasicFoodForms
    template_name = 'food/basicfood_form.html'
    extra_context = {"title": _("Update an aliment")}

    @transaction.atomic
    def form_valid(self, form):
        form.instance.creater = self.request.user
        basic_food_form = BasicFoodForms(data=self.request.POST)
        if not basic_food_form.is_valid():
            return self.form_invalid(form)

        ans = super().form_valid(form)
        form.instance.update()
        return ans

    def get_success_url(self, **kwargs):
        self.object.refresh_from_db()
        return reverse('food:food_view', kwargs={"pk": self.object.pk})

    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        return context


class FoodView(ProtectQuerysetMixin, LoginRequiredMixin, DetailView):
    """
    A view to see a food
    """
    model = Food
    extra_context = {"title": _("Details of:")}
    context_object_name = "food"

    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)

        context["can_update"] = PermissionBackend.check_perm(self.request, "food.change_food")
        context["can_add_ingredient"] = PermissionBackend.check_perm(self.request, "food.change_transformedfood")
        return context


class QRCodeBasicFoodCreateView(ProtectQuerysetMixin, ProtectedCreateView):
    #####################################################################
    # TO DO
    # - this feature is very pratical for meat or fish, nevertheless we can implement this later
    # - fix picture save
    # - implement solution crop and convert image (reuse or recode ImageForm from members apps)
    #####################################################################
    """
    A view to add a basic food with a qrcode
    """
    model = BasicFood
    form_class = BasicFoodForms
    template_name = 'food/basicfood_form.html'
    extra_context = {"title": _("Add a new basic food with QRCode")}

    @transaction.atomic
    def form_valid(self, form):
        form.instance.creater = self.request.user
        basic_food_form = BasicFoodForms(data=self.request.POST)
        if not basic_food_form.is_valid():
            return self.form_invalid(form)

        # Save the aliment and the allergens associed
        basic_food = form.save(commit=False)
        # We assume the date of labeling and the same as the date of arrival
        basic_food.arrival_date = timezone.now()
        basic_food.is_ready = False
        basic_food.is_active = True
        basic_food.was_eaten = False
        basic_food._force_save = True
        basic_food.save()
        basic_food.refresh_from_db()

        qrcode = QRCode()
        qrcode.qr_code_number = self.kwargs['slug']
        qrcode.food_container = basic_food
        qrcode.save()

        return super().form_valid(form)

    def get_success_url(self, **kwargs):
        self.object.refresh_from_db()
        return reverse('food:qrcode_view', kwargs={"slug": self.kwargs['slug']})

    def get_sample_object(self):

        # We choose a club which may work or BDE else
        owner_id = 1
        for membership in self.request.user.memberships.all():
            club_id = membership.club.id
            food = BasicFood(name="", expiry_date=timezone.now(), owner_id=club_id)
            if PermissionBackend.check_perm(self.request, "food.add_basicfood", food):
                owner_id = club_id

        return BasicFood(
            name="",
            expiry_date=timezone.now(),
            owner_id=owner_id,
        )

    def get_context_data(self, **kwargs):
        # Some field are hidden on create
        context = super().get_context_data(**kwargs)

        form = context['form']
        form.fields['is_active'].widget = HiddenInput()
        form.fields['was_eaten'].widget = HiddenInput()

        copy = self.request.GET.get('copy', None)
        if copy is not None:
            basic = BasicFood.objects.get(pk=copy)
            for field in ['date_type', 'expiry_date', 'name', 'owner']:
                form.fields[field].initial = getattr(basic, field)
            for field in ['allergens']:
                form.fields[field].initial = getattr(basic, field).all()

        return context


class QRCodeCreateView(ProtectQuerysetMixin, ProtectedCreateView):
    """
    A view to add a new qrcode
    """
    model = QRCode
    template_name = 'food/create_qrcode_form.html'
    form_class = QRCodeForms
    extra_context = {"title": _("Add a new QRCode")}

    def get(self, *args, **kwargs):
        qrcode = kwargs["slug"]
        if self.model.objects.filter(qr_code_number=qrcode).count() > 0:
            return HttpResponseRedirect(reverse("food:qrcode_view", kwargs=kwargs))
        else:
            return super().get(*args, **kwargs)

    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        context["slug"] = self.kwargs["slug"]

        context["last_basic"] = BasicFood.objects.order_by('-pk').all()[:10]

        return context

    @transaction.atomic
    def form_valid(self, form):
        form.instance.creater = self.request.user
        qrcode_food_form = QRCodeForms(data=self.request.POST)
        if not qrcode_food_form.is_valid():
            return self.form_invalid(form)

        # Save the qrcode
        qrcode = form.save(commit=False)
        qrcode.qr_code_number = self.kwargs["slug"]
        qrcode._force_save = True
        qrcode.save()
        qrcode.refresh_from_db()

        qrcode.food_container.save()

        return super().form_valid(form)

    def get_success_url(self, **kwargs):
        self.object.refresh_from_db()
        return reverse('food:qrcode_view', kwargs={"slug": self.kwargs['slug']})

    def get_sample_object(self):
        return QRCode(
            qr_code_number=self.kwargs["slug"],
            food_container_id=1
        )


class QRCodeView(ProtectQuerysetMixin, LoginRequiredMixin, DetailView):
    """
    A view to see a qrcode
    """
    model = QRCode
    extra_context = {"title": _("QRCode")}
    context_object_name = "qrcode"
    slug_field = "qr_code_number"

    def get(self, *args, **kwargs):
        qrcode = kwargs["slug"]
        if self.model.objects.filter(qr_code_number=qrcode).count() > 0:
            return super().get(*args, **kwargs)
        else:
            return HttpResponseRedirect(reverse("food:qrcode_create", kwargs=kwargs))

    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)

        qr_code_number = self.kwargs['slug']
        qrcode = self.model.objects.get(qr_code_number=qr_code_number)

        model = qrcode.food_container.polymorphic_ctype.model

        if model == "basicfood":
            context["can_update_basic"] = PermissionBackend.check_perm(self.request, "food.change_basicfood")
            context["can_view_detail"] = PermissionBackend.check_perm(self.request, "food.view_basicfood")
        if model == "transformedfood":
            context["can_update_transformed"] = PermissionBackend.check_perm(self.request, "food.change_transformedfood")
            context["can_view_detail"] = PermissionBackend.check_perm(self.request, "food.view_transformedfood")
        context["can_add_ingredient"] = PermissionBackend.check_perm(self.request, "food.change_transformedfood")
        return context


class TransformedFoodCreateView(ProtectQuerysetMixin, ProtectedCreateView):
    """
    A view to add a tranformed food
    """
    model = TransformedFood
    template_name = 'food/transformedfood_form.html'
    form_class = TransformedFoodForms
    extra_context = {"title": _("Add a new meal")}

    @transaction.atomic
    def form_valid(self, form):
        form.instance.creater = self.request.user
        transformed_food_form = TransformedFoodForms(data=self.request.POST)
        if not transformed_food_form.is_valid():
            return self.form_invalid(form)

        # Save the aliment and allergens associated
        transformed_food = form.save(commit=False)
        transformed_food.expiry_date = transformed_food.creation_date
        transformed_food.is_active = True
        transformed_food.is_ready = False
        transformed_food.was_eaten = False
        transformed_food._force_save = True
        transformed_food.save()
        transformed_food.refresh_from_db()
        ans = super().form_valid(form)
        transformed_food.update()
        return ans

    def get_success_url(self, **kwargs):
        self.object.refresh_from_db()
        return reverse('food:food_view', kwargs={"pk": self.object.pk})

    def get_sample_object(self):
        # We choose a club which may work or BDE else
        owner_id = 1
        for membership in self.request.user.memberships.all():
            club_id = membership.club.id
            food = TransformedFood(name="",
                                   creation_date=timezone.now(),
                                   expiry_date=timezone.now(),
                                   owner_id=club_id)
            if PermissionBackend.check_perm(self.request, "food.add_transformedfood", food):
                owner_id = club_id
                break

        return TransformedFood(
            name="",
            owner_id=owner_id,
            creation_date=timezone.now(),
            expiry_date=timezone.now(),
        )

    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)

        # Some field are hidden on create
        form = context['form']
        form.fields['is_active'].widget = HiddenInput()
        form.fields['is_ready'].widget = HiddenInput()
        form.fields['was_eaten'].widget = HiddenInput()
        form.fields['shelf_life'].widget = HiddenInput()

        return context


class TransformedFoodUpdateView(ProtectQuerysetMixin, LoginRequiredMixin, UpdateView):
    """
    A view to update transformed product
    """
    model = TransformedFood
    template_name = 'food/transformedfood_form.html'
    form_class = TransformedFoodForms
    extra_context = {'title': _('Update a meal')}

    @transaction.atomic
    def form_valid(self, form):
        form.instance.creater = self.request.user
        transformedfood_form = TransformedFoodForms(data=self.request.POST)
        if not transformedfood_form.is_valid():
            return self.form_invalid(form)

        ans = super().form_valid(form)
        form.instance.update()
        return ans

    def get_success_url(self, **kwargs):
        self.object.refresh_from_db()
        return reverse('food:food_view', kwargs={"pk": self.object.pk})

    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        return context


class TransformedListView(ProtectQuerysetMixin, LoginRequiredMixin, MultiTableMixin, ListView):
    """
    Displays ready TransformedFood
    """
    model = TransformedFood
    tables = [TransformedFoodTable, TransformedFoodTable, TransformedFoodTable]
    extra_context = {"title": _("Transformed food")}

    def get_queryset(self, **kwargs):
        return super().get_queryset(**kwargs).distinct()

    def get_tables(self):
        tables = super().get_tables()

        tables[0].prefix = "all-"
        tables[1].prefix = "open-"
        tables[2].prefix = "served-"
        return tables

    def get_tables_data(self):
        # first table = all transformed food, second table = free, third = served
        return [
            self.get_queryset().order_by("-creation_date"),
            TransformedFood.objects.filter(is_ready=True, is_active=True, was_eaten=False, expiry_date__lt=timezone.now())
                                   .filter(PermissionBackend.filter_queryset(self.request, TransformedFood, "view"))
                                   .distinct()
                                   .order_by("-creation_date"),
            TransformedFood.objects.filter(is_ready=True, is_active=True, was_eaten=False, expiry_date__gte=timezone.now())
                                   .filter(PermissionBackend.filter_queryset(self.request, TransformedFood, "view"))
                                   .distinct()
                                   .order_by("-creation_date")
        ]

    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)

        # We choose a club which should work
        for membership in self.request.user.memberships.all():
            club_id = membership.club.id
            food = TransformedFood(
                name="",
                owner_id=club_id,
                creation_date=timezone.now(),
                expiry_date=timezone.now(),
            )
            if PermissionBackend.check_perm(self.request, "food.add_transformedfood", food):
                context['can_create_meal'] = True
                break

        tables = context["tables"]
        for name, table in zip(["table", "open", "served"], tables):
            context[name] = table
        return context