"""Teste T3 — validare de domeniu prezentari (app.validation).

Un test per regula din plan.md sect. 2 / contract. Validarea normalizeaza
(strip/upper) si intoarce erori structurate {field, message}; nu ridica exceptii.
"""

from __future__ import annotations

from datetime import timedelta

import pytest

from app.models import PrezentareIn
from app.validation import today_bucuresti, validate_prezentare


def _base(**overrides) -> dict:
    """Prezentare valida ca dict (normalizat prin PrezentareIn), cu suprascrieri."""
    data = {
        "vin": "WVWZZZ1KZAW000123",
        "nr_inmatriculare": "B999TST",
        "data_prestatie": "2026-06-15",
        "odometru_final": "123456",
        "odometru_initial": None,
        "prestatii": [{"cod_prestatie": "OE-1"}],
        "sistem_reparat": "null",
    }
    data.update(overrides)
    return PrezentareIn(**data).model_dump()


def _fields(errors: list[dict]) -> set[str]:
    return {e["field"] for e in errors}


def test_prezentare_valida_fara_erori():
    assert validate_prezentare(_base()) == []


def test_normalizare_strip_upper():
    c = _base(vin="  wvwzzz1kzaw000123 ", nr_inmatriculare=" b999tst ")
    assert c["vin"] == "WVWZZZ1KZAW000123"
    assert c["nr_inmatriculare"] == "B999TST"
    assert validate_prezentare(c) == []


@pytest.mark.parametrize("vin", [
    "WVWZZ1KZAW000123",     # 16 caractere
    "WVWZZZ1KZAW0001234",   # 18 caractere
    "WVWZZZ1OZAW000123",    # contine O
    "WVWZZZ1IZAW000123",    # contine I
    "WVWZZZ1QZAW000123",    # contine Q
    "WVW ZZ1KZAW00012",     # spatiu
])
def test_vin_invalid(vin):
    errors = validate_prezentare(_base(vin=vin))
    assert "vin" in _fields(errors)


def test_nrinmatriculare_prea_lung():
    errors = validate_prezentare(_base(nr_inmatriculare="ABCDEFGHIJK"))  # 11
    assert "nr_inmatriculare" in _fields(errors)


def test_nrinmatriculare_caracter_special():
    errors = validate_prezentare(_base(nr_inmatriculare="B-99"))
    assert "nr_inmatriculare" in _fields(errors)


def test_data_prea_veche():
    errors = validate_prezentare(_base(data_prestatie="2024-11-30"))
    assert "data_prestatie" in _fields(errors)


def test_data_la_limita_inferioara_ok():
    assert validate_prezentare(_base(data_prestatie="2024-12-01")) == []


def test_data_in_viitor():
    maine = (today_bucuresti() + timedelta(days=1)).isoformat()
    errors = validate_prezentare(_base(data_prestatie=maine))
    assert "data_prestatie" in _fields(errors)


def test_data_azi_ok():
    azi = today_bucuresti().isoformat()
    assert validate_prezentare(_base(data_prestatie=azi)) == []


def test_data_format_invalid():
    errors = validate_prezentare(_base(data_prestatie="15-06-2026"))
    assert "data_prestatie" in _fields(errors)


def test_odometru_final_nenumeric():
    errors = validate_prezentare(_base(odometru_final="abc"))
    assert "odometru_final" in _fields(errors)


def test_rodo_fara_odometru_initial():
    errors = validate_prezentare(_base(prestatii=[{"cod_prestatie": "R-ODO"}]))
    assert "odometru_initial" in _fields(errors)


def test_iodo_fara_odometru_initial():
    errors = validate_prezentare(_base(prestatii=[{"cod_prestatie": "I-ODO"}]))
    assert "odometru_initial" in _fields(errors)


def test_rodo_cu_odometru_initial_ok():
    c = _base(prestatii=[{"cod_prestatie": "R-ODO"}], odometru_initial="100000")
    assert validate_prezentare(c) == []


def test_odometru_initial_mai_mare_decat_final():
    c = _base(prestatii=[{"cod_prestatie": "R-ODO"}], odometru_initial="200000", odometru_final="100000")
    assert "odometru_initial" in _fields(errors := validate_prezentare(c))
    assert any("<=" in e["message"] for e in errors)


def test_prestatii_goale():
    errors = validate_prezentare(_base(prestatii=[]))
    assert "prestatii" in _fields(errors)


def test_b64image_invalid():
    errors = validate_prezentare(_base(b64_image="@@@not-base64@@@"))
    assert "b64_image" in _fields(errors)


def test_b64image_valid_ok():
    import base64
    good = base64.b64encode(b"poza odometru").decode()
    assert validate_prezentare(_base(b64_image=good)) == []


def test_erori_multiple_cumulate():
    errors = validate_prezentare(_base(vin="BAD", nr_inmatriculare="X-Y", data_prestatie="2024-01-01"))
    assert {"vin", "nr_inmatriculare", "data_prestatie"} <= _fields(errors)


# ---------------------------------------------------------------------------
# US-002: 3 niveluri — coduri stabile, forma aditiva, back-compat
# ---------------------------------------------------------------------------

from app import errors as err_mod  # noqa: E402


def test_vin_invalid_are_3niveluri():
    """VIN cu O/I/Q => eroarea are cod, problema, fix, field, message neschimbat."""
    errs = validate_prezentare(_base(vin="WVWZZZ1OZAW000123"))
    vin_errs = [e for e in errs if e.get("field") == "vin"]
    assert vin_errs, "Trebuie cel putin o eroare cu field==vin"
    e = vin_errs[0]
    assert e["cod"] == "VIN_FORMAT"
    assert e["problema"], "problema trebuie sa fie ne-goala"
    assert e["fix"], "fix trebuie sa fie ne-gol"
    assert e["field"] == "vin"
    # message trebuie sa fie mesajul existent (back-compat)
    assert "17" in e["message"] or "O, I, Q" in e["message"]


def test_data_prea_veche_cod():
    errs = validate_prezentare(_base(data_prestatie="2024-11-30"))
    dp = [e for e in errs if e.get("field") == "data_prestatie"]
    assert dp
    assert dp[0]["cod"] == "DATA_PREA_VECHE"


def test_data_viitor_cod():
    from datetime import timedelta
    maine = (today_bucuresti() + timedelta(days=1)).isoformat()
    errs = validate_prezentare(_base(data_prestatie=maine))
    dp = [e for e in errs if e.get("field") == "data_prestatie"]
    assert dp
    assert dp[0]["cod"] == "DATA_VIITOR"


def test_data_format_cod():
    errs = validate_prezentare(_base(data_prestatie="15-06-2026"))
    dp = [e for e in errs if e.get("field") == "data_prestatie"]
    assert dp
    assert dp[0]["cod"] == "DATA_FORMAT"


def test_odometru_initial_lipsa_cod():
    errs = validate_prezentare(_base(prestatii=[{"cod_prestatie": "R-ODO"}]))
    oi = [e for e in errs if e.get("field") == "odometru_initial"]
    assert oi
    assert oi[0]["cod"] == "ODOMETRU_INITIAL_LIPSA"


def test_odometru_ordine_cod():
    c = _base(prestatii=[{"cod_prestatie": "R-ODO"}], odometru_initial="200000", odometru_final="100000")
    errs = validate_prezentare(c)
    oi = [e for e in errs if e.get("field") == "odometru_initial" and e.get("cod") == "ODOMETRU_INITIAL_ORDINE"]
    assert oi, "Trebuie eroare cu cod ODOMETRU_INITIAL_ORDINE"


def test_prestatii_goale_cod():
    errs = validate_prezentare(_base(prestatii=[]))
    pr = [e for e in errs if e.get("field") == "prestatii"]
    assert pr
    assert pr[0]["cod"] == "PRESTATII_GOALE"


def test_b64_invalid_cod():
    errs = validate_prezentare(_base(b64_image="@@@not-base64@@@"))
    b = [e for e in errs if e.get("field") == "b64_image"]
    assert b
    assert b[0]["cod"] == "B64_INVALID"


def test_back_compat_field_message():
    """Fiecare eroare are inca field + message (forma veche)."""
    errs = validate_prezentare(_base(
        vin="BAD",
        nr_inmatriculare="X-Y",
        data_prestatie="2024-01-01",
        odometru_final="abc",
        prestatii=[],
    ))
    for e in errs:
        assert "field" in e, f"Lipseste 'field' in {e}"
        assert "message" in e, f"Lipseste 'message' in {e}"
        assert e["message"], f"'message' gol in {e}"


def test_toate_codurile_in_catalog():
    """Fiecare cod emis de validate_prezentare exista in errors.CATALOG."""
    from datetime import timedelta

    cazuri = [
        _base(vin="WVWZZZ1OZAW000123"),                         # VIN_FORMAT
        _base(nr_inmatriculare="X-Y"),                           # NR_INMATRICULARE_FORMAT
        _base(data_prestatie="15-06-2026"),                      # DATA_FORMAT
        _base(data_prestatie="2024-11-30"),                      # DATA_PREA_VECHE
        _base(data_prestatie=(today_bucuresti() + timedelta(days=1)).isoformat()),  # DATA_VIITOR
        _base(odometru_final="abc"),                             # ODOMETRU_FINAL_FORMAT
        _base(prestatii=[{"cod_prestatie": "R-ODO"}]),           # ODOMETRU_INITIAL_LIPSA
        _base(prestatii=[{"cod_prestatie": "R-ODO"}], odometru_initial="abc"),     # ODOMETRU_INITIAL_FORMAT
        _base(prestatii=[{"cod_prestatie": "R-ODO"}], odometru_initial="200000", odometru_final="100000"),  # ODOMETRU_INITIAL_ORDINE
        _base(prestatii=[]),                                     # PRESTATII_GOALE
        _base(b64_image="@@@not-base64@@@"),                     # B64_INVALID
    ]
    for caz in cazuri:
        errs = validate_prezentare(caz)
        for e in errs:
            if "cod" in e:
                assert e["cod"] in err_mod.CATALOG, f"Cod {e['cod']!r} absent din CATALOG"
