"""
La pantalla de avisos y sus dos acciones.

Marcar leído es una acción y no un efecto de abrir la lista. Marcar todo al
entrar vaciaría el número sin que nadie hubiera leído nada, y quien entró de
paso perdería la única señal de que había algo pendiente.
"""

from django.contrib.auth.decorators import login_required
from django.shortcuts import redirect
from django.views.decorators.http import require_http_methods
from inertia import render, scroll

from apps.core.tables import Filter, TableSpec, filter_options, table_props
from apps.notifications import services
from apps.notifications.models import Notification, NotificationKind, NotificationLevel

#: Qué se puede filtrar y ordenar.
#:
#: El orden por omisión es lo más nuevo arriba y no se ofrece cambiarlo por
#: estado de lectura: para eso está el filtro, que además queda en la dirección.
NOTIFICATIONS = TableSpec(
    sortable={'date': ('-created_at',)},
    default_sort='date',
    filters=(
        Filter(param='kind', lookup='kind', choices=tuple(NotificationKind.values)),
        Filter(param='level', lookup='level', choices=tuple(NotificationLevel.values)),
    ),
    # Tandas más chicas que las 50 del resto: acá no se pagina, se agrega al
    # final. Con 50 la primera tanda ya cubre casi toda cuenta y el botón de
    # cargar más no aparecería nunca; con 20 la lista abre liviana y crece
    # cuando alguien la pide.
    per_page=20,
)


@login_required
def index(request):
    notifications = services.company_notifications().select_related('domain')

    # «Sin leer» no es un filtro más de la tabla: es una vista distinta de lo
    # mismo, y la que trae a la persona acá. Va aparte para que el enlace del
    # menú pueda apuntar directo a ella.
    only_unread = request.GET.get('unread') == '1'
    if only_unread:
        notifications = notifications.filter(read_at=None)

    page = table_props(
        request,
        notifications,
        NOTIFICATIONS,
        serialize=_row,
        # El recorte no es un filtro declarado sino otra vista de lo mismo, así
        # que viaja aparte para que sobreviva a paginar: sin esto, la página dos
        # de «sin leer» devolvería la página dos de todo sin que nada avisara.
        extra_query={'unread': '1'} if only_unread else None,
    )

    return render(
        request,
        'Notifications',
        props={
            # Los avisos no se paginan: se cargan de a tandas y la siguiente se
            # **agrega** a la anterior. Una lista de avisos se recorre hacia
            # abajo buscando desde cuándo pasa algo, y paginarla obliga a
            # recordar qué había en la página anterior.
            #
            # Lo hace `scroll()` y no una acumulación en el cliente: el cliente
            # tendría que decidir cuándo lo acumulado dejó de valer —al cambiar
            # un filtro, al marcar una leída— y ésa es exactamente la clase de
            # estado que se desincroniza en silencio.
            'notifications': scroll(
                page,
                {
                    'pageName': 'page',
                    'currentPage': page['page'],
                    'previousPage': page['page'] - 1 if page['page'] > 1 else None,
                    'nextPage': page['page'] + 1 if page['page'] < page['pages'] else None,
                },
                wrapper='rows',
            ),
            'unread': services.company_unread_count(),
            # Cuántos de los sin leer piden algo, que es la pregunta que trae acá.
            #
            # Viaja como prop de la pantalla además de compartida porque en ésta
            # —y sólo en ésta— la prop `notifications` es la tabla paginada, que
            # tapa a la compartida del mismo nombre. Leerlo de la compartida
            # funcionaría en las otras trece pantallas y daría cero justo en la
            # que lo necesita.
            'unread_action': services.company_unread_action_count(),
            'total': services.company_notifications().count(),
            'only_unread': only_unread,
            'options': {
                'kind': filter_options(NotificationKind.choices),
                'level': filter_options(NotificationLevel.choices),
            },
        },
    )


@login_required
@require_http_methods(['POST'])
def read(request, notification_id):
    services.mark_company_read(notification_id)
    # Se vuelve a donde estaba, con su filtro: marcar uno como leído no es
    # motivo para perder el recorte que la persona venía mirando.
    return redirect(request.META.get('HTTP_REFERER') or 'notifications')


@login_required
@require_http_methods(['POST'])
def read_all(request):
    """
    Marca todo como leído y vuelve a donde estaba, igual que marcar uno solo.

    Volver siempre a la lista sin filtros dejaría a las dos acciones de esta
    pantalla comportándose distinto sin motivo. Y si quien la usó estaba en el
    recorte de «sin leer», lo que encuentra es su estado vacío —«no te queda
    ninguno»—, que es exactamente lo que acaba de conseguir.
    """
    services.mark_all_company_read()
    return redirect(request.META.get('HTTP_REFERER') or 'notifications')


def _row(notification: Notification) -> dict:
    """
    Lo que la pantalla necesita de un aviso.

    Viajan la clave del texto y sus datos, no la frase: el catálogo del cliente
    la arma en el idioma de quien está leyendo. `action_path` sí viaja resuelto,
    porque es una dirección y no una oración.
    """
    return {
        'id': str(notification.id),
        'kind': notification.kind,
        'level': notification.level,
        'text_key': notification.text_key,
        'params': notification.params,
        'action_path': notification.action_path,
        'hostname': notification.domain.hostname if notification.domain else None,
        'created_at': notification.created_at.isoformat(),
        'read_at': notification.read_at.isoformat() if notification.read_at else None,
    }
