"""
El recorte de la tabla de cliente, que es donde el error no se ve.

Una tabla mal filtrada **no parece rota**: muestra filas, ordena, pagina y dice
un total. Por eso las dos reglas de acá se defienden aparte del dibujo, y por eso
la suite las vale: la única señal de que estaban mal fue que alguien eligiera dos
valores a mano y se encontrara la tabla vacía con los contadores del filtro
diciendo que había cuarenta y siete filas.

Las dos que se fijan son las dos mitades del mismo defecto:

- **cómo se compara** un valor elegido contra el de la celda, y
- **que la comparación se aplique**, que es lo que efectivamente faltaba.

Se ejercita el TypeScript real en Node. Todo se pide de una sola vez: Node tarda
más en arrancar que en contestar.
"""

import pytest

from tests.frontend.harness import call, needs_frontend, resolve

TABLE = '@/components/ClientDataTable'

pytestmark = needs_frontend

#: Cómo declara sus columnas una vista: sin `filterFn`, que es justamente lo que
#: nadie tiene que acordarse de escribir.
COLUMNS = [
    {'id': 'url', 'header': 'URL'},
    {'id': 'state', 'header': 'State'},
    {'id': 'family', 'header': 'Slice'},
]

FILTERS = [
    {'column': 'state', 'title': 'State', 'selection': 'multiple', 'options': []},
    {'column': 'family', 'title': 'Slice', 'selection': 'single', 'options': []},
]

REQUESTS = {
    # --- Cómo se compara -----------------------------------------------------
    'nothing_selected_filters_nothing': call(TABLE, 'isSelected', 'INDEXED', []),
    'one_value_matches_itself': call(TABLE, 'isSelected', 'INDEXED', ['INDEXED']),
    'one_value_rejects_another': call(TABLE, 'isSelected', 'UNKNOWN', ['INDEXED']),
    # El caso que el usuario encontró a mano: dos valores del mismo eje son
    # alternativas, no requisitos.
    'two_values_match_the_first': call(
        TABLE, 'isSelected', 'UNKNOWN', ['UNKNOWN', 'DISCOVERED_NOT_INDEXED']
    ),
    'two_values_match_the_second': call(
        TABLE, 'isSelected', 'DISCOVERED_NOT_INDEXED', ['UNKNOWN', 'DISCOVERED_NOT_INDEXED']
    ),
    'two_values_reject_a_third': call(
        TABLE, 'isSelected', 'INDEXED', ['UNKNOWN', 'DISCOVERED_NOT_INDEXED']
    ),
    # El defecto silencioso: comparar por subcadena traía las no indexadas
    # adentro de «indexadas», y eso se ve como una tabla que funciona.
    'a_state_is_not_a_substring_of_another': call(
        TABLE, 'isSelected', 'CRAWLED_NOT_INDEXED', ['INDEXED']
    ),
    'nor_the_other_way_around': call(TABLE, 'isSelected', 'INDEXED', ['CRAWLED_NOT_INDEXED']),
    # --- Que la comparación se aplique ---------------------------------------
    'columns_with_faceted_filters': call(TABLE, 'withSelectedFilter', COLUMNS, FILTERS),
    'columns_without_any_filter': call(TABLE, 'withSelectedFilter', COLUMNS, []),
    'a_declared_filter_is_respected': call(
        TABLE,
        'withSelectedFilter',
        [{'id': 'state', 'filterFn': 'includesSelected'}],
        FILTERS,
    ),
}


@pytest.fixture(scope='module')
def answers() -> dict:
    return resolve(REQUESTS)


# --- Cómo se compara --------------------------------------------------------


def test_with_nothing_selected_the_filter_lets_everything_through(answers):
    """Un filtro sin nada elegido no es un filtro que no deja pasar nada."""
    assert answers['nothing_selected_filters_nothing'] is True


def test_one_value_keeps_its_own_rows_and_only_esas(answers):
    assert answers['one_value_matches_itself'] is True
    assert answers['one_value_rejects_another'] is False


def test_two_values_of_the_same_axis_are_alternatives_and_not_requirements(answers):
    """
    **El defecto que se vio en pantalla.**

    Pedir «sin consultar» y «descubiertas» es pedir las dos, no las filas que
    sean las dos a la vez, que no existen. Con la comparación equivocada la tabla
    quedaba vacía mientras el propio filtro mostraba los contadores al lado.
    """
    assert answers['two_values_match_the_first'] is True
    assert answers['two_values_match_the_second'] is True
    assert answers['two_values_reject_a_third'] is False


def test_a_state_never_matches_another_that_contains_its_name(answers):
    """
    **El defecto que no se veía**, y el peor de los dos.

    Comparando por subcadena, elegir «indexadas» traía además
    `CRAWLED_NOT_INDEXED` y `DISCOVERED_NOT_INDEXED`, porque las dos contienen
    `INDEXED`. La tabla mostraba filas y un total, así que nada delataba que el
    recorte estaba diciendo lo contrario de lo que se pidió.
    """
    assert answers['a_state_is_not_a_substring_of_another'] is False
    assert answers['nor_the_other_way_around'] is False


# --- Que la comparación se aplique ------------------------------------------


def test_every_column_with_a_faceted_filter_gets_the_comparison_imposed(answers):
    """
    La tabla se lo impone a la columna; la vista no tiene que acordarse.

    Es la mitad que faltaba de verdad: la comparación correcta existía y estaba
    registrada, pero las columnas de Cobertura no la declaraban, así que TanStack
    elegía una mirando el tipo del dato de la celda. Declararlo en cada vista era
    la manera de que en algún momento faltara.
    """
    columns = {column['id']: column for column in answers['columns_with_faceted_filters']}

    assert columns['state']['filterFn'] == 'includesSelected'
    assert columns['family']['filterFn'] == 'includesSelected'


def test_a_column_without_a_faceted_filter_is_left_alone(answers):
    """
    Sólo las que tienen filtro facetado.

    Una columna que nadie filtra por enumeración no tiene por qué recibir un
    comparador de enumeración: el buscador general la sigue mirando como texto.
    """
    columns = {column['id']: column for column in answers['columns_with_faceted_filters']}

    assert 'filterFn' not in columns['url']


def test_without_faceted_filters_nothing_is_touched(answers):
    assert all('filterFn' not in column for column in answers['columns_without_any_filter'])


def test_a_column_that_already_declared_it_keeps_saying_the_same(answers):
    """
    Imponer no es pisar con algo distinto.

    Dos tablas del producto ya lo declaraban a mano, y tienen que seguir
    funcionando igual mientras alguien las deja como están.
    """
    assert answers['a_declared_filter_is_respected'][0]['filterFn'] == 'includesSelected'
