"""
Models for :mod:`django_notifications application`.

:creationdate: 06/01/2022 11:31
:moduleauthor: François GUÉRIN <fguerin@ville-tourcoing.fr>
:modulename: django_notifications.models
"""
import logging
from typing import Optional

from django.conf import settings
from django.contrib import admin, messages
from django.db.models import QuerySet
from django.http import HttpRequest
from django.template.loader import render_to_string
from django.urls import reverse_lazy
from django.utils.safestring import SafeString, mark_safe
from django.utils.translation import gettext_lazy as _
from solo import admin as solo_admin

import django_notifications.models.config
from django_notifications import models

from . import forms, inlines
from .filters import DelayedMessageListFilter, SentMessageListFilter

logger = logging.getLogger(__name__)
__author__ = "François GUÉRIN"


@admin.register(models.Notification)
class NotificationAdmin(admin.ModelAdmin):
    """Admin for :class:`django_notifications.models.Notification`."""

    list_display = (
        "get_subject",
        "get_dates",
        "get_related_object",
        "get_states",
        "get_recipients",
    )

    date_hierarchy = "created_at"

    list_filter = [
        SentMessageListFilter,
        DelayedMessageListFilter,
    ]

    inlines = [
        inlines.AttachmentInline,
    ]

    form = forms.NotificationAdminForm

    fieldsets = (
        (
            _("Notification"),
            {
                "fields": (
                    "subject",
                    "text_body",
                    "html_body",
                    "state_from",
                    "state_to",
                )
            },
        ),
        (
            _("Related object"),
            {"fields": ("content_type", "object_id")},
        ),
        (
            _("Recipients"),
            {
                "fields": (
                    "recipients",
                    "email_recipients",
                )
            },
        ),
        (_("Delayed"), {"fields": ("delayed_sending_at",)}),
    )

    search_fields = [
        "email_recipients",
        "recipients__email",
    ]

    # region Permissions
    def has_add_permission(self, request: HttpRequest) -> bool:
        """
        Check if user has permission to add a notification.

        .. note:: Always return ``False``.
        """
        return False

    def has_delete_permission(
        self, request: HttpRequest, obj: Optional[models.Notification] = None
    ) -> bool:
        """
        Check if user has permission to delete a notification.

        .. note:: Return `True` for super users.
        """
        user = request.user
        return user.is_superuser

    # endregion Permissions

    # region List display methods
    @admin.display(description=_("Subject"))
    def get_subject(self, obj: models.Notification) -> SafeString:
        """
        Get the subject and the `sent` status.

        :param obj: The notification object
        :return: The subject and the `sent` status.
        """
        return mark_safe(
            render_to_string(
                "django_notifications/admin/snippets/subject.html",
                {
                    "debug": settings.DEBUG,
                    "object": obj,
                },
            )
        )

    @admin.display(description=_("Dates"), ordering="created_at")
    def get_dates(self, obj: models.Notification) -> SafeString:
        """
        Display dates.

        :param obj: :class:`django_notifications.models.Notification` instance
        :return: rendered HTML
        """
        return mark_safe(
            render_to_string(
                "django_notifications/admin/snippets/dates.html",
                {
                    "debug": settings.DEBUG,
                    "object": obj,
                },
            )
        )

    @admin.display(description=_("Related object"))
    def get_related_object(self, obj: models.Notification) -> Optional[SafeString]:
        """
        Display related object.

        :param obj: :class:`django_notifications.models.Notification` instance
        :return: rendered HTML
        """
        if obj.related_object:
            admin_url = reverse_lazy(
                "admin:{app_label}_{model_name}_change".format(
                    app_label=obj.related_object._meta.app_label,  # noqa
                    model_name=obj.related_object._meta.model_name,  # noqa
                ),
                args=(obj.related_object.pk,),
            )
            return mark_safe(
                render_to_string(
                    "django_notifications/admin/snippets/related_object.html",
                    {
                        "debug": settings.DEBUG,
                        "object": obj,
                        "admin_url": admin_url,
                    },
                )
            )
        return None

    @admin.display(description=_("States"), ordering="state_to")
    def get_states(self, obj) -> SafeString:
        """
        Display states.

        :param obj: :class:`django_notifications.models.Notification` instance
        :return: rendered HTML
        """
        return mark_safe(
            render_to_string(
                "django_notifications/admin/snippets/states.html",
                {
                    "debug": settings.DEBUG,
                    "object": obj,
                },
            )
        )

    @admin.display(description=_("Recipients"))
    def get_recipients(self, obj) -> SafeString:
        """
        Display recipients.

        :param obj: :class:`django_notifications.models.Notification` instance
        :return: rendered HTML
        """
        return mark_safe(
            render_to_string(
                "django_notifications/admin/snippets/recipients.html",
                {
                    "debug": settings.DEBUG,
                    "object": obj,
                },
            )
        )

    # endregion List display methods

    # region Action methods
    @admin.action(description=_("Send notifications"))
    def send_notifications(
        self, request: HttpRequest, queryset: QuerySet[models.Notification]
    ) -> None:
        """
        Re-send notifications.

        :param request HTTP request
        :param queryset: Selected notifications
        :return: Nothing
        """
        for notification in queryset:
            _sent = notification.notify(force=True)
            if _sent:
                messages.info(
                    request=request,
                    message=_(
                        "A notification has been sent for {notification}."
                    ).format(notification=notification),
                )
            else:
                messages.warning(
                    request=request,
                    message=_("No notification sent for {notification}.").format(
                        notification=notification
                    ),
                )

    actions = [
        send_notifications,
    ]
    # endregion Action methods


@admin.register(models.Attachment)
class AttachmentAdmin(admin.ModelAdmin):
    """Admin for :class:`django_notifications.models.Attachment`."""

    list_display = [
        "id",
        "notification",
        "attachment_file",
    ]

    def has_add_permission(self, request: HttpRequest) -> bool:  # noqa
        """
        Don't allow adding attachments.

        :param request: HTTP request
        :return: False
        """
        return False

    def has_delete_permission(
        self, request: HttpRequest, obj: models.Attachment = None
    ) -> bool:  # noqa
        """
        Don't allow deleting attachments.

        :param request: HTTP request
        :param obj: :class:`django_notifications.models.Attachment` instance
        :return: False
        """
        return False

    def has_change_permission(
        self, request: HttpRequest, obj: models.Attachment = None
    ) -> bool:  # noqa
        """
        Don't allow changing attachments.

        :param request: HTTP request
        :param obj: :class:`django_notifications.models.Attachment` instance
        :return: False
        """
        return False


@admin.register(django_notifications.models.config.NotificationConfig)
class NotificationConfigAdmin(solo_admin.SingletonModelAdmin):
    """Admin for :class:`django_notifications.models.NotificationConfig`."""

    fieldsets = (
        (
            None,
            {"fields": ("receive_notifications_field", "receive_messages_field")},
        ),
        (
            _("Delayed notifications"),
            {"fields": ("delay_notifications", "delay")},
        ),
        (
            _("Debug"),
            {
                "classes": ("collapse",),
                "fields": ("enable_debug_notifications", "debug_notifications_email"),
            },
        ),
    )
