"""Teste US-006 (PRD 5.15): prestatii multi-cod (lista) la editare/corectie.

AC-uri verificate:
- Handler-ele accepta LISTA de cod_prestatie (form.getlist) -> prestatii cu mai multe coduri.
- cod_op_service/denumire RAMAN pe item (invariant D7, E1 IRON RULE).
- Cod invalid -> respins cu mesaj; cod necunoscut NU ajunge la RAR (ORA-12899).
- Lista goala -> ramane needs_mapping.
- Dedup per-item: (op_service, cod) unic, NU cod unic (doua ops diferite cu acelasi cod ok).
- Recalcul idempotenta dupa editare.
- odometruInitial obligatoriu cand cod_prestatie contine R-ODO/I-ODO.
- REGRESIE E1 (IRON RULE): op_service supravietuieste /repune cu cod.

TDD: toate testele sunt scrise INAINTE de implementare (RED -> GREEN).
"""

from __future__ import annotations

import json
import os
import re
import tempfile

import pytest
from starlette.testclient import TestClient


# --------------------------------------------------------------------------- #
# Fixtures                                                                     #
# --------------------------------------------------------------------------- #

@pytest.fixture()
def client(monkeypatch):
    tmp = tempfile.mkdtemp()
    monkeypatch.setenv("AUTOPASS_DB_PATH", os.path.join(tmp, "prestatii.db"))
    monkeypatch.setenv("AUTOPASS_WEB_AUTH_REQUIRED", "true")
    from app.config import get_settings
    get_settings.cache_clear()
    from app.web import ratelimit
    ratelimit._hits.clear()
    from app.main import app
    with TestClient(app, follow_redirects=False) as c:
        yield c
    ratelimit._hits.clear()
    get_settings.cache_clear()


# --------------------------------------------------------------------------- #
# Helpere                                                                      #
# --------------------------------------------------------------------------- #

def _create_account_user(email: str, password: str = "parolasecreta10"):
    from app.accounts import create_account
    from app.users import create_user
    from app.db import get_connection
    conn = get_connection()
    try:
        acct_id = create_account(conn, "Service", active=True)
        create_user(conn, acct_id, email, password)
        return acct_id
    finally:
        conn.close()


def _login(client, email: str, password: str = "parolasecreta10") -> None:
    resp = client.get("/login")
    m = re.search(r'name="csrf_token"\s+value="([^"]+)"', resp.text) or \
        re.search(r'value="([^"]+)"\s+name="csrf_token"', resp.text)
    assert m, "csrf_token nu gasit in login"
    resp = client.post("/login", data={"email": email, "parola": password, "csrf_token": m.group(1)})
    assert resp.status_code == 303


def _csrf(client) -> str:
    resp = client.get("/?tab=coada")
    m = re.search(r'name="csrf_token"\s+value="([^"]+)"', resp.text)
    assert m, "csrf_token nu gasit in dashboard"
    return m.group(1)


def _insert(acct: int, *, status: str, payload: dict, key: str | None = None) -> int:
    from app.db import get_connection
    conn = get_connection()
    try:
        k = key or f"k-{os.urandom(6).hex()}"
        cur = conn.execute(
            "INSERT INTO submissions (idempotency_key, account_id, status, payload_json) "
            "VALUES (?, ?, ?, ?)",
            (k, acct, status, json.dumps(payload)),
        )
        conn.commit()
        return int(cur.lastrowid)
    finally:
        conn.close()


def _row(sid: int):
    from app.db import get_connection
    conn = get_connection()
    try:
        return conn.execute("SELECT * FROM submissions WHERE id=?", (sid,)).fetchone()
    finally:
        conn.close()


def _payload_json(sid: int) -> dict:
    from app.db import get_connection
    conn = get_connection()
    try:
        r = conn.execute("SELECT payload_json FROM submissions WHERE id=?", (sid,)).fetchone()
        return json.loads(r["payload_json"])
    finally:
        conn.close()


def _seed_cod(cod: str, denumire: str = "Prestatie test") -> None:
    """Insereaza un cod in nomenclator_rar (fara operatii_mapping)."""
    from app.db import get_connection
    conn = get_connection()
    try:
        conn.execute(
            "INSERT OR REPLACE INTO nomenclator_rar (cod_prestatie, nume_prestatie) VALUES (?, ?)",
            (cod, denumire),
        )
        conn.commit()
    finally:
        conn.close()


def _payload_cu_ops(vin: str, ops: list[tuple[str, str]]) -> dict:
    """Payload cu prestatii avand cod_op_service/denumire (needs_mapping state)."""
    return {
        "vin": vin,
        "nr_inmatriculare": "B100AAA",
        "data_prestatie": "2026-06-10",
        "odometru_final": "50000",
        "prestatii": [
            {"cod_op_service": op, "denumire": den}
            for op, den in ops
        ],
    }


# --------------------------------------------------------------------------- #
# Teste                                                                        #
# --------------------------------------------------------------------------- #

def test_mai_multe_coduri_acceptate(client):
    """US-006 AC1: LISTA de cod_prestatie -> prestatii cu N itemi, fiecare cu cod setat.

    RED: form.get("cod_prestatie") intoarce doar primul cod; form.getlist necesar.
    """
    acct = _create_account_user("multi.cod@test.com")
    _login(client, "multi.cod@test.com")
    _seed_cod("OE-1", "Schimb ulei")
    _seed_cod("IG-1", "Inlocuire garnitura")

    sid = _insert(acct, status="needs_mapping", payload=_payload_cu_ops(
        "WVWZZZ1JZXW0MC001",
        [("Op-A", "Schimb ulei motor"), ("Op-B", "Inlocuire garnitura chiulasa")],
    ))
    csrf = _csrf(client)

    resp = client.post(
        f"/trimitere/{sid}/corecteaza",
        data={
            "csrf_token": csrf,
            "cod_prestatie": ["OE-1", "IG-1"],  # 2 coduri pentru 2 operatii
        },
    )
    assert resp.status_code == 200, resp.text[:500]

    r = _row(sid)
    assert r["status"] == "queued", f"status asteptat queued, got {r['status']}"
    prestatii = _payload_json(sid)["prestatii"]
    assert len(prestatii) == 2, f"asteptat 2 prestatii, got {len(prestatii)}: {prestatii}"
    coduri = [p.get("cod_prestatie") for p in prestatii]
    assert "OE-1" in coduri, f"OE-1 lipsa din prestatii: {prestatii}"
    assert "IG-1" in coduri, f"IG-1 lipsa din prestatii: {prestatii}"


def test_cod_op_service_pastrat_dupa_corecteaza(client):
    """E1/D7: cod_op_service si denumire RAMAN pe item dupa /corecteaza cu cod direct.

    RED: implementarea veche injecta in prestatii[0] fara sa afecteze op_service
    (intr-adevar in /corecteaza nu se facea pop), dar testul confirma explicit invariantul.
    """
    acct = _create_account_user("op.pastrat@test.com")
    _login(client, "op.pastrat@test.com")
    _seed_cod("OE-1")

    sid = _insert(acct, status="needs_mapping", payload=_payload_cu_ops(
        "WVWZZZ1JZXW0OP001",
        [("Schimb ulei", "Schimb ulei motor 5W30")],
    ))
    csrf = _csrf(client)

    resp = client.post(
        f"/trimitere/{sid}/corecteaza",
        data={"csrf_token": csrf, "cod_prestatie": "OE-1"},
    )
    assert resp.status_code == 200

    prestatii = _payload_json(sid)["prestatii"]
    assert len(prestatii) == 1
    item = prestatii[0]
    assert item.get("cod_prestatie") == "OE-1", f"cod_prestatie lipsa: {item}"
    assert item.get("cod_op_service") == "Schimb ulei", f"cod_op_service pierdut: {item}"
    assert item.get("denumire") == "Schimb ulei motor 5W30", f"denumire pierduta: {item}"


def test_cod_invalid_respins(client):
    """US-006 AC3: cod necunoscut in nomenclator -> respins cu mesaj, status neschimbat.

    RED: validarea fata de nomenclator nu e aplicata per-cod la multi-select.
    """
    acct = _create_account_user("cod.invalid@test.com")
    _login(client, "cod.invalid@test.com")
    # NU seed-uim "XX-99" -> cod necunoscut

    sid = _insert(acct, status="needs_mapping", payload=_payload_cu_ops(
        "WVWZZZ1JZXW0CI001",
        [("Op-Test", "Operatie test")],
    ))
    old_status = _row(sid)["status"]
    csrf = _csrf(client)

    resp = client.post(
        f"/trimitere/{sid}/corecteaza",
        data={"csrf_token": csrf, "cod_prestatie": "XX-99"},
    )
    assert resp.status_code == 200
    # Cod invalid -> mesaj de eroare vizibil
    assert "XX-99" in resp.text or "necunoscut" in resp.text.lower(), (
        f"Mesaj de eroare lipsa pentru cod invalid; text={resp.text[:500]}"
    )
    # Status neschimbat
    assert _row(sid)["status"] == old_status, (
        f"Status s-a schimbat desi codul e invalid: {_row(sid)['status']}"
    )


def test_lista_goala_needs_mapping(client):
    """US-006 AC4: nicio cod_prestatie trimis -> submission ramane needs_mapping.

    RED: cu multi-select, lista goala nu injecteaza nimic; resolve_prestatii
    gaseste inca operatii nemapate -> trebuie sa ramana needs_mapping.
    """
    acct = _create_account_user("goala.nemap@test.com")
    _login(client, "goala.nemap@test.com")
    # NU seed-uim nicio mapare -> operatia ramane nemapata

    sid = _insert(acct, status="needs_mapping", payload=_payload_cu_ops(
        "WVWZZZ1JZXW0GN001",
        [("Op-Nemap", "Operatie nemapata")],
    ))
    csrf = _csrf(client)

    # Trimit form FARA cod_prestatie (lista goala)
    resp = client.post(
        f"/trimitere/{sid}/corecteaza",
        data={"csrf_token": csrf},
    )
    assert resp.status_code == 200
    assert _row(sid)["status"] == "needs_mapping", (
        f"Status trebuia sa ramana needs_mapping, got {_row(sid)['status']}"
    )


def test_idempotency_recalculat(client):
    """US-006 AC6: dupa setarea de coduri noi, cheia de idempotenta e recalculata.

    RED: single-cod injecta in prestatii[0] si recalcula cheia; cu multi-cod
    acelasi mecanism se aplica tuturor itemilor.
    """
    acct = _create_account_user("ido.recalc@test.com")
    _login(client, "ido.recalc@test.com")
    _seed_cod("OE-1")

    sid = _insert(acct, status="needs_mapping", payload=_payload_cu_ops(
        "WVWZZZ1JZXW0ND001",
        [("Op-Ido", "Operatie ido")],
    ))
    old_key = _row(sid)["idempotency_key"]
    csrf = _csrf(client)

    resp = client.post(
        f"/trimitere/{sid}/corecteaza",
        data={"csrf_token": csrf, "cod_prestatie": "OE-1"},
    )
    assert resp.status_code == 200
    assert _row(sid)["status"] == "queued"
    new_key = _row(sid)["idempotency_key"]
    assert new_key != old_key, (
        f"Cheia de idempotenta NU s-a schimbat dupa setarea codului: {new_key}"
    )


def test_odometru_initial_conditionat_R_ODO(client):
    """US-006 AC7: cod_prestatie=R-ODO fara odometruInitial -> validate_prezentare
    intoarce eroare -> submission ramane needs_data (NU queued).

    RED: validarea R-ODO e deja in validate_prezentare; testul confirma ca
    multi-cod nu bypass-eaza aceasta regula.
    """
    acct = _create_account_user("odo.rodo@test.com")
    _login(client, "odo.rodo@test.com")
    _seed_cod("R-ODO", "Revizie odometru")

    # Payload: needs_mapping (op fara cod), FARA odometru_initial
    sid = _insert(acct, status="needs_mapping", payload={
        "vin": "WVWZZZ1JZXW0RO001",
        "nr_inmatriculare": "B100AAA",
        "data_prestatie": "2026-06-10",
        "odometru_final": "50000",
        # odometru_initial ABSENT
        "prestatii": [{"cod_op_service": "Revizie", "denumire": "Revizie odometru"}],
    })
    csrf = _csrf(client)

    # Trimit R-ODO ca cod (valid in nomenclator), dar fara odometru_initial
    resp = client.post(
        f"/trimitere/{sid}/corecteaza",
        data={"csrf_token": csrf, "cod_prestatie": "R-ODO"},
    )
    assert resp.status_code == 200
    status = _row(sid)["status"]
    # R-ODO fara odometruInitial -> validare esuata -> needs_data (nu queued)
    assert status in ("needs_data", "needs_mapping"), (
        f"Status neasteptat: {status}; trebuia needs_data/needs_mapping (R-ODO fara odo initial)"
    )
    assert status != "queued", (
        "R-ODO fara odometruInitial NU trebuie sa treaca in queued!"
    )


def test_dedup_per_item_nu_dupa_cod(client):
    """US-006 AC5 (E4): doua operatii DIFERITE cu ACELASI cod RAR ambele supravietuiesc.

    Dedup = (op_service, cod) identice, NU cod singur. Doua ops distincte pot
    mapa legitim la acelasi cod RAR fara sa fie sterse de dedup.

    RED: dedupare naiva dupa cod ar sterge a doua operatie (op-B cu acelasi OE-1).
    """
    acct = _create_account_user("dedup.ops@test.com")
    _login(client, "dedup.ops@test.com")
    _seed_cod("OE-1", "Schimb ulei")

    # Doua operatii distincte, ambele vor primi OE-1
    sid = _insert(acct, status="needs_mapping", payload=_payload_cu_ops(
        "WVWZZZ1JZXW0DD001",
        [("Op-A", "Prima operatie"), ("Op-B", "A doua operatie")],
    ))
    csrf = _csrf(client)

    resp = client.post(
        f"/trimitere/{sid}/corecteaza",
        data={
            "csrf_token": csrf,
            "cod_prestatie": ["OE-1", "OE-1"],  # acelasi cod pentru ambele ops
        },
    )
    assert resp.status_code == 200
    prestatii = _payload_json(sid)["prestatii"]
    # Ambele TREBUIE sa supravietuiasca: (Op-A, OE-1) != (Op-B, OE-1)
    assert len(prestatii) == 2, (
        f"Dedup a sters o operatie distincta! prestatii={prestatii} "
        "(doua ops cu acelasi cod trebuie pastrate)"
    )
    ops = [p.get("cod_op_service") for p in prestatii]
    assert "Op-A" in ops and "Op-B" in ops, f"ops_service pierdute: {ops}"


# --------------------------------------------------------------------------- #
# Test de regresie E1 (IRON RULE): op_service supravietuieste /repune cu cod  #
# --------------------------------------------------------------------------- #

def test_op_service_supravietuieste_repune_cu_cod(client):
    """E1 IRON RULE: dupa /repune cu cod_prestatie, cod_op_service/denumire RAMAN pe item.

    RED: routes.py:1371 face `p0.pop("cod_op_service", None)` — sterge operatia
    cand se seteaza un cod direct prin /repune. US-006 ELIMINA acel pop.

    Aceasta regresie e CRITICA: sterge contextul op->cod necesar pentru US-009
    (salvare mapare din chip) si rupe invariantul D7.
    """
    acct = _create_account_user("e1.repune@test.com")
    _login(client, "e1.repune@test.com")
    _seed_cod("OE-1", "Schimb ulei motor")

    # Starea error: payload cu op_service (operatia venita de la import/API)
    sid = _insert(acct, status="error", payload={
        "vin": "WVWZZZ1JZXW0E1001",
        "nr_inmatriculare": "B100AAA",
        "data_prestatie": "2026-06-10",
        "odometru_final": "50000",
        "prestatii": [{
            "cod_op_service": "Schimb ulei",
            "denumire": "Schimb ulei motor 5W30",
            # fara cod_prestatie initial
        }],
    })
    csrf = _csrf(client)

    # /repune cu cod direct
    resp = client.post(
        f"/trimitere/{sid}/repune",
        data={"csrf_token": csrf, "cod_prestatie": "OE-1"},
    )
    assert resp.status_code == 200, resp.text[:500]

    r = _row(sid)
    assert r["status"] == "queued", f"status neasteptat: {r['status']}"

    prestatii = _payload_json(sid)["prestatii"]
    assert len(prestatii) == 1
    item = prestatii[0]

    # IRON RULE E1: op_service si denumire TREBUIE sa fie prezente
    assert item.get("cod_op_service") == "Schimb ulei", (
        f"E1 VIOLATED: cod_op_service a fost sters de /repune! item={item}"
    )
    assert item.get("denumire") == "Schimb ulei motor 5W30", (
        f"E1 VIOLATED: denumire a fost stearsa de /repune! item={item}"
    )
    # Codul trebuie setat
    assert item.get("cod_prestatie") == "OE-1", (
        f"cod_prestatie nu a fost setat corect: item={item}"
    )


def test_repune_nu_trunchiaza_prestatii_multiple(client):
    """Bug fix (code-review 5.15): /repune NU pierde prestatii[1:].

    Formularul /repune trimite UN SINGUR select cod_prestatie. Implementarea veche
    itera `enumerate(codes)` -> pastra doar len(codes) itemi, deci un rand error cu
    2+ prestatii pierdea toate prestatiile dupa prima -> declaratie INCOMPLETA la RAR
    (FINALIZATA ireversibil). Fix: iteram peste `existing`, aplicam codes pozitional,
    pastram toate prestatiile.

    RED inainte de fix: len(prestatii) == 1 (a doua prestatie pierduta).
    """
    acct = _create_account_user("repune.multi@test.com")
    _login(client, "repune.multi@test.com")
    _seed_cod("AAA", "Prestatie A")
    _seed_cod("BBB", "Prestatie B")
    _seed_cod("CCC", "Prestatie C")

    # Rand error cu DOUA prestatii (ambele cu cod valid).
    sid = _insert(acct, status="error", payload={
        "vin": "WVWZZZ1JZXW0RM001",
        "nr_inmatriculare": "B100AAA",
        "data_prestatie": "2026-06-10",
        "odometru_final": "50000",
        "prestatii": [
            {"cod_prestatie": "AAA"},
            {"cod_prestatie": "BBB"},
        ],
    })
    csrf = _csrf(client)

    # /repune cu UN SINGUR cod nou (schimba prima prestatie).
    resp = client.post(
        f"/trimitere/{sid}/repune",
        data={"csrf_token": csrf, "cod_prestatie": "CCC"},
    )
    assert resp.status_code == 200, resp.text[:500]

    r = _row(sid)
    assert r["status"] == "queued", f"status neasteptat: {r['status']}"

    prestatii = _payload_json(sid)["prestatii"]
    assert len(prestatii) == 2, (
        f"AMBELE prestatii trebuie pastrate de /repune, nu doar prima! got={prestatii}"
    )
    coduri = [p.get("cod_prestatie") for p in prestatii]
    assert coduri == ["CCC", "BBB"], (
        f"Codul nou se aplica POZITIONAL primei prestatii, a doua ramane intacta: {coduri}"
    )


def test_corectie_eroare_validare_pastreaza_picker(client):
    """Bug fix (code-review 5.15): re-render-ul de eroare validare pastreaza optiunile pickerului.

    post_corectie_trimitere re-randa _trimitere_detaliu pe ramura erori-validare FARA
    `conn`/`account_id` -> `nomenclator_rar=[]` -> picker-ul chips randa ZERO optiuni ->
    userul nu mai poate alege cod RAR fara sa inchida+redeschida modalul. Fix: pasam
    `conn`+`account_id` la _detaliu_ctx pe TOATE ramurile de re-render.

    RED inainte de fix: codul de picker "PK-1" lipseste din re-render.
    """
    acct = _create_account_user("corectie.picker@test.com")
    _login(client, "corectie.picker@test.com")
    _seed_cod("ZZ-9", "Operatie existenta")   # codul curent al randului (valid -> fara unmapped)
    _seed_cod("PK-1", "Optiune picker")        # cod doar in nomenclator (detector de picker)

    # needs_data editabil, prestatie cu cod direct valid (resolve OK, fara unmapped).
    sid = _insert(acct, status="needs_data", payload={
        "vin": "WVWZZZ1JZXW0PK001",
        "nr_inmatriculare": "B100AAA",
        "data_prestatie": "2026-06-10",
        "odometru_final": "50000",
        "prestatii": [{"cod_prestatie": "ZZ-9"}],
    })
    csrf = _csrf(client)

    # Corectie cu VIN invalid -> validare esueaza -> ramura de re-render 1432.
    resp = client.post(
        f"/trimitere/{sid}/corecteaza",
        data={"csrf_token": csrf, "vin": "BAD"},
    )
    assert resp.status_code == 200
    assert _row(sid)["status"] == "needs_data"
    # Picker-ul trebuie sa contina optiunile din nomenclator (conn/account_id pasate).
    assert "PK-1" in resp.text, (
        "Picker-ul chips e GOL dupa eroare de validare — _detaliu_ctx fara conn/account_id"
    )


def test_repune_select_afiseaza_denumirea(client):
    """Bug fix (code-review 5.15): selectul /repune afiseaza denumirea operatiei.

    Template-ul folosea cheia gresita `item.nome_prestatie` (typo) -> optiunile
    apareau ca "AAA — " fara denumire. Cheia corecta e `nume_prestatie`.
    """
    acct = _create_account_user("repune.denumire@test.com")
    _login(client, "repune.denumire@test.com")
    _seed_cod("AAA", "Schimb ulei motor")

    sid = _insert(acct, status="error", payload={
        "vin": "WVWZZZ1JZXW0RD001",
        "nr_inmatriculare": "B100AAA",
        "data_prestatie": "2026-06-10",
        "odometru_final": "50000",
        "prestatii": [{"cod_prestatie": "AAA"}],
    })

    resp = client.get(f"/_fragments/trimitere/{sid}")
    assert resp.status_code == 200
    html = resp.text
    # Optiunea trebuie sa afiseze denumirea, nu doar codul gol.
    assert "Schimb ulei motor" in html, (
        "Selectul /repune nu afiseaza denumirea operatiei (typo nome_prestatie)"
    )
    assert "AAA — Schimb ulei motor" in html, (
        f"Optiunea select nu randeaza 'cod — denumire': {html[html.find('AAA'):html.find('AAA')+60]}"
    )


# ============================================================================= #
# Teste noi 5.16: US-004 (denumiri picker), US-005 (add_extra),                #
# US-006 (save picker fara buton), T-E3 (by-index), T-D1/T-E5, T-C1/T-E4      #
# ============================================================================= #

def test_picker_flat_arata_cod_si_denumire(client):
    """US-004 (5.16): picker plat afiseaza 'cod — denumire', nu doar codul.

    RED: _chips_prestatii.html:147 afiseaza doar {{ n.cod_prestatie }};
    modul operatii (:101) afiseaza deja 'cod — nume'. Fix: uniformizare.
    """
    acct = _create_account_user("picker.flat.denu@test.com")
    _login(client, "picker.flat.denu@test.com")
    _seed_cod("FRN1", "Sistem de franare")

    # Submission flat: fara cod_op_service (mod plat)
    sid = _insert(acct, status="needs_mapping", payload={
        "vin": "WVWZZZ1JZXW0US4001",
        "nr_inmatriculare": "B100AAA",
        "data_prestatie": "2026-06-10",
        "odometru_final": "50000",
        "prestatii": [],  # mod plat: fara operatii cu cod_op_service
    })

    resp = client.get(f"/_fragments/trimitere/{sid}")
    assert resp.status_code == 200
    # Optiunea trebuie sa arate 'FRN1 — Sistem de franare', nu doar 'FRN1'
    assert "FRN1 — Sistem de franare" in resp.text, (
        f"Picker plat nu arata denumirea: "
        f"{resp.text[resp.text.find('FRN1'):resp.text.find('FRN1')+80] if 'FRN1' in resp.text else 'FRN1 absent'}"
    )


def test_adauga_cod_extra_in_mod_operatii(client):
    """US-005 (5.16): in mod operatii, actiunea add_extra adauga un cod RAR liber.

    RED: post_form_chips nu are actiunea 'add_extra' -> chips_action ignorata.
    """
    acct = _create_account_user("add.extra.ops@test.com")
    _login(client, "add.extra.ops@test.com")
    _seed_cod("OE-1", "Schimb ulei motor")
    _seed_cod("FRN1", "Sistem de franare")
    csrf = _csrf(client)

    # Chips stare: 1 operatie deja mapata (mod ops) → _has_ops = True
    resp = client.post(
        "/form-chips",
        data={
            "csrf_token": csrf,
            "cod_prestatie": ["OE-1"],            # chip existent (op mapata)
            "chip_op_service": ["SchimbUlei"],
            "chip_denumire": ["Schimb ulei motor"],
            "chips_action": "add_extra",
            "chips_add_cod_flat": "FRN1",          # codul extra de adaugat
        },
    )
    assert resp.status_code == 200, resp.text[:300]
    # FRN1 trebuie sa apara in raspuns (chip extra adaugat)
    assert "FRN1" in resp.text, (
        f"Codul extra FRN1 nu a fost adaugat in mod operatii: {resp.text[:300]}"
    )
    # OE-1 trebuie sa ramana (chip original neatins)
    assert "OE-1" in resp.text, f"Chip original OE-1 disparut: {resp.text[:300]}"


def test_extra_cod_persistat_la_salvare(client):
    """US-005 (5.16): codul extra adaugat via form-chips e salvat la /corecteaza.

    Simulam starea form dupa add_extra: hidden inputs pentru op mapata (OE-1)
    + hidden inputs pentru chip extra flat (FRN1, fara op_service).
    """
    acct = _create_account_user("extra.persist@test.com")
    _login(client, "extra.persist@test.com")
    _seed_cod("OE-1", "Schimb ulei")
    _seed_cod("FRN1", "Sistem de franare")

    sid = _insert(acct, status="needs_mapping", payload=_payload_cu_ops(
        "WVWZZZ1JZXW0XP001",
        [("SchimbUlei", "Schimb ulei motor")],
    ))
    csrf = _csrf(client)

    # Form state dupa add_extra: op mapata (idx=0, OE-1) + chip extra flat (idx=1, FRN1)
    resp = client.post(
        f"/trimitere/{sid}/corecteaza",
        data={
            "csrf_token": csrf,
            "cod_prestatie": ["OE-1", "FRN1"],     # OE-1 pt op, FRN1 chip extra
            "chip_op_service": ["SchimbUlei", ""],  # idx 0 are op_service, idx 1 nu
            "chip_denumire": ["Schimb ulei motor", ""],
        },
    )
    assert resp.status_code == 200, resp.text[:300]

    r = _row(sid)
    assert r["status"] == "queued", f"status asteptat queued, got {r['status']}"
    prestatii = _payload_json(sid)["prestatii"]
    coduri = [p.get("cod_prestatie") for p in prestatii]
    assert "OE-1" in coduri, f"OE-1 (op mapata) lipsa: {prestatii}"
    assert "FRN1" in coduri, f"FRN1 (chip extra) lipsa: {prestatii}"


def test_extra_cod_validat_nomenclator(client):
    """US-005 (5.16): add_extra respinge cod necunoscut in nomenclator (invariant ORA-12899).

    RED: actiunea add_extra nu exista; dupa fix, cod invalid nu se adauga.
    """
    acct = _create_account_user("extra.valid@test.com")
    _login(client, "extra.valid@test.com")
    _seed_cod("OE-1", "Schimb ulei")
    csrf = _csrf(client)

    # add_extra cu cod INVALID (XX-99 nu e in nomenclator)
    resp = client.post(
        "/form-chips",
        data={
            "csrf_token": csrf,
            "cod_prestatie": ["OE-1"],
            "chip_op_service": ["SchimbUlei"],
            "chip_denumire": ["Schimb ulei"],
            "chips_action": "add_extra",
            "chips_add_cod_flat": "XX-99",  # cod necunoscut
        },
    )
    assert resp.status_code == 200
    html = resp.text
    # XX-99 NU trebuie sa apara ca chip valid (hidden input cu valoarea XX-99)
    import re as _re
    hidden_xx99 = _re.search(r'<input[^>]+name="cod_prestatie"[^>]+value="XX-99"', html)
    assert hidden_xx99 is None, (
        f"Codul invalid XX-99 a fost adaugat ca chip! HTML: {html[:500]}"
    )


def test_cod_ales_in_picker_se_salveaza_fara_buton_add(client):
    """US-006 (5.16): codul ales in picker flat se aplica la /corecteaza fara a apasa '+'.

    RED: post_corectie_trimitere citeste form.getlist('cod_prestatie') (hidden inputs)
    dar ignora 'chips_add_cod_flat' (picker neselectat ca chip) → submission ramane
    needs_mapping desi codul e ales.
    """
    acct = _create_account_user("picker.save.nobutton@test.com")
    _login(client, "picker.save.nobutton@test.com")
    _seed_cod("OE-1", "Schimb ulei motor")

    # Submission flat fara prestatii
    sid = _insert(acct, status="needs_mapping", payload={
        "vin": "WVWZZZ1JZXW0PS001",
        "nr_inmatriculare": "B100AAA",
        "data_prestatie": "2026-06-10",
        "odometru_final": "50000",
        "prestatii": [],
    })
    csrf = _csrf(client)

    # Browser trimite chips_add_cod_flat=OE-1 (ales in picker) dar FARA hidden cod_prestatie
    # (userul nu a apasat '+' sa promoveze selectia intr-un chip).
    resp = client.post(
        f"/trimitere/{sid}/corecteaza",
        data={
            "csrf_token": csrf,
            "chips_add_cod_flat": "OE-1",  # ales in picker, ne-aprobat prin '+'
            # NU exista 'cod_prestatie' in form (zero hidden chips)
        },
    )
    assert resp.status_code == 200, resp.text[:300]

    r = _row(sid)
    assert r["status"] == "queued", (
        f"Codul ales in picker trebuia sa se aplice la salvare fara '+': status={r['status']}"
    )
    prestatii = _payload_json(sid)["prestatii"]
    coduri = [p.get("cod_prestatie") for p in prestatii]
    assert "OE-1" in coduri, f"OE-1 (ales in picker) lipsa din prestatii: {prestatii}"


def test_salvare_fara_chip_explicit_nu_e_no_op(client):
    """US-006 (5.16): o trimitere needs_mapping cu cod ales in picker nu ramane no-op.

    Complementar cu test_cod_ales_in_picker_se_salveaza_fara_buton_add: verifica
    explicit ca statusul se schimba (nu ramane needs_mapping).
    """
    acct = _create_account_user("noop.previne@test.com")
    _login(client, "noop.previne@test.com")
    _seed_cod("FRN1", "Sistem de franare")

    sid = _insert(acct, status="needs_mapping", payload={
        "vin": "WVWZZZ1JZXW0NP001",
        "nr_inmatriculare": "B100AAA",
        "data_prestatie": "2026-06-10",
        "odometru_final": "50000",
        "prestatii": [],
    })
    old_status = _row(sid)["status"]
    assert old_status == "needs_mapping"
    csrf = _csrf(client)

    resp = client.post(
        f"/trimitere/{sid}/corecteaza",
        data={
            "csrf_token": csrf,
            "chips_add_cod_flat": "FRN1",
        },
    )
    assert resp.status_code == 200
    new_status = _row(sid)["status"]
    assert new_status != "needs_mapping", (
        f"Salvarea cu cod ales in picker trebuia sa nu fie no-op: status ramas {new_status}"
    )
    assert new_status == "queued", f"status asteptat queued, got {new_status}"


def test_picker_by_index_op2_nu_op1(client):
    """T-E3 (5.16): codul ales pe picker-ul op#2 aterizeaza pe op#2, NU pe op#1.

    Verifica alinierea by-index in modul operatii: chips_add_op_index=1 + chips_add_cod_1
    actualizeaza chips[1] (op#2), nu chips[0] (op#1).
    """
    acct = _create_account_user("byindex.op2@test.com")
    _login(client, "byindex.op2@test.com")
    _seed_cod("OE-1", "Schimb ulei")
    _seed_cod("FRN1", "Sistem de franare")
    csrf = _csrf(client)

    # Chips: op#1 (idx=0) deja mapata cu OE-1, op#2 (idx=1) nemapata (cod gol)
    resp = client.post(
        "/form-chips",
        data={
            "csrf_token": csrf,
            "cod_prestatie": ["OE-1", ""],          # idx 0=OE-1 (mapata), idx 1="" (nemapata)
            "chip_op_service": ["Op-A", "Op-B"],
            "chip_denumire": ["Prima", "A doua"],
            "chips_action": "add",
            "chips_add_op_index": "1",               # adauga pe op#2 (idx=1)
            "chips_add_cod_1": "FRN1",               # picker-ul op#2 contine FRN1
        },
    )
    assert resp.status_code == 200, resp.text[:300]
    html = resp.text

    import re as _re
    hidden_vals = _re.findall(r'<input[^>]+name="cod_prestatie"[^>]+value="([^"]*)"', html)
    assert "OE-1" in hidden_vals, f"OE-1 (op#1) a disparut dupa adaugare pe op#2: {hidden_vals}"
    assert "FRN1" in hidden_vals, f"FRN1 nu a aterizat pe op#2: {hidden_vals}"
    # By-index: OE-1 trebuie sa fie INAINTE de FRN1 (idx 0 < idx 1)
    oe1_pos = hidden_vals.index("OE-1") if "OE-1" in hidden_vals else -1
    frn1_pos = hidden_vals.index("FRN1") if "FRN1" in hidden_vals else -1
    assert oe1_pos < frn1_pos, (
        f"FRN1 (op#2, idx=1) trebuie dupa OE-1 (op#1, idx=0) by-index: {hidden_vals}"
    )


def test_empty_state_picker_nomenclator_gol(client):
    """T-D1/T-E5 (5.16): empty-state vizibil cand nomenclatorul e gol.

    RED: {% if nomenclator_rar %} fara {% else %} -> silentios; un rand needs_mapping
    fara nomenclator nu are nicio cale de a adauga cod (nereparabil silentios).
    GREEN: div.chips-nom-gol vizibil.
    """
    acct = _create_account_user("empty.nom@test.com")
    _login(client, "empty.nom@test.com")
    # Golim nomenclatorul: seed_nomenclator_if_empty populeaza la initializare DB;
    # testul simuleaza cazul extrem cand tabla e goala (post-update, inainte de re-seed).
    from app.db import get_connection as _gconn
    _c = _gconn()
    _c.execute("DELETE FROM nomenclator_rar")
    _c.commit()
    _c.close()

    sid = _insert(acct, status="needs_mapping", payload={
        "vin": "WVWZZZ1JZXW0EN001",
        "nr_inmatriculare": "B100AAA",
        "data_prestatie": "2026-06-10",
        "odometru_final": "50000",
        "prestatii": [],
    })

    resp = client.get(f"/_fragments/trimitere/{sid}")
    assert resp.status_code == 200
    assert "chips-nom-gol" in resp.text, (
        f"Empty state 'chips-nom-gol' lipsa cand nomenclatorul e gol: {resp.text[resp.text.find('chips'):resp.text.find('chips')+200] if 'chips' in resp.text else resp.text[:500]}"
    )


def test_add_extra_semnal_vizibil_cod_invalid(client):
    """T-C1/T-E4 (5.16): add_extra cu cod invalid da semnal vizibil (nu esua silentios).

    RED: actiunea add_extra nu exista → nu exista niciun semnal.
    GREEN: div.chips-extra-error vizibil cand codul e invalid sau selectul e gol.
    """
    acct = _create_account_user("extra.err.signal@test.com")
    _login(client, "extra.err.signal@test.com")
    _seed_cod("OE-1", "Schimb ulei")
    csrf = _csrf(client)

    # add_extra cu cod necunoscut in nomenclator
    resp = client.post(
        "/form-chips",
        data={
            "csrf_token": csrf,
            "cod_prestatie": ["OE-1"],
            "chip_op_service": ["SchimbUlei"],
            "chip_denumire": ["Schimb ulei"],
            "chips_action": "add_extra",
            "chips_add_cod_flat": "XX-99",  # cod inexistent
        },
    )
    assert resp.status_code == 200
    assert "chips-extra-error" in resp.text, (
        f"Semnalul 'chips-extra-error' lipsa pentru cod invalid: {resp.text[:300]}"
    )
