"""
Abstract view classes for building services.

These view classes are provided as convenience only. If they
don't make sense to use in your service, there is no obligation
to use these views. You are free to build the internal structure
of the service as you like.
"""

import logging
from collections import OrderedDict

from django.contrib import messages
from django.contrib.auth.mixins import LoginRequiredMixin, PermissionRequiredMixin
from django.core.exceptions import ObjectDoesNotExist
from django.db import IntegrityError, models
from django.shortcuts import Http404, redirect, render
from django.urls import reverse_lazy
from django.utils.translation import gettext_lazy as _
from django.views import View
from django.views.generic import DeleteView, UpdateView
from django.views.generic.detail import SingleObjectMixin

from .forms import ServicePasswordModelForm

logger = logging.getLogger(__name__)


class AbstractServiceModel(models.Model):
    user = models.OneToOneField('auth.User',
                                primary_key=True,
                                on_delete=models.CASCADE,
                                related_name='%(app_label)s'
                                )

    class Meta:
        abstract = True

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.credentials = OrderedDict()
        # Should be set with a dict of service credentials (username, password etc) when changed

    def update_password(self, password=None):
        pass

    def reset_password(self):
        pass

class BaseServiceView(LoginRequiredMixin, PermissionRequiredMixin, View):
    """
    Define:
        permission_required
    """
    index_redirect = 'services:services'
    success_url = reverse_lazy(index_redirect)
    model = AbstractServiceModel  # Overload
    service_name = 'base'  # Overload


class ServiceCredentialsViewMixin:
    template_name = 'services/service_credentials.html'


class BaseCreatePasswordServiceAccountView(BaseServiceView, ServiceCredentialsViewMixin):
    def get(self, request):
        logger.debug(f"{self.__class__.__name__} called by user {request.user}")
        try:
            svc_obj = self.model.objects.create(user=request.user)
        except IntegrityError:
            messages.error(request, _("That service account already exists"))
            return redirect(self.index_redirect)

        return render(request, self.template_name, context={'credentials': svc_obj.credentials, 'service': self.service_name, 'view': self})


class ServicesCRUDMixin(SingleObjectMixin):
    def get_object(self, queryset=None):
        """
        Returns the object the view is displaying.
        """
        if queryset is None:
            queryset = self.get_queryset()

        try:
            return queryset.get(user__pk=self.request.user.pk)
        except ObjectDoesNotExist as e:
            raise Http404 from e


class BaseDeactivateServiceAccountView(ServicesCRUDMixin, BaseServiceView, DeleteView):
    template_name = 'services/service_confirm_delete.html'


class BaseSetPasswordServiceAccountView(ServicesCRUDMixin, BaseServiceView, UpdateView):
    template_name = 'services/service_password.html'
    form_class = ServicePasswordModelForm  # You should overload this with a subclass

    def post(self, request, *args, **kwargs):
        result = super().post(request, *args, **kwargs)
        if self.get_form().is_valid():
            messages.success(request, _(f"Successfully set your {self.service_name} password"))
        return result


class BaseResetPasswordServiceAccountView(ServicesCRUDMixin, BaseServiceView, ServiceCredentialsViewMixin):
    """
    Set a random password
    """
    def get(self, request):
        svc_obj = self.get_object()
        svc_obj.reset_password()
        return render(request, self.template_name, context={'credentials': svc_obj.credentials, 'service': self.service_name, 'view': self})
