"""
Qué idioma y qué tema le corresponden a esta petición.

**La cuenta es la fuente de verdad y la cookie es su espejo.** Las dos existen
porque se leen en momentos distintos: la cuenta manda en cuanto hay sesión, pero
el HTML inicial se pinta antes de que exista una prop de Inertia, y el ingreso
no tiene cuenta de la cual leer nada. Sin la cookie, quien usa el producto en
español vería la pantalla de ingreso en inglés cada vez.

**Sin cookie, inglés.** No se mira `Accept-Language`: el idioma inicial es una
decisión del producto y no del navegador de turno, y así es reproducible en un
test en vez de depender de con qué configuración se abrió la máquina.
"""

from dataclasses import dataclass

from apps.accounts.models import Language, Theme
from apps.core.tables import from_slug, slug

#: Nombres de las cookies espejo. Sin prefijo del producto: son de esta
#: instalación y no viajan a ningún lado.
LANGUAGE_COOKIE = 'app_language'
THEME_COOKIE = 'app_theme'

#: Un año. La preferencia no caduca sola: quien eligió español lo eligió hasta
#: que diga lo contrario, y una cookie que vence obligaría a volver a elegir sin
#: que nadie haya cambiado nada.
COOKIE_MAX_AGE = 60 * 60 * 24 * 365


@dataclass(frozen=True)
class Preferences:
    """
    Las dos preferencias, ya en su forma pública (`en`, `es-ar`, `dark`).

    Viajan en forma pública y no con el valor de base porque el destino es el
    atributo `lang` del documento y una clase de CSS: `ES_AR` no sirve en
    ninguno de los dos.
    """

    language: str
    theme: str


def resolve(request) -> Preferences:
    """
    La preferencia que rige esta petición.

    Con sesión gana la cuenta, siempre: si alguien cambió su idioma desde otra
    máquina, la cookie de ésta está vieja y no tiene por qué ganarle a lo que la
    persona eligió.
    """
    user = getattr(request, 'user', None)

    if user is not None and user.is_authenticated:
        return Preferences(language=slug(user.language), theme=slug(user.theme))

    return Preferences(
        language=_from_cookie(request, LANGUAGE_COOKIE, Language, Language.EN),
        theme=_from_cookie(request, THEME_COOKIE, Theme, Theme.SYSTEM),
    )


def remember(response, preferences: Preferences):
    """
    Deja la preferencia en el navegador para la próxima visita sin sesión.

    No es `httponly`: el guion que resuelve «seguí al sistema» corre antes que
    cualquier módulo y necesita poder leerla. No hay nada sensible acá —qué
    idioma lee alguien no es un secreto— y ponerla fuera del alcance del guion
    obligaría a un viaje al servidor para saber de qué color pintar la página.
    """
    for name, value in (
        (LANGUAGE_COOKIE, preferences.language),
        (THEME_COOKIE, preferences.theme),
    ):
        response.set_cookie(name, value, max_age=COOKIE_MAX_AGE, samesite='Lax')
    return response


def context(request) -> dict:
    """
    Lo que la plantilla base necesita para pintar el documento sin parpadeo.

    `theme_class` sale vacío cuando la preferencia es «seguí al sistema»: ése es
    el único caso que el servidor no puede decidir —no sabe qué contestó
    `prefers-color-scheme`— y lo resuelve el guion inline de `base.html`.
    """
    preferences = resolve(request)

    return {
        'language': preferences.language,
        'theme': preferences.theme,
        'theme_class': 'dark' if preferences.theme == slug(Theme.DARK) else '',
    }


def _from_cookie(request, name: str, choices, fallback: str) -> str:
    """
    El valor público que trae la cookie, o el de fábrica si trae cualquier cosa.

    Se valida contra el enumerado y no se usa tal cual: la cookie la puede
    escribir cualquiera, y un valor inventado terminaría como clase de CSS o
    como nombre de catálogo a cargar.
    """
    value = request.COOKIES.get(name, '')
    return slug(from_slug(value, choices.values) or fallback)
