#
# Copyright 2017,2020
# - Skia <skia@libskia.so>
# - Sli <antoine.bartuccio@gmail.com>
#
# Ce fichier fait partie du site de l'Association des Étudiants de l'UTBM,
# http://ae.utbm.fr.
#
# This program is free software; you can redistribute it and/or modify it under
# the terms of the GNU General Public License a published by the Free Software
# Foundation; either version 3 of the License, or (at your option) any later
# version.
#
# This program is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
# details.
#
# You should have received a copy of the GNU General Public License along with
# this program; if not, write to the Free Sofware Foundation, Inc., 59 Temple
# Place - Suite 330, Boston, MA 02111-1307, USA.
#
#

from datetime import date

from django import forms
from django.conf import settings
from django.contrib import messages
from django.contrib.auth.mixins import LoginRequiredMixin, UserPassesTestMixin
from django.contrib.messages.views import SuccessMessageMixin
from django.core.exceptions import PermissionDenied
from django.db import IntegrityError
from django.forms.models import modelform_factory
from django.http import Http404, HttpResponseRedirect
from django.shortcuts import get_object_or_404, redirect
from django.urls import reverse, reverse_lazy
from django.utils.functional import cached_property
from django.utils.translation import gettext_lazy as _
from django.views.generic import DetailView, RedirectView, TemplateView, View
from django.views.generic.edit import CreateView, DeleteView, UpdateView

from club.models import Club
from core.auth.mixins import CanEditMixin, CanEditPropMixin, CanViewMixin
from core.models import User
from core.views.forms import SelectDate
from core.views.mixins import TabedViewMixin
from core.views.widgets.ajax_select import AutoCompleteSelectUser
from trombi.models import Trombi, TrombiClubMembership, TrombiComment, TrombiUser


class TrombiTabsMixin(TabedViewMixin):
    def get_tabs_title(self):
        return _("Trombi")

    def get_list_of_tabs(self):
        tab_list = []
        tab_list.append(
            {"url": reverse("trombi:user_tools"), "slug": "tools", "name": _("Tools")}
        )
        if hasattr(self.request.user, "trombi_user"):
            tab_list.append(
                {
                    "url": reverse("trombi:profile"),
                    "slug": "profile",
                    "name": _("My profile"),
                }
            )
            tab_list.append(
                {
                    "url": reverse("trombi:pictures"),
                    "slug": "pictures",
                    "name": _("My pictures"),
                }
            )
        if (
            hasattr(self.request.user, "trombi_user")
            and self.request.user.trombi_user.trombi
        ):
            trombi = self.request.user.trombi_user.trombi
            if self.request.user.is_owner(trombi):
                tab_list.append(
                    {
                        "url": reverse(
                            "trombi:detail", kwargs={"trombi_id": trombi.id}
                        ),
                        "slug": "admin_tools",
                        "name": _("Admin tools"),
                    }
                )
        return tab_list


class UserIsInATrombiMixin(View):
    """Check if the requested user has a trombi_user attribute."""

    def dispatch(self, request, *args, **kwargs):
        if not hasattr(self.request.user, "trombi_user"):
            raise Http404()

        return super().dispatch(request, *args, **kwargs)


class TrombiForm(forms.ModelForm):
    class Meta:
        model = Trombi
        fields = [
            "subscription_deadline",
            "comments_deadline",
            "max_chars",
            "show_profiles",
        ]
        widgets = {"subscription_deadline": SelectDate, "comments_deadline": SelectDate}


class TrombiCreateView(UserPassesTestMixin, CreateView):
    """Create a trombi for a club."""

    model = Trombi
    form_class = TrombiForm
    template_name = "core/create.jinja"

    @cached_property
    def club(self):
        return get_object_or_404(Club, id=self.kwargs["club_id"])

    def test_func(self):
        return self.request.user.can_edit(self.club)

    def post(self, request, *args, **kwargs):
        """Affect club."""
        form = self.get_form()
        if form.is_valid():
            form.instance.club = self.club
            ret = self.form_valid(form)
            return ret
        else:
            return self.form_invalid(form)


class TrombiEditView(
    CanEditPropMixin, TrombiTabsMixin, SuccessMessageMixin, UpdateView
):
    model = Trombi
    form_class = TrombiForm
    template_name = "core/edit.jinja"
    pk_url_kwarg = "trombi_id"
    current_tab = "admin_tools"
    success_message = _("Trombi modified")


class AddUserForm(forms.Form):
    user = forms.ModelChoiceField(
        label=_("Select user"),
        help_text=None,
        required=True,
        widget=AutoCompleteSelectUser,
        queryset=User.objects.all(),
    )


class TrombiDetailView(CanEditMixin, TrombiTabsMixin, DetailView):
    model = Trombi
    template_name = "trombi/detail.jinja"
    pk_url_kwarg = "trombi_id"
    current_tab = "admin_tools"

    def post(self, request, *args, **kwargs):
        self.object = self.get_object()
        form = AddUserForm(request.POST)
        if form.is_valid():
            try:
                TrombiUser(user=form.cleaned_data["user"], trombi=self.object).save()
                messages.success(self.request, _("User added to the trombi"))
            except IntegrityError:  # We don't care about duplicate keys
                messages.error(self.request, _("User couldn't be added to the trombi"))
        return super().get(request, *args, **kwargs)

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


class TrombiExportView(CanEditMixin, TrombiTabsMixin, DetailView):
    model = Trombi
    template_name = "trombi/export.jinja"
    pk_url_kwarg = "trombi_id"
    current_tab = "admin_tools"


class TrombiDeleteUserView(
    CanEditPropMixin, TrombiTabsMixin, SuccessMessageMixin, DeleteView
):
    model = TrombiUser
    pk_url_kwarg = "user_id"
    template_name = "core/delete_confirm.jinja"
    current_tab = "admin_tools"
    success_message = _("User removed from the trombi")

    def get_success_url(self):
        return reverse("trombi:detail", kwargs={"trombi_id": self.object.trombi.id})


class TrombiModerateCommentsView(CanEditPropMixin, TrombiTabsMixin, DetailView):
    model = Trombi
    template_name = "trombi/comment_moderation.jinja"
    pk_url_kwarg = "trombi_id"
    current_tab = "admin_tools"

    def get_context_data(self, **kwargs):
        kwargs = super().get_context_data(**kwargs)
        kwargs["comments"] = TrombiComment.objects.filter(
            is_moderated=False, author__trombi__id=self.object.id
        ).exclude(target__user__id=self.request.user.id)
        return kwargs


class TrombiModerateForm(forms.Form):
    reason = forms.CharField(help_text=_("Explain why you rejected the comment"))
    action = forms.CharField(initial="delete", widget=forms.widgets.HiddenInput)


class TrombiModerateCommentView(DetailView):
    model = TrombiComment
    template_name = "core/edit.jinja"
    pk_url_kwarg = "comment_id"

    def dispatch(self, request, *args, **kwargs):
        self.object = self.get_object()
        if not request.user.is_owner(self.object.author.trombi):
            raise Http404()
        return super().dispatch(request, *args, **kwargs)

    def post(self, request, *args, **kwargs):
        if "action" in request.POST:
            if request.POST["action"] == "accept":
                self.object.is_moderated = True
                self.object.save()
                messages.success(self.request, _("Comment accepted"))
                return redirect(
                    reverse(
                        "trombi:moderate_comments",
                        kwargs={"trombi_id": self.object.author.trombi.id},
                    )
                )
            elif request.POST["action"] == "reject":
                messages.success(self.request, _("Comment rejected"))
                return super().get(request, *args, **kwargs)
            elif request.POST["action"] == "delete" and "reason" in request.POST:
                messages.success(self.request, _("Comment removed"))
                self.object.author.user.email_user(
                    subject="[%s] %s" % (settings.SITH_NAME, _("Rejected comment")),
                    message=_(
                        'Your comment to %(target)s on the Trombi "%(trombi)s" '
                        "was rejected for the following reason: %(reason)s\n\n"
                        "Your comment was:\n\n%(content)s"
                    )
                    % {
                        "target": self.object.target.user.get_display_name(),
                        "trombi": self.object.author.trombi,
                        "reason": request.POST["reason"],
                        "content": self.object.content,
                    },
                )
                self.object.delete()
                return redirect(
                    reverse(
                        "trombi:moderate_comments",
                        kwargs={"trombi_id": self.object.author.trombi.id},
                    )
                )
        raise Http404

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


# User side


class TrombiModelChoiceField(forms.ModelChoiceField):
    def label_from_instance(self, obj):
        return _("%(name)s (deadline: %(date)s)") % {
            "name": str(obj),
            "date": str(obj.subscription_deadline),
        }


class UserTrombiForm(forms.Form):
    trombi = TrombiModelChoiceField(
        Trombi.availables.all(),
        required=False,
        label=_("Select trombi"),
        help_text=_(
            "This allows you to subscribe to a Trombi. "
            "Be aware that you can subscribe only once, so don't play with that, "
            "or you will expose yourself to the admins' wrath!"
        ),
    )


class UserTrombiToolsView(LoginRequiredMixin, TrombiTabsMixin, TemplateView):
    """Display a user's trombi tools."""

    template_name = "trombi/user_tools.jinja"
    current_tab = "tools"

    def post(self, request, *args, **kwargs):
        self.form = UserTrombiForm(request.POST)
        if self.form.is_valid():
            if hasattr(request.user, "trombi_user"):
                trombi_user = request.user.trombi_user
                trombi_user.trombi = self.form.cleaned_data["trombi"]
            else:
                trombi_user = TrombiUser(
                    user=request.user, trombi=self.form.cleaned_data["trombi"]
                )
            trombi_user.save()
        return super().get(request, *args, **kwargs)

    def get_context_data(self, **kwargs):
        kwargs = super().get_context_data(**kwargs)
        kwargs["user"] = self.request.user
        if not (
            hasattr(self.request.user, "trombi_user")
            and self.request.user.trombi_user.trombi
        ):
            kwargs["subscribe_form"] = UserTrombiForm()
        else:
            kwargs["trombi"] = self.request.user.trombi_user.trombi
            kwargs["date"] = date
        return kwargs


class UserTrombiEditPicturesView(
    TrombiTabsMixin, UserIsInATrombiMixin, SuccessMessageMixin, UpdateView
):
    model = TrombiUser
    fields = ["profile_pict", "scrub_pict"]
    template_name = "core/edit.jinja"
    current_tab = "pictures"
    success_message = _("User modified")

    def get_object(self):
        return self.request.user.trombi_user

    def get_success_url(self):
        return reverse("trombi:user_tools")


class UserTrombiEditProfileView(
    TrombiTabsMixin, UserIsInATrombiMixin, SuccessMessageMixin, UpdateView
):
    model = User
    form_class = modelform_factory(
        User,
        fields=[
            "second_email",
            "phone",
            "department",
            "dpt_option",
            "quote",
            "parent_address",
        ],
        labels={
            "second_email": _("Personal email (not UTBM)"),
            "phone": _("Phone"),
            "parent_address": _("Native town"),
        },
    )
    template_name = "trombi/edit_profile.jinja"
    current_tab = "profile"
    success_message = _("User modified")

    def get_object(self):
        return self.request.user

    def get_success_url(self):
        return reverse("trombi:user_tools")


class UserTrombiResetClubMembershipsView(
    UserIsInATrombiMixin, SuccessMessageMixin, RedirectView
):
    permanent = False
    success_message = _("User modified")

    def get(self, request, *args, **kwargs):
        user = self.request.user.trombi_user
        user.make_memberships()
        return redirect(self.get_success_url())

    def get_success_url(self):
        return reverse("trombi:profile")


class UserTrombiDeleteMembershipView(
    TrombiTabsMixin, CanEditMixin, SuccessMessageMixin, DeleteView
):
    model = TrombiClubMembership
    pk_url_kwarg = "membership_id"
    template_name = "core/delete_confirm.jinja"
    success_url = reverse_lazy("trombi:profile")
    current_tab = "profile"
    success_message = _("User removed from trombi")


# Used by admins when someone does not have every club in his list
class UserTrombiAddMembershipView(TrombiTabsMixin, CreateView):
    model = TrombiClubMembership
    template_name = "core/edit.jinja"
    fields = ["club", "role", "start", "end"]
    pk_url_kwarg = "user_id"
    current_tab = "profile"

    def dispatch(self, request, *arg, **kwargs):
        self.trombi_user = get_object_or_404(TrombiUser, pk=kwargs["user_id"])
        if not self.trombi_user.trombi.is_owned_by(request.user):
            raise PermissionDenied()

        return super().dispatch(request, *arg, **kwargs)

    def form_valid(self, form):
        membership = form.save(commit=False)
        membership.user = self.trombi_user
        membership.save()
        return HttpResponseRedirect(self.get_success_url())

    def get_success_url(self):
        return reverse(
            "trombi:detail", kwargs={"trombi_id": self.trombi_user.trombi.id}
        )


class UserTrombiEditMembershipView(
    CanEditMixin, TrombiTabsMixin, SuccessMessageMixin, UpdateView
):
    model = TrombiClubMembership
    pk_url_kwarg = "membership_id"
    fields = ["role", "start", "end"]
    template_name = "core/edit.jinja"
    current_tab = "profile"
    success_message = _("User modified")

    def get_success_url(self):
        return super().get_success_url()


class UserTrombiProfileView(TrombiTabsMixin, DetailView):
    model = TrombiUser
    pk_url_kwarg = "user_id"
    template_name = "trombi/user_profile.jinja"
    context_object_name = "trombi_user"
    current_tab = "tools"

    def get(self, request, *args, **kwargs):
        self.object = self.get_object()

        if request.user.is_anonymous:
            raise PermissionDenied()

        if (
            self.object.trombi.id != request.user.trombi_user.trombi.id
            or self.object.user.id == request.user.id
            or not self.object.trombi.show_profiles
        ):
            raise Http404()
        return super().get(request, *args, **kwargs)


class TrombiCommentFormView(LoginRequiredMixin, SuccessMessageMixin, View):
    """Create/edit a trombi comment."""

    model = TrombiComment
    fields = ["content"]
    template_name = "trombi/comment.jinja"
    success_message = _("Comment added")

    def get_form_class(self):
        self.trombi = self.request.user.trombi_user.trombi
        if date.today() <= self.trombi.subscription_deadline:
            raise Http404(
                _(
                    "You can not yet write comment, you must wait for "
                    "the subscription deadline to be passed."
                )
            )
        if self.trombi.comments_deadline < date.today():
            raise Http404(
                _("You can not write comment anymore, the deadline is already passed.")
            )
        return modelform_factory(
            self.model,
            fields=self.fields,
            widgets={
                "content": forms.widgets.Textarea(
                    attrs={"maxlength": self.trombi.max_chars}
                )
            },
            help_texts={
                "content": _("Maximum characters: %(max_length)s")
                % {"max_length": self.trombi.max_chars}
            },
        )

    def get_success_url(self):
        return reverse("trombi:user_tools")

    def get_context_data(self, **kwargs):
        kwargs = super().get_context_data(**kwargs)
        if "user_id" in self.kwargs:
            kwargs["target"] = get_object_or_404(TrombiUser, id=self.kwargs["user_id"])
        else:
            kwargs["target"] = self.object.target
        return kwargs


class TrombiCommentCreateView(TrombiCommentFormView, CreateView):
    def form_valid(self, form):
        target = get_object_or_404(TrombiUser, id=self.kwargs["user_id"])
        author = self.request.user.trombi_user
        form.instance.author = author
        form.instance.target = target
        # Check that this combination does not already have a comment
        old = TrombiComment.objects.filter(author=author, target=target).first()
        if old:
            old.content = form.instance.content
            old.save()
            return HttpResponseRedirect(self.get_success_url())
        return super().form_valid(form)


class TrombiCommentEditView(TrombiCommentFormView, CanViewMixin, UpdateView):
    pk_url_kwarg = "comment_id"

    def form_valid(self, form):
        form.instance.is_moderated = False
        return super().form_valid(form)
