"""Teste US-006 (PRD 5.4): Componenta UI de eroare pe 3 niveluri.

Pasul A (RED): teste scrise inainte de implementare.
"""

from __future__ import annotations

import csv
import io
import json
import os
import re
import tempfile

import pytest


# ---------------------------------------------------------------------------
# Teste pure pentru parse_erori (fara HTTP)
# ---------------------------------------------------------------------------

from app.web.labels import parse_erori  # noqa: E402


def test_parse_erori_array_3niveluri():
    """Array imbogatit cu cod/problema/cauza/fix -> lista cu toate 3 nivelurile."""
    rar_error = json.dumps([
        {
            "field": "vin",
            "cod": "VIN_FORMAT",
            "problema": "VIN invalid",
            "cauza": "VIN-ul are 16 caractere; RAR cere exact 17.",
            "fix": "Verifica VIN-ul pe talon (pozitia E); 17 caractere majuscule.",
            "message": "VIN-ul are 16 caractere; RAR cere exact 17.",
        }
    ])
    rezultat = parse_erori(rar_error)
    assert len(rezultat) == 1
    e = rezultat[0]
    assert e["problema"] == "VIN invalid"
    assert "17" in e["cauza"]
    assert e["fix"]  # non-gol
    assert e.get("field") == "vin"


def test_parse_erori_unmapped():
    """Dict unmapped 3-niveluri (cod=COD_NEMAPAT) -> 1 element corect."""
    rar_error = json.dumps({
        "cod": "COD_NEMAPAT",
        "problema": "Lipseste codul RAR al operatiei",
        "cauza": "Codul OP-99 nu are mapare RAR.",
        "fix": "Alege codul RAR pentru aceasta operatie in tab-ul Mapari.",
        "unmapped": [{"cod_op_service": "OP-99", "denumire": "Operatie test"}],
    })
    rezultat = parse_erori(rar_error)
    assert len(rezultat) == 1
    e = rezultat[0]
    assert e["problema"] == "Lipseste codul RAR al operatiei"
    assert e["fix"]


def test_parse_erori_creds():
    """Dict cu cod=RAR_CREDS_INVALIDE -> 1 element corect."""
    rar_error = json.dumps({
        "cod": "RAR_CREDS_INVALIDE",
        "problema": "Credentiale RAR invalide",
        "cauza": "Autentificarea la RAR a esuat (401).",
        "fix": "Verifica email-ul si parola contului RAR in Meniu cont › Cont.",
    })
    rezultat = parse_erori(rar_error)
    assert len(rezultat) == 1
    e = rezultat[0]
    assert e["problema"] == "Credentiale RAR invalide"
    assert e["fix"]


def test_parse_erori_forma_veche_si_corupt():
    """Forma veche [{ field, message }], string plain, None, invalid -> degradeaza fara exceptie."""
    # Forma veche: lista cu field+message dar fara cod
    vechi = json.dumps([{"field": "vin", "message": "VIN invalid"}])
    r = parse_erori(vechi)
    assert isinstance(r, list)
    assert len(r) >= 1
    # Nu arunca pentru niciun element
    for e in r:
        assert "problema" in e

    # String plain
    r2 = parse_erori("Eroare generica de la RAR")
    assert isinstance(r2, list)
    assert len(r2) >= 1
    assert r2[0]["problema"]

    # None
    r3 = parse_erori(None)
    assert isinstance(r3, list)
    assert r3 == []

    # JSON corupt
    r4 = parse_erori("{invalid json[[[")
    assert isinstance(r4, list)
    assert len(r4) >= 1
    # Nu arunca

    # Dict fara cod (forma veche dict)
    r5 = parse_erori(json.dumps({"auto_send": "cod-abc", "motiv": "auto_send oprit"}))
    assert isinstance(r5, list)
    assert len(r5) >= 1


# ---------------------------------------------------------------------------
# Fixture HTTP
# ---------------------------------------------------------------------------

@pytest.fixture()
def client(monkeypatch):
    tmp = tempfile.mkdtemp()
    monkeypatch.setenv("AUTOPASS_DB_PATH", os.path.join(tmp, "erori.db"))
    monkeypatch.setenv("AUTOPASS_WEB_AUTH_REQUIRED", "false")
    from app.config import get_settings
    get_settings.cache_clear()
    from app.main import app
    from fastapi.testclient import TestClient
    with TestClient(app) as c:
        yield c
    get_settings.cache_clear()


def _csv_bytes(rows: list[dict]) -> bytes:
    buf = io.StringIO()
    writer = csv.DictWriter(buf, fieldnames=list(rows[0].keys()), delimiter=";")
    writer.writeheader()
    writer.writerows(rows)
    return buf.getvalue().encode("utf-8")


def _seed_mapping(conn=None):
    """Mapeaza OP-1 -> R-FRANE (cont dev id=1)."""
    from app.db import get_connection
    c = conn or get_connection()
    try:
        c.execute("INSERT OR REPLACE INTO nomenclator_rar (cod_prestatie, nume_prestatie) VALUES ('R-FRANE','Reparatie frane')")
        c.execute(
            "INSERT OR IGNORE INTO operations_mapping (account_id, cod_op_service, cod_prestatie, auto_send) "
            "VALUES (1, 'OP-1', 'R-FRANE', 1)"
        )
        c.commit()
    finally:
        if conn is None:
            c.close()


def _creeaza_submission_needs_data(client, fix_text=None):
    """Creeaza un submission in starea needs_data si returneaza id-ul."""
    import json as _json
    from app.db import get_connection

    _seed_mapping()

    # Insereaza direct un submission needs_data cu rar_error 3-niveluri
    fix_folosit = fix_text or "Verifica VIN-ul pe talon (pozitia E); 17 caractere majuscule."
    rar_error_3n = _json.dumps([{
        "field": "vin",
        "cod": "VIN_FORMAT",
        "problema": "VIN invalid",
        "cauza": "VIN-ul are 16 caractere; RAR cere exact 17.",
        "fix": fix_folosit,
        "message": "VIN-ul are 16 caractere; RAR cere exact 17.",
    }])

    payload = _json.dumps({
        "vin": "VIN16CARACT000000",
        "nr_inmatriculare": "B001TST",
        "data_prestatie": "2026-06-15",
        "odometru_final": 145000,
        "cod_prestatie": "R-FRANE",
        "prestatii": [{"cod_prestatie": "R-FRANE"}],
    })
    idem_key = f"test-erori-{fix_folosit[:20]}"

    conn = get_connection()
    try:
        cur = conn.execute(
            """INSERT INTO submissions
               (account_id, idempotency_key, payload_json, status, rar_error, retry_count)
               VALUES (1, ?, ?, 'needs_data', ?, 0)""",
            (idem_key, payload, rar_error_3n)
        )
        conn.commit()
        return cur.lastrowid
    finally:
        conn.close()


def test_detaliu_afiseaza_fix(client):
    """Fragmentul de detaliu al unui submission needs_data contine textul fix-ului."""
    sub_id = _creeaza_submission_needs_data(client, "Verifica VIN-ul pe talon (pozitia E)")
    resp = client.get(f"/_fragments/trimitere/{sub_id}")
    assert resp.status_code == 200
    html = resp.text
    # Fix-ul trebuie sa apara in HTML
    assert "pozitia E" in html, (
        f"HTML-ul detaliu nu contine fix-ul ('pozitia E'). Primii 2000 chars:\n{html[:2000]}"
    )


def _import_preview_cu_vin_invalid(client):
    """Efectueaza un import cu un rand cu VIN invalid si returneaza HTML-ul preview."""
    _seed_mapping()
    rows = [
        # rand cu VIN de 16 caractere (invalid, trebuie 17) -> needs_data
        {"VIN": "VIN16CARACT00000", "Nr inmatriculare": "B001TST",
         "Data prestatie": "15.06.2026", "Odometru final": "145000", "Operatie": "OP-1"},
    ]
    data = _csv_bytes(rows)

    r = client.post("/_import/upload", files={"file": ("test.csv", data, "text/csv")})
    assert r.status_code == 200
    m = re.search(r"/_import/(\d+)/mapare-coloane", r.text)
    assert m, f"Nu am gasit import_id. Raspuns: {r.text[:1000]}"
    import_id = int(m.group(1))

    r = client.post(f"/_import/{import_id}/mapare-coloane", data={
        "colname": ["VIN", "Nr inmatriculare", "Data prestatie", "Odometru final", "Operatie"],
        "canon": ["vin", "nr_inmatriculare", "data_prestatie", "odometru_final", "operatie"],
        "format_data": "DD.MM.YYYY",
    })
    assert r.status_code == 200
    return r.text


def test_preview_rand_per_camp_fix(client):
    """Preview rand needs_data -> HTML contine fix-ul per camp (VIN)."""
    html = _import_preview_cu_vin_invalid(client)
    # Randul trebuie sa fie needs_data
    assert "needs_data" in html or "needs_review" in html or "VIN" in html.upper(), (
        f"HTML-ul preview nu contine starea asteptata. Primii 2000 chars:\n{html[:2000]}"
    )
    # Fix-ul pentru VIN trebuie sa apara in preview
    assert "talon" in html.lower() or "majuscule" in html.lower() or "pozitia" in html.lower(), (
        f"HTML-ul preview nu contine fix-ul VIN. Primii 3000 chars:\n{html[:3000]}"
    )


# ---------------------------------------------------------------------------
# Teste noi (BUG 1 + BUG 2) — adaugate TDD-style (RED inainte de fix)
# ---------------------------------------------------------------------------

from app.web.labels import motiv_uman  # noqa: E402
from app.errors import eroare  # noqa: E402


def test_motiv_uman_creds_3niveluri():
    """BUG 1: motiv_uman pe dict 3-niveluri (RAR_CREDS_INVALIDE) -> problema, NU text garbled."""
    rar_err = json.dumps(eroare("RAR_CREDS_INVALIDE", cauza="credentiale RAR invalide"))
    rezultat = motiv_uman("error", rar_err)
    # Nu trebuie sa contina "field:" / "cod:" (text garbled)
    assert "field:" not in rezultat, f"Text garbled cu 'field:': {rezultat!r}"
    assert "cod:" not in rezultat, f"Text garbled cu 'cod:': {rezultat!r}"
    # Trebuie sa returneze textul 'problema' (primul nivel)
    data = json.loads(rar_err)
    problema = data.get("problema") or ""
    assert problema, "eroare() nu a returnat 'problema' — verifica error_codes.py"
    assert rezultat == problema[:200], (
        f"Asteptat {problema[:200]!r}, obtinut {rezultat!r}"
    )


def test_motiv_uman_unmapped_neschimbat():
    """Ramura unmapped inca functioneaza dupa adaugarea ramurii 3-niveluri."""
    rar_err = json.dumps({"unmapped": [{"cod_op_service": "OP-99", "denumire": "Test"}]})
    rezultat = motiv_uman("needs_mapping", rar_err)
    assert rezultat.startswith("Cod RAR lipsa pentru:"), (
        f"Ramura unmapped regresta. Obtinut: {rezultat!r}"
    )
    assert "OP-99" in rezultat


def test_parse_erori_gol_returneaza_lista_goala():
    """BUG 2: parse_erori pe dict/lista goala -> [], nu 1 element cu problema=''."""
    r1 = parse_erori("{}")
    assert r1 == [], f"parse_erori('{{}}') -> {r1!r}, asteptat []"

    r2 = parse_erori("[{}]")
    assert r2 == [], f"parse_erori('[{{}}]') -> {r2!r}, asteptat []"
