"""Tests para sync_xlsx_to_dq_checks (logica pura: parseo/validacion/derivacion).

No tocan BigQuery: la autenticacion, el dry-run y la carga quedan fuera del
test unitario. Solo se ejercita _parse_catalog_rows y sus helpers.
"""

from sync_xlsx_to_dq_checks import _parse_catalog_rows, _slug, _parse_bool, _parse_threshold


def _row(**kwargs) -> dict:
    base = {
        "check_id": "",
        "tipo": "",
        "subcategoria": "",
        "pregunta": "",
        "assert_sql": "",
        "threshold": "",
        "severity": "",
        "enabled": "",
        "owner": "",
    }
    base.update(kwargs)
    return base


def test_fila_valida_minima_se_carga():
    valid, skipped = _parse_catalog_rows([
        _row(tipo="fiabilidad", pregunta=" hay nulos? ", assert_sql="SELECT 1", enabled="TRUE"),
    ])
    assert len(valid) == 1
    assert len(skipped) == 0
    r = valid[0]
    assert r["tipo"] == "fiabilidad"
    assert r["pregunta"] == "hay nulos?"
    assert r["severity"] == "warning"  # default
    assert r["threshold"] == 0  # default
    assert r["enabled"] is True
    assert r["owner"] == ""
    assert r["created_at"] == r["updated_at"]
    assert r["created_at"].endswith("Z")


def test_check_id_se_autoderiva_cuando_vacio():
    valid, _ = _parse_catalog_rows([
        _row(tipo="negocio", subcategoria="Ventas Diarias",
             pregunta="¿La venta es positiva?", assert_sql="SELECT 1"),
    ])
    cid = valid[0]["check_id"]
    assert cid.startswith("negocio__ventas_diarias_")
    assert "la_venta_es_positiva" in cid


def test_check_id_explicito_se_respeta():
    valid, _ = _parse_catalog_rows([
        _row(check_id="custom_id_1", tipo="fiabilidad",
             pregunta="x", assert_sql="SELECT 1"),
    ])
    assert valid[0]["check_id"] == "custom_id_1"


def test_check_id_colision_se_sufija():
    valid, _ = _parse_catalog_rows([
        _row(check_id="dup", tipo="fiabilidad", pregunta="a", assert_sql="SELECT 1"),
        _row(check_id="dup", tipo="negocio", pregunta="b", assert_sql="SELECT 2"),
        _row(check_id="dup", tipo="negocio", pregunta="c", assert_sql="SELECT 3"),
    ])
    ids = [r["check_id"] for r in valid]
    assert ids == ["dup", "dup_2", "dup_3"]


def test_tipo_invalido_se_skipea():
    valid, skipped = _parse_catalog_rows([
        _row(tipo="otracosa", pregunta="x", assert_sql="SELECT 1"),
    ])
    assert valid == []
    assert len(skipped) == 1
    assert "tipo invalido" in skipped[0]["reason"]


def test_tipo_case_insensitive_se_normaliza():
    valid, skipped = _parse_catalog_rows([
        _row(tipo="  Fiabilidad ", pregunta="x", assert_sql="SELECT 1"),
    ])
    assert len(valid) == 1
    assert skipped == []
    assert valid[0]["tipo"] == "fiabilidad"


def test_pregunta_vacia_se_skipea():
    valid, skipped = _parse_catalog_rows([
        _row(tipo="negocio", pregunta="   ", assert_sql="SELECT 1"),
    ])
    assert valid == []
    assert skipped[0]["reason"] == "pregunta vacia"


def test_assert_sql_vacia_se_skipea():
    valid, skipped = _parse_catalog_rows([
        _row(tipo="negocio", pregunta="x", assert_sql=""),
    ])
    assert valid == []
    assert skipped[0]["reason"] == "assert_sql vacia"


def test_severity_invalida_se_skipea():
    valid, skipped = _parse_catalog_rows([
        _row(tipo="negocio", pregunta="x", assert_sql="SELECT 1", severity="urgente"),
    ])
    assert valid == []
    assert "severity invalida" in skipped[0]["reason"]


def test_threshold_negativo_se_skipea():
    valid, skipped = _parse_catalog_rows([
        _row(tipo="negocio", pregunta="x", assert_sql="SELECT 1", threshold="-5"),
    ])
    assert valid == []
    assert "threshold debe ser >= 0" in skipped[0]["reason"]


def test_threshold_no_numerico_se_skipea():
    valid, skipped = _parse_catalog_rows([
        _row(tipo="negocio", pregunta="x", assert_sql="SELECT 1", threshold="abc"),
    ])
    assert valid == []
    assert "threshold no es numerico" in skipped[0]["reason"]


def test_fila_totalmente_vacia_se_ignora_sin_skip():
    valid, skipped = _parse_catalog_rows([
        _row(),  # todo vacio
        _row(tipo="negocio", pregunta="x", assert_sql="SELECT 1"),
    ])
    assert len(valid) == 1
    assert skipped == []  # la vacia se ignora, no se cuenta como skipped


def test_parse_bool_variantes():
    assert _parse_bool("TRUE") is True
    assert _parse_bool("True") is True
    assert _parse_bool(1) is True
    assert _parse_bool("si") is True
    assert _parse_bool("sí") is True
    assert _parse_bool("x") is True
    assert _parse_bool("FALSE") is False
    assert _parse_bool("0") is False
    assert _parse_bool("") is False
    assert _parse_bool(None) is False


def test_parse_threshold_defaults_y_floats_enteros():
    assert _parse_threshold("") == (0, None)
    assert _parse_threshold(None) == (0, None)
    assert _parse_threshold("3") == (3, None)
    assert _parse_threshold("3.0") == (3, None)
    val, err = _parse_threshold("3.5")
    assert val is None and "entero" in err


def test_slug_colapsa_y_recorta():
    assert _slug("  Hola, Mundo!! ") == "hola_mundo"
    assert _slug("A---B___C") == "a_b_c"
    assert _slug("") == ""


def test_assert_sql_con_comas_y_comillas_se_conserva():
    sql = 'SELECT COUNT(*) FROM t WHERE col IN ("a", "b"), foo = 1'
    valid, _ = _parse_catalog_rows([
        _row(tipo="fiabilidad", pregunta="x", assert_sql=sql),
    ])
    assert valid[0]["assert_sql"] == sql
