"""
Rutas nombradas, publicadas hacia el frontend (R19).

Django no tiene un equivalente de Ziggy y las dos bibliotecas del ecosistema
quedaron descartadas por datos: una declara una versión de Django que excluye a
la del proyecto y la otra no se publica desde 2021.

El reemplazo son estas pocas líneas, y salen mejor que las dos. Las rutas viajan
como prop compartida de Inertia —que ya es el canal del servidor al cliente en
cada navegación, sin un script aparte que quede viejo tras un despliegue— y lo
que se publica es una lista explícita, no el mapa completo de URLs de la
aplicación: el panel interno y las rutas de servicio no tienen por qué viajar al
navegador.
"""

from django.urls import get_resolver

#: Rutas que el frontend puede resolver por nombre.
#:
#: La lista es explícita a propósito. Publicar todo lo que Django conoce
#: entregaría también el panel interno y cualquier ruta de servicio que se
#: agregue mañana, sin que nadie lo decida.
#:
#: Tiene que coincidir con la unión de tipos de `frontend/lib/routes.ts`. Hay un
#: test que lo verifica: si se agrega una acá y no allá, la suite lo dice.
PUBLISHED_ROUTES: tuple[str, ...] = (
    'home',
    'login',
    'logout',
    'search_console',
    'onboarding',
    'onboarding.verify',
    'onboarding.dismiss',
    'settings',
    'credential.upload',
    'credential.verify',
    # 'domains' no está: el listado no tiene ruta mientras la interfaz trabaje
    # contra un solo sitio. Ver `config/urls.py`.
    'domain.new',
    'domain.show',
    'domain.check',
    'domain.connect',
    'domain.deactivate',
    'domain.sync',
    'coverage',
    'coverage.inspect',
    'coverage.export',
    'export.download',
    'sitemaps',
    'sitemap.create',
    'batches',
    'batch.show',
    'indexing.create',
    'indexing.batch',
    'indexing.run',
    'indexing.resume',
    'indexing.delete',
    'indexing.request',
    'indexing.evidence',
    'notifications',
    'notifications.read_all',
    'notification.read',
    # La segunda herramienta. `seo` y `seo.connection` no llevan el sitio: son
    # del módulo, como `search_console` y `settings`. Las demás sí, porque los
    # datos son de un sitio concreto.
    'seo',
    'seo.connection',
    'seo.keywords',
    'seo.keyword',
    'seo.pages',
    'seo.page',
    'seo.cannibalization',
    'seo.cannibalization.pages',
    'seo.backfill',
    'seo.keyword.track',
    'profile',
    'profile.preferences',
    'profile.email',
    'profile.password',
    'sessions',
    'sessions.close_others',
    'sessions.close_selected',
    'session.close',
    'api_keys',
    'api_key.create',
    'api_key.revoke',
    'users',
    'user.invite',
    'invitation.accept',
    'user.role',
    'user.deactivate',
)


class UnknownRoute(Exception):
    """Se declaró una ruta publicada que no existe en el ruteo."""


def _uri_and_params(name: str) -> tuple[str, list[str]]:
    """
    Traduce una ruta de Django a un patrón con marcadores nombrados.

    Django guarda internamente la ruta como plantilla de interpolación de
    Python (`domains/%(domain_id)s`). Se la pasa a la forma con llaves porque es
    la que el frontend puede sustituir sin arrastrar una convención de Python al
    navegador.
    """
    possibilities = get_resolver().reverse_dict.getlist(name)
    if not possibilities:
        raise UnknownRoute(
            f'La ruta «{name}» está declarada como publicada pero no existe en el ruteo. '
            f'Revisá PUBLISHED_ROUTES o el nombre en config/urls.py.'
        )
    matches, _pattern, _defaults, _converters = possibilities[0]
    template, params = matches[0]
    uri = '/' + template % {param: f'{{{param}}}' for param in params}
    return uri, list(params)


def published() -> dict[str, dict]:
    """
    Devuelve el mapa que viaja al frontend.

    Se calcula en cada petición y no se memoriza: el costo es recorrer una
    decena de nombres, y una caché acá significaría que el primer proceso que
    arranca fija las rutas para todos los demás.
    """
    return {
        name: {'uri': uri, 'params': params}
        for name, (uri, params) in ((n, _uri_and_params(n)) for n in PUBLISHED_ROUTES)
    }
