"""Tests para metabase_mbql_validate."""

import sys
import os

sys.path.insert(0, os.path.join(os.path.dirname(__file__), ".."))

from metabase.metabase_mbql_validate import metabase_mbql_validate


# ---------------------------------------------------------------------------
# DQ valido: estructura simplificada basada en card 5705 post-fix
# ---------------------------------------------------------------------------

VALID_DQ = {
    "lib/type": "mbql/query",
    "database": 6,
    "stages": [
        {
            "lib/type": "mbql.stage/mbql",
            "source-card": 100,
            "aggregation": [
                [
                    "sum",
                    {"lib/uuid": "agg-uuid-1"},
                    [
                        "field",
                        {"base-type": "type/Decimal", "lib/uuid": "field-uuid-1"},
                        "Cantidad",
                    ],
                ]
            ],
            "expressions": [
                [
                    "-",
                    {
                        "lib/uuid": "expr-uuid-1",
                        "lib/expression-name": "Diferencia_Cantidad",
                    },
                    [
                        "field",
                        {"base-type": "type/Decimal", "lib/uuid": "field-uuid-2"},
                        "Valor_A",
                    ],
                    [
                        "field",
                        {"base-type": "type/Decimal", "lib/uuid": "field-uuid-3"},
                        "Valor_B",
                    ],
                ]
            ],
        },
        {
            "lib/type": "mbql.stage/mbql",
            "expressions": [
                [
                    "case",
                    {
                        "lib/uuid": "case-uuid-1",
                        "lib/expression-name": "Valor medio de venta",
                    },
                    [
                        [
                            [
                                "=",
                                {"lib/uuid": "cond-uuid-1"},
                                [
                                    "field",
                                    {
                                        "base-type": "type/Integer",
                                        "lib/uuid": "field-uuid-4",
                                    },
                                    "Tickets",
                                ],
                                0,
                            ],
                            0,
                        ],
                        [
                            [
                                "!=",
                                {"lib/uuid": "cond-uuid-2"},
                                [
                                    "field",
                                    {
                                        "base-type": "type/Integer",
                                        "lib/uuid": "field-uuid-5",
                                    },
                                    "Tickets",
                                ],
                                0,
                            ],
                            [
                                "/",
                                {"lib/uuid": "div-uuid-1"},
                                [
                                    "field",
                                    {
                                        "base-type": "type/Decimal",
                                        "lib/uuid": "field-uuid-6",
                                    },
                                    "Valor_vendido",
                                ],
                                [
                                    "field",
                                    {
                                        "base-type": "type/Integer",
                                        "lib/uuid": "field-uuid-7",
                                    },
                                    "Tickets",
                                ],
                            ],
                        ],
                    ],
                ]
            ],
        },
    ],
}


def test_valid_dq_returns_no_errors():
    """DQ valido retorna lista vacia."""
    errors = metabase_mbql_validate(VALID_DQ)
    assert errors == [], f"Esperaba 0 errores, got: {errors}"


# ---------------------------------------------------------------------------
# Check 1: UUID duplicado
# ---------------------------------------------------------------------------


def test_duplicate_uuid_detected():
    """UUID repetido en el arbol MBQL genera error."""
    dq = {
        "stages": [
            {
                "lib/type": "mbql.stage/mbql",
                "expressions": [
                    [
                        "-",
                        {"lib/uuid": "dup-uuid-001", "lib/expression-name": "ExprA"},
                        ["field", {"base-type": "type/Decimal", "lib/uuid": "dup-uuid-001"}, "CampoX"],
                        ["field", {"base-type": "type/Decimal", "lib/uuid": "unique-uuid-002"}, "CampoY"],
                    ]
                ],
            }
        ]
    }
    errors = metabase_mbql_validate(dq)
    assert any("dup-uuid-001" in e for e in errors), f"Esperaba error de UUID duplicado, got: {errors}"


# ---------------------------------------------------------------------------
# Check 2: Stage mixing — expressions referencian slots de aggregation
# ---------------------------------------------------------------------------


def test_stage_mixing_detected():
    """Stage con aggregations y expressions que referencian slots generados."""
    dq = {
        "stages": [
            {
                "lib/type": "mbql.stage/mbql",
                "aggregation": [
                    ["sum", {"lib/uuid": "agg1"}, ["field", {"base-type": "type/Decimal", "lib/uuid": "f1"}, "Precio"]]
                ],
                "expressions": [
                    [
                        "*",
                        {"lib/uuid": "expr2", "lib/expression-name": "DobleSum"},
                        # field sin base-type y nombre 'sum' = slot ref de aggregation
                        ["field", {"lib/uuid": "ref-slot"}, "sum"],
                        2,
                    ]
                ],
            }
        ]
    }
    errors = metabase_mbql_validate(dq)
    assert any("mezcla" in e.lower() or "mixing" in e.lower() or "stage" in e.lower() for e in errors), \
        f"Esperaba error de stage mixing, got: {errors}"


# ---------------------------------------------------------------------------
# Check 3: Slot ref rota — sum_99 inexistente
# ---------------------------------------------------------------------------


def test_broken_slot_ref_detected():
    """Expression que referencia sum_99 cuando solo hay 1 sum genera error."""
    dq = {
        "stages": [
            {
                "lib/type": "mbql.stage/mbql",
                "aggregation": [
                    ["sum", {"lib/uuid": "agg-s1"}, ["field", {"base-type": "type/Decimal", "lib/uuid": "f-s1"}, "Precio"]]
                ],
                "expressions": [
                    [
                        "+",
                        {"lib/uuid": "expr-broken", "lib/expression-name": "SumRota"},
                        # sum_99 no existe (solo hay 1 sum = sum_0)
                        ["field", {"lib/uuid": "ref-broken"}, "sum_99"],
                        1,
                    ]
                ],
            }
        ]
    }
    errors = metabase_mbql_validate(dq)
    assert any("sum_99" in e for e in errors), f"Esperaba error de slot ref roto, got: {errors}"


# ---------------------------------------------------------------------------
# Check 4: Case con estructura invalida
# ---------------------------------------------------------------------------


def test_case_malformed_cases_not_pairs():
    """Case con casos que no son pares [cond, result] genera error."""
    dq = {
        "stages": [
            {
                "lib/type": "mbql.stage/mbql",
                "expressions": [
                    [
                        "case",
                        {"lib/uuid": "case-bad", "lib/expression-name": "CaseMalo"},
                        # lista con un solo elemento (no par)
                        [["solo_elemento"]],
                    ]
                ],
            }
        ]
    }
    errors = metabase_mbql_validate(dq)
    assert any("case" in e.lower() for e in errors), f"Esperaba error de case structure, got: {errors}"


# ---------------------------------------------------------------------------
# Check 5: Name collision en expressions
# ---------------------------------------------------------------------------


def test_name_collision_detected():
    """Dos expressions con mismo lib/expression-name en la misma stage generan error."""
    dq = {
        "stages": [
            {
                "lib/type": "mbql.stage/mbql",
                "expressions": [
                    ["-", {"lib/uuid": "e1", "lib/expression-name": "MiCalculo"},
                     ["field", {"base-type": "type/Decimal", "lib/uuid": "f1"}, "A"],
                     ["field", {"base-type": "type/Decimal", "lib/uuid": "f2"}, "B"]],
                    ["+", {"lib/uuid": "e2", "lib/expression-name": "MiCalculo"},
                     ["field", {"base-type": "type/Decimal", "lib/uuid": "f3"}, "C"],
                     1],
                ],
            }
        ]
    }
    errors = metabase_mbql_validate(dq)
    assert any("MiCalculo" in e for e in errors), f"Esperaba error de name collision, got: {errors}"


# ---------------------------------------------------------------------------
# Check: stages ausente
# ---------------------------------------------------------------------------


def test_missing_stages_returns_error():
    """dataset_query sin 'stages' devuelve error de estructura."""
    errors = metabase_mbql_validate({"database": 1})
    assert any("stages" in e.lower() for e in errors), f"Esperaba error de stages ausente, got: {errors}"
