"""
Views module for the cards app.
"""
from django.db import transaction
from django.http import HttpResponse
from django.http import HttpResponseRedirect
from django.urls import reverse
from django.urls import reverse_lazy
from django.db.models import Q
from django.views.generic import View
from django.views.generic import ListView
from django.views.generic import CreateView
from django.views.generic import DeleteView
from django.views.generic import DetailView
from django.views.generic import UpdateView
from django.views.generic.base import TemplateResponseMixin
from django.contrib.auth.mixins import UserPassesTestMixin
from django.views.generic.detail import SingleObjectMixin

from .forms import CardForm
from .forms import UploadCardForm
from .forms import MetadataCardForm
from .models import Card
from .models import Template
from .models import CardMetadata
from .services import get_cards_with_rating
from .services import get_default_card_with_rating


class OwnerRequiredMixin(UserPassesTestMixin, SingleObjectMixin):
    """
    Require card owner.
    """

    def test_func(self):
        card = self.get_object()
        return self.request.user == card.user


class CardListView(ListView):
    """
    Cards list view.
    """

    model = Card
    context_object_name = "cards"
    template_name = "cards/card_list.html"
    paginate_by = 12


class MyCardListView(ListView):
    """
    My cards list view.
    """

    context_object_name = "cards"
    template_name = "cards/my_card_list.html"

    def get_queryset(self):
        return Card.objects.filter(user=self.request.user).all()


class CardTemplateListView(ListView):
    """
    List templates used to create cards.
    """

    context_object_name = "templates"
    template_name = "cards/card_template_list.html"
    model = Template

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

        context["template_form"] = UploadCardForm()

        return context


class CardDetailView(DetailView):
    """
    Card detail view.
    """

    context_object_name = "card"
    template_name = "cards/card_detail.html"

    def get_queryset(self):
        return get_cards_with_rating()

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

        card = self.get_object()

        favorite = False
        if self.request.user.is_authenticated:
            favorite = self.request.user in card.followers.all()

        context["favorite"] = favorite

        return context


class DefaultCardDetailView(DetailView):
    """
    Default card detail view for user.
    """

    context_object_name = "card"
    template_name = "cards/card_detail.html"

    def get_object(self, queryset=None):
        username = self.kwargs.get("username")
        return get_default_card_with_rating(username)

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

        card = self.get_object()

        favorite = False
        if self.request.user.is_authenticated:
            favorite = self.request.user in card.followers.all()

        context["favorite"] = favorite

        return context


class CardDeleteView(OwnerRequiredMixin, DeleteView):
    """
    Card delete view.
    """

    model = Card
    context_object_name = "card"
    success_url = reverse_lazy("cards:my-card-list")
    template_name = "cards/card_delete.html"


class CardCreateView(CreateView):
    """
    Card create view.
    """

    model = Card
    form_class = CardForm
    success_url = reverse_lazy("cards:my-card-list")
    template_name = "cards/card_form.html"

    def get_card_template(self):
        try:
            return Template.objects.get(pk=self.kwargs["pk"])
        except Template.DoesNotExist:
            return None

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

        orientation = self.request.GET.get("orientation")
        if orientation in ["1", "2"]:
            context["orientation"] = int(orientation)
        else:
            context["orientation"] = 1

        if "pk" in self.kwargs:
            context["template"] = self.get_card_template()
        else:
            context["blank"] = True

        return context

    def form_valid(self, form):  # pylint: disable=R0914,R0915
        card_image = self.request.POST.get("card")
        template_id = self.request.POST.get("template_id")

        if template_id:
            template = Template.objects.get(pk=template_id)
            form.instance.template = template

        have_default = Card.objects.filter(
            default=True,
            user=self.request.user,
        ).exists()
        form.instance.default = not have_default

        form.instance.user = self.request.user
        form.instance.image = card_image

        card = form.save()

        metadata = MetadataCardForm(data=self.request.POST)
        if metadata.is_valid():
            metadata.instance.card = card
            metadata.save()

        return super().form_valid(form)


class CardUploadView(CreateView):
    """
    Card upload view.
    """

    model = Card
    form_class = UploadCardForm

    def get_success_url(self):
        return reverse_lazy("cards:card-edit", kwargs={"pk": self.object.pk})

    def form_valid(self, form):
        have_default = Card.objects.filter(
            default=True,
            user=self.request.user,
        ).exists()
        form.instance.default = not have_default
        form.instance.user = self.request.user

        return super().form_valid(form)


class CardUpdateView(OwnerRequiredMixin, UpdateView):
    """
    Card update view.
    """

    model = Card
    context_object_name = "card"
    form_class = CardForm
    success_url = reverse_lazy("cards:my-card-list")
    template_name = "cards/card_form.html"
    object = None  # noqa: A003

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

        context["template"] = self.object.template

        return context

    def form_valid(self, form):
        card_image = self.request.POST.get("card")
        form.instance.image = card_image
        self.object = form.save()

        instance = None
        try:
            instance = self.object.cardmetadata
        except CardMetadata.DoesNotExist:
            pass

        metadata = MetadataCardForm(instance=instance, data=self.request.POST)
        if metadata.is_valid():
            metadata.instance.card = self.object
            metadata.save()

        return super().form_valid(form)


class DefaultCardView(OwnerRequiredMixin, DetailView):
    """
    Default card view.
    """

    model = Card
    context_object_name = "card"
    template_name = "cards/card_default.html"

    @transaction.atomic()
    def post(self, request, pk=None):  # pylint: disable=C0103,W0613
        """
        Set card as default.
        """
        default_cards = Card.objects.filter(
            default=True,
            user=self.request.user,
        )
        for card in default_cards:
            card.default = False
            card.save()

        obj = self.get_object()
        obj.default = True
        obj.save()

        return HttpResponseRedirect(reverse("cards:my-card-list"))


class ContactCardView(SingleObjectMixin, TemplateResponseMixin, View):
    """
    Set or remove card from contacts.
    """

    context_object_name = "card"
    template_name = "cards/card_contact.html"
    model = Card

    def toggle_contact(self):
        """
        Toggle card contact.
        """
        card = self.get_object()
        user = self.request.user

        if user in card.followers.all():
            card.followers.remove(user)
        else:
            card.followers.add(user)

    def get(self, request, *args, **kwargs):
        """
        GET method handler.
        """
        if "pk" in kwargs:
            self.toggle_contact()
            return HttpResponseRedirect(reverse("cards:card-contact"))

        context = {}
        context["cards"] = request.user.favorite_cards.all()

        return self.render_to_response(context)

    def post(self, request, pk=None):  # pylint: disable=C0103,W0613
        """
        Toggle card from contacts.
        """

        self.toggle_contact()

        return HttpResponse("ok")


class SearchListView(ListView):
    """
    Cards search list view.
    """

    context_object_name = "cards"
    template_name = "cards/card_search.html"

    def get_queryset(self):
        keyword = self.request.GET.get("q")

        if keyword == "":
            return Card.objects.none()

        qs = Card.objects.filter(
            Q(cardmetadata__productos_servicios__icontains=keyword)
            | Q(
                cardmetadata__descripcion_productos_servicios__icontains=keyword,  # noqa: E501 pylint: disable=C0301
            )
            | Q(user__full_name__icontains=keyword)
            | Q(user__username__icontains=keyword)
            | Q(name__icontains=keyword)
            | Q(position__icontains=keyword)
            | Q(email__icontains=keyword),
        ).all()

        return qs

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

        context["query"] = self.request.GET.get("q")

        return context
