"""
Rutas nombradas publicadas al frontend (R19).

Tres cosas se verifican acá, y la tercera es la que justifica el helper propio:
que sólo salga lo declarado, que el panel interno quede afuera, y que la lista
de Python y la unión de tipos de TypeScript no se separen. Si se separan, un
enlace compila y falla recién en producción.
"""

import re
from pathlib import Path

import pytest
from django.conf import settings

from apps.core import routes

ROUTES_TS = Path(settings.BASE_DIR) / 'frontend' / 'lib' / 'routes.ts'


def test_only_the_declared_routes_are_published():
    published = routes.published()
    assert set(published) == set(routes.PUBLISHED_ROUTES)


def test_the_admin_and_the_service_routes_stay_out():
    """
    Publicar el mapa completo entregaría el panel interno y el esquema de la API.

    No es un secreto —el panel exige sesión de todos modos— pero enumerar la
    superficie de administración en cada respuesta al navegador es regalar
    trabajo de reconocimiento a cambio de nada.
    """
    published = routes.published()

    assert 'admin' not in published
    assert 'api-schema' not in published
    assert 'api-docs' not in published
    assert not any(name.startswith('admin') for name in published)


def test_every_published_route_brings_its_uri_and_its_params():
    published = routes.published()

    assert published['login']['uri'] == '/login'
    assert published['login']['params'] == []

    verify = published['credential.verify']
    assert verify['params'] == ['credential_id']
    assert '{credential_id}' in verify['uri']


def test_declaring_a_nonexistent_route_fails_with_a_message_that_says_where_to_look(monkeypatch):
    monkeypatch.setattr(routes, 'PUBLISHED_ROUTES', ('esta-ruta-no-existe',))

    with pytest.raises(routes.UnknownRoute, match='PUBLISHED_ROUTES'):
        routes.published()


def test_the_python_list_and_the_typescript_union_match():
    """
    El único punto donde las dos mitades pueden separarse.

    La unión de TypeScript es lo que hace que un nombre inexistente sea un error
    de compilación en vez de un enlace roto. Ese beneficio depende de que la
    unión diga lo mismo que el servidor publica, y nada en el lenguaje lo
    garantiza: lo garantiza este test.
    """
    source = ROUTES_TS.read_text(encoding='utf-8')
    block = re.search(r'export type RouteName =(.*?)\n\n', source, re.DOTALL)
    assert block, 'No se encontró la unión RouteName en frontend/lib/routes.ts'

    in_typescript = set(re.findall(r"'([^']+)'", block.group(1)))
    in_python = set(routes.PUBLISHED_ROUTES)

    missing_in_ts = in_python - in_typescript
    extra_in_ts = in_typescript - in_python

    assert not missing_in_ts, (
        f'Estas rutas se publican desde Python pero no están en RouteName: '
        f'{sorted(missing_in_ts)}. Agregalas a frontend/lib/routes.ts.'
    )
    assert not extra_in_ts, (
        f'RouteName declara rutas que el servidor no publica: {sorted(extra_in_ts)}. '
        f'Agregalas a PUBLISHED_ROUTES o sacalas de TypeScript.'
    )
