"""
Pruebas de regresion del parche de autenticacion.

Las vistas de envio declaraban `authentication_classes` pero ninguna declaraba
`permission_classes`, y `core/settings.py` no definia `REST_FRAMEWORK`. El
valor por defecto de DRF es `AllowAny`, asi que una peticion sin ninguna
credencial llegaba al cuerpo de la vista y enviaba el mensaje.

Estas pruebas existen para que eso no pueda volver a pasar sin que la suite se
ponga en rojo.
"""

from __future__ import annotations

import pytest
from django.urls import reverse

from keys.models.access_token import AccessToken

pytestmark = pytest.mark.django_db

RUTAS_DE_ENVIO = [
    ("v1:mail-send-template", {"to": ["a@ejemplo.com"], "system": "S", "notification": "N"}),
    ("v1:mail-send", {"from_email": "avisos@ejemplo.com", "to": ["a@ejemplo.com"], "subject": "S", "html": "<p>x</p>"}),
    ("v1:sms-send", {"to": ["+521555"], "body": "hola"}),
    ("v1:whatsapp-send-template", {"to": ["+521555"], "system": "S", "notification": "N"}),
]


@pytest.mark.parametrize(("ruta", "cuerpo"), RUTAS_DE_ENVIO)
def test_sin_credenciales_se_rechaza(api, ruta, cuerpo):
    """Esto devolvia 200 y enviaba el mensaje antes del parche."""
    respuesta = api.post(reverse(ruta), cuerpo, format="json")
    assert respuesta.status_code == 401


@pytest.mark.parametrize(("ruta", "cuerpo"), RUTAS_DE_ENVIO)
def test_con_token_inexistente_se_rechaza(api, ruta, cuerpo):
    api.credentials(HTTP_AUTHORIZATION="Bearer noexiste")
    respuesta = api.post(reverse(ruta), cuerpo, format="json")
    assert respuesta.status_code == 401


@pytest.mark.parametrize(("ruta", "cuerpo"), RUTAS_DE_ENVIO)
def test_con_esquema_incorrecto_se_rechaza(api, ruta, cuerpo):
    """
    Antes, `auth_type != "Bearer"` devolvia None, lo que deja la peticion como
    anonima en lugar de rechazarla, y con AllowAny acababa enviando.
    """
    api.credentials(HTTP_AUTHORIZATION="Token abc123")
    respuesta = api.post(reverse(ruta), cuerpo, format="json")
    assert respuesta.status_code == 401


@pytest.mark.parametrize(("ruta", "cuerpo"), RUTAS_DE_ENVIO)
def test_con_cabecera_malformada_se_rechaza(api, ruta, cuerpo):
    api.credentials(HTTP_AUTHORIZATION="Bearer")
    respuesta = api.post(reverse(ruta), cuerpo, format="json")
    assert respuesta.status_code == 401


def test_el_token_es_de_un_solo_uso(api, clave_api):
    _, token = AccessToken.issue(clave_api)
    api.credentials(HTTP_AUTHORIZATION=f"Bearer {token}")

    cuerpo = {
        "from_email": "avisos@ejemplo.com",
        "to": ["destino@ejemplo.com"],
        "subject": "Prueba",
        "html": "<p>x</p>",
    }

    primera = api.post(reverse("v1:mail-send"), cuerpo, format="json")
    assert primera.status_code == 202

    segunda = api.post(reverse("v1:mail-send"), cuerpo, format="json")
    assert segunda.status_code == 401


def test_un_usuario_desactivado_no_autentica(api, usuario, clave_api):
    usuario.is_active = False
    usuario.save(update_fields=["is_active"])
    _, token = AccessToken.issue(clave_api)
    api.credentials(HTTP_AUTHORIZATION=f"Bearer {token}")

    respuesta = api.post(
        reverse("v1:mail-send"),
        {"from_email": "avisos@ejemplo.com", "to": ["a@ejemplo.com"], "subject": "S", "html": "<p>x</p>"},
        format="json",
    )
    assert respuesta.status_code == 401


def test_el_endpoint_de_token_exige_clave_de_api(api):
    respuesta = api.get("/auth/v1/token/")
    assert respuesta.status_code == 401


def test_el_endpoint_de_token_acepta_una_clave_valida(api, clave_api):
    respuesta = api.get("/auth/v1/token/", HTTP_X_API_KEY=clave_api.raw_key)
    assert respuesta.status_code == 200
    assert respuesta.json()["token"]


def test_una_clave_inactiva_se_rechaza(api, clave_api):
    clave_api.active = False
    clave_api.save(update_fields=["active"])
    respuesta = api.get("/auth/v1/token/", HTTP_X_API_KEY=clave_api.raw_key)
    assert respuesta.status_code == 401


def test_los_endpoints_de_salud_son_publicos(api):
    assert api.get(reverse("v1:health")).status_code == 200
    assert api.get(reverse("v1:health-ready")).status_code == 200
