"""
Una pantalla que dibuja trabajo en curso tiene que actualizarse sola.

`useBatchPolling` es el único mecanismo de actualización del producto (RT-13), y
que exista no alcanza: hay que acordarse de usarlo en cada pantalla nueva, y
olvidarlo **no rompe nada visible**. La pantalla dibuja bien, contesta bien y
queda clavada en el estado que tenía al cargar.

Eso fue exactamente lo que pasó con la ficha de indexación: se confirmaba el
borrador, el envío salía, corría entero y terminaba, y ahí no pasaba nada —ni
progreso, ni cambio de estado, ni el aviso final—. Quien lo miraba concluía que
el producto estaba roto. Se enteraba recargando a mano.

Ninguna prueba podía fallar por eso, porque no hay nada roto que probar: lo que
falta es una llamada. Este guard existe para que la próxima pantalla que dibuje
un lote no pueda olvidarla en silencio.
"""

import re
from pathlib import Path

import pytest

PAGES = Path(__file__).resolve().parents[2] / 'frontend' / 'pages'

#: Lo que delata a una pantalla que muestra el estado de un lote. Son las piezas
#: que sólo tienen sentido sobre algo que avanza: el badge del estado, la barra
#: de progreso y el titular que explica cómo terminó.
LIVE_PIECES = ('BatchStateBadge', 'BatchStateHeadline', 'BatchProgress', 'IndexingStateBadge')

POLLING = 'useBatchPolling'


def screens() -> list[Path]:
    return sorted(PAGES.rglob('*.tsx'))


def draws_live_work(body: str) -> bool:
    """
    Si la pantalla importa alguna de las piezas de trabajo en curso.

    Se mira la importación y no cualquier mención: el nombre aparece también en
    comentarios que explican por qué algo **no** la usa, y una regla que se
    dispara al nombrarla deja el código sin poder documentarse. Es la misma
    lección de `test_request_reading.py`.
    """
    imports = re.findall(r'^import\s+.*?from\s+.*$', body, flags=re.MULTILINE)
    return any(piece in line for line in imports for piece in LIVE_PIECES)


def test_every_screen_that_draws_a_batch_updates_itself():
    missing = [
        path.relative_to(PAGES).as_posix()
        for path in screens()
        if draws_live_work(path.read_text(encoding='utf-8'))
        and POLLING not in path.read_text(encoding='utf-8')
    ]

    assert not missing, (
        'Estas pantallas dibujan el estado de un lote y no se actualizan solas, '
        f'así que se quedan clavadas en lo que había al cargar: {missing}. '
        f'Les falta {POLLING}.'
    )


def test_the_guard_would_notice_a_screen_that_forgot_it():
    """
    Una red de contención que no atrapa nada pasa en verde sobre un repo roto.

    Se comprueba con una pantalla inventada, no tocando una real: el guard tiene
    que reaccionar a la forma, no a un archivo en particular.
    """
    forgetful = "import { BatchProgress } from '@/components/BatchProgress'\n"

    assert draws_live_work(forgetful)
    assert POLLING not in forgetful


def test_a_screen_that_only_names_the_piece_in_a_comment_is_left_alone():
    """
    Explicar por qué una pantalla no sondea no puede convertirla en infractora.
    """
    explaining = '// Esta pantalla no dibuja BatchProgress: el lote ya cerró.\n'

    assert not draws_live_work(explaining)


@pytest.mark.parametrize('piece', LIVE_PIECES)
def test_each_watched_piece_exists(piece: str):
    """
    Los nombres de arriba están escritos a mano. Si uno se renombra, el guard
    deja de mirarlo y nadie se entera: seguiría verde sin cubrir nada.
    """
    components = PAGES.parent / 'components'
    found = any(piece in path.read_text(encoding='utf-8') for path in components.rglob('*.tsx'))

    assert found, f'«{piece}» ya no existe: el guard está vigilando un nombre muerto.'
