"""Teste PRD 5.9 US-006: fundatie responsive — viewport, header/nav colapsabil,
modal full-screen pe mobil, breakpoint-uri consistente.

Verificam markup-ul + CSS server-side randat (TestClient): prezenta meta viewport,
existenta unei reguli `@media (max-width:767px)` care trece modalul pe full-screen
(`.modal-dialog`) cu buton `x` >=44px, si structura de nav colapsabil (meniul de cont
hamburger + tinte touch >=44px). Verificarile vizuale efective (375px, fara scroll
orizontal) sunt deferate la VERIFY (gstack browser).
"""

from __future__ import annotations

import json
import os
import re
import tempfile

import pytest
from starlette.testclient import TestClient


def _create_account_user(email: str, name: str = "Service", 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, name, active=True)
        create_user(conn, acct_id, email, password)
        return acct_id
    finally:
        conn.close()


def _insert_submission(acct: int, status: str = "needs_data") -> int:
    from app.db import get_connection

    conn = get_connection()
    try:
        p = {
            "vin": "WVWZZZ1JZXW000888",
            "nr_inmatriculare": "B888ZZZ",
            "data_prestatie": "2026-06-18",
            "odometru_final": "55000",
            "prestatii": [{"cod_prestatie": "R-FRANE", "denumire": "Reparatie frane"}],
        }
        cur = conn.execute(
            "INSERT INTO submissions (idempotency_key, account_id, status, payload_json) "
            "VALUES (?, ?, ?, ?)",
            (f"k-{status}-{os.urandom(4).hex()}", acct, status, json.dumps(p)),
        )
        conn.commit()
        return int(cur.lastrowid)
    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
    resp = client.post("/login", data={"email": email, "parola": password, "csrf_token": m.group(1)})
    assert resp.status_code == 303


def _bloc_mobil_principal(html: str) -> str:
    """Felie din CSS de la sentinel-ul blocului mobil principal pana la `</style>`."""
    i = html.find("SENTINEL-TESTE-MOBIL")
    assert i != -1, "Lipseste SENTINEL-TESTE-MOBIL in base.html (vezi PRD 5.13 Wave 0)"
    j = html.find("</style>", i)
    return html[i:(j if j != -1 else len(html))]


@pytest.fixture()
def client(monkeypatch):
    tmp = tempfile.mkdtemp()
    monkeypatch.setenv("AUTOPASS_DB_PATH", os.path.join(tmp, "responsive.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()


def test_viewport_meta_prezent(client):
    """`<meta name=viewport>` cu width=device-width prezent in base.html."""
    _create_account_user("vp@test.com")
    _login(client, "vp@test.com")
    html = client.get("/?tab=acasa").text
    assert 'name="viewport"' in html
    assert "width=device-width" in html
    assert "initial-scale=1" in html


def test_modal_fullscreen_clasa_mobil(client):
    """Sub 768px modalul devine full-screen: o regula `@media (max-width:767px)`
    pune `.modal-dialog` la latime/inaltime pline (fara latimea marginita de desktop)
    si butonul `x` la >=44px. Desktop pastreaza regula centrata (`max-width:680px`)."""
    _create_account_user("mf@test.com")
    _login(client, "mf@test.com")
    html = client.get("/?tab=acasa").text

    # Regula de baza (desktop) ramane: dialog centrat cu latime marginita.
    assert "max-width:680px" in html

    # Exista un bloc media mobil care vizeaza modalul.
    assert "@media (max-width:767px)" in html
    # Dialogul ocupa tot ecranul pe mobil (latime/inaltime pline, fara border-radius lateral).
    mobil = html[html.find("@media (max-width:767px)"):]
    assert "100vw" in mobil or "width:100%" in mobil
    assert "100vh" in mobil
    # Butonul de inchidere >=44px pe mobil (tinta touch).
    assert "44px" in mobil


def test_nav_colapsabil_sub_breakpoint(client):
    """Nav colapsabil: meniul de cont e un buton hamburger (☰) ascuns intr-un dropdown,
    iar tintele touch (icon-btn, tab-link, itemi meniu) ajung la >=44px sub breakpoint."""
    _create_account_user("nav@test.com")
    _login(client, "nav@test.com")
    html = client.get("/?tab=acasa").text

    # Hamburger-ul de cont exista si dropdown-ul e colapsat (hidden) implicit.
    assert 'id="cont-menu-toggle"' in html
    assert "&#9776;" in html  # pictograma hamburger
    assert 'id="cont-menu"' in html
    assert 'class="cont-menu"' in html

    # Tab-bar-ul (nav principal) exista si e scrollabil orizontal (nu deborda pagina).
    assert "tab-bar" in html
    assert "overflow-x:auto" in html

    # Sub breakpoint, tintele touch din header/nav cresc la >=44px.
    mobil = html[html.find("@media (max-width:767px)"):]
    assert "44px" in mobil


# ============================================================
# PRD 5.9 US-007: responsive paginile de continut
# (Mapari, Cont, Nomenclator, Integrare, Jurnal, Admin)
# ============================================================


def _seed_nomenclator(items):
    from app.mapping import upsert_nomenclator
    from app.db import get_connection

    conn = get_connection()
    try:
        upsert_nomenclator(conn, items)
        conn.commit()
    finally:
        conn.close()


def _seed_event(account_id, tip="test", mesaj="eveniment de test"):
    from app.observ import log_event
    from app.db import get_connection

    conn = get_connection()
    try:
        log_event(tip, account_id=account_id, mesaj=mesaj, conn=conn)
        conn.commit()
    finally:
        conn.close()


def test_tabele_continut_au_clasa_responsive(client):
    """R12 politica per-tabel:
    - Mapari (actionabil) = CARD per rand: clasa proprie `.tabel-card` + `data-eticheta` pe `<td>`-uri.
    - Jurnal / Nomenclator (dense read-only) = `.tablewrap` (scroll orizontal CONTAINED),
      FARA `.tabel-card`.
    Definitia regulii de card traieste in base.html, scopata SEPARAT de `.tabel-trimiteri`.
    """
    acct = _create_account_user("t7@test.com")
    _login(client, "t7@test.com")

    # --- Mapari = card. Tabelul „Reguli automate (text)" e mereu randat (rand de adaugare),
    #     deci nu depinde de date seedate. ---
    mapari = client.get("/?tab=mapari").text
    assert "tabel-card" in mapari
    assert 'data-eticheta="Cod RAR"' in mapari
    assert 'data-eticheta="Daca operatia contine"' in mapari
    # Regula de card e definita o data in base.html, scopata pe `.tabel-card`.
    assert ".tabel-card thead" in mapari

    # --- Jurnal = scroll contained. Cu un eveniment seedat, tabelul apare in `.tablewrap`,
    #     NU ca `.tabel-card`. ---
    _seed_event(acct)
    jurnal = client.get("/?tab=jurnal").text
    assert "tablewrap" in jurnal
    # Sectiunea de jurnal NU foloseste cardul actionabil.
    body = jurnal[jurnal.find('id="jurnal-section"'):]
    assert "tabel-card" not in body

    # --- Nomenclator = scroll contained (dens read-only). ---
    _seed_nomenclator([{"codPrestatie": "OE-2", "numePrestatie": "Revizie"}])
    nomen = client.get("/?tab=nomenclator").text
    assert "tablewrap" in nomen


def test_formulare_full_width_mobil(client):
    """Formularele de continut (Cont, Integrare test, filtre Jurnal) stiveaza pe o coloana
    sub 767px: inputuri full-width + butoane >=44px. Verificam ancorele de scope (id-uri)
    plus regulile CSS mobil din base.html (toate scopate sub `@media (max-width:767px)`)."""
    _create_account_user("f7@test.com")
    _login(client, "f7@test.com")

    cont = client.get("/?tab=cont").text
    assert 'id="card-cont"' in cont
    # Regulile mobil de formular exista si sunt scopate pe sectiunile de continut.
    mobil = cont[cont.find("@media (max-width:767px)"):]
    assert "#card-cont button" in mobil
    assert "min-height:44px" in mobil
    assert "width:100% !important" in mobil  # suprascrie latimile inline doar pe mobil

    integrare = client.get("/?tab=integrare").text
    assert 'id="form-test-cheie"' in integrare

    # Filtrele de jurnal (form mereu prezent, indiferent de date) primesc scope-ul mobil.
    jurnal = client.get("/?tab=jurnal").text
    assert 'id="filtre-jurnal"' in jurnal
    assert "#jurnal-section #filtre-jurnal button" in jurnal


def test_carduri_trimiteri_5_8_supravietuiesc(client):
    """Regresie R12: scoparea `.tabel-card` (US-007) NU trebuie sa atinga blocul
    `.tabel-trimiteri @media(max-width:767px)` din 5.8 — cardurile de trimiteri raman."""
    _create_account_user("r58@test.com")
    _login(client, "r58@test.com")
    html = client.get("/?tab=acasa").text

    mobil = html[html.find("@media (max-width:767px)"):]
    # Cardurile de trimiteri 5.8 (clasa proprie, separata de `.tabel-card`).
    assert ".tabel-trimiteri thead { display:none; }" in mobil
    assert ".tabel-trimiteri td::before" in mobil
    # Cele doua mecanisme coexista, scopate distinct.
    assert ".tabel-card thead" in mobil


# ============================================================
# PRD 5.9 US-008: responsive Acasa (upload, status, filtre) + login/signup
# ============================================================


def test_acasa_fara_scroll_orizontal_mobil(client):
    """US-008: pe Acasa sub 767px zona de upload, bara de status si bara de filtre
    stiveaza pe O coloana, cu inputuri/butoane full-width >=44px. Verificam ancorele
    de scope (id-uri) + regulile CSS mobil din base.html (toate sub `@media (max-width:767px)`),
    fara sa atingem cardurile de trimiteri 5.8 (verificate separat)."""
    acct = _create_account_user("a8@test.com")
    _insert_submission(acct)  # sectiunea Trimiteri (filtre + wrap) apare doar cu randuri
    _login(client, "a8@test.com")
    html = client.get("/?tab=acasa").text

    # Ancore de scope prezente in markup.
    assert 'id="import-section"' in html
    assert 'id="status-bar"' in html
    assert 'id="filtre-trimiteri"' in html

    mobil = html[html.find("@media (max-width:767px)"):]

    # Bara de upload: zona de drop trece pe coloana, butonul de alegere full-width.
    assert "#import-section .drop-zone" in mobil
    assert "#import-section #upload-btn" in mobil

    # Bara de filtre: o coloana, controale full-width, buton >=44px.
    assert "#filtre-trimiteri" in mobil
    filtre = mobil[mobil.find("#filtre-trimiteri"):]
    assert "width:100% !important" in filtre  # suprascrie latimile inline (max-width:180px etc.)
    assert "min-height:44px" in mobil

    # Bara de status stiveaza pe coloana (scope dedicat).
    assert "#status-bar" in mobil


def test_login_signup_full_width_mobil(client):
    """US-008: login.html si signup.html randeaza un card centrat (`.auth-card`, margin auto)
    care nu depaseste latimea pe mobil (max-width:100% sub 767px), cu inputuri full-width.
    Rutele `/login` si `/signup` sunt publice (fara autentificare)."""
    for ruta in ("/login", "/signup"):
        html = client.get(ruta).text
        # Card de autentificare marcat si centrat.
        assert "auth-card" in html, ruta
        assert "margin:40px auto" in html or "margin:24px auto" in html, ruta
        # Inputurile sunt full-width.
        assert "width:100%" in html, ruta
        # Regula mobil: cardul nu depaseste viewport-ul.
        mobil = html[html.find("@media (max-width:767px)"):]
        assert ".auth-card" in mobil, ruta


# ============================================================
# PRD 5.12 US-008: responsive tableta+mobil + header fara suprapuneri
# ============================================================


def test_header_are_breakpoint_tableta(client):
    """US-008 RED: exista reguli @media intre 768 si 1024 pentru header.
    Desktop: grid 3-coloane + min-height:92px. Pe tableta (768-1024px) nu exista
    inca breakpoint — logo+titlu+badge+tema+versiune+hamburger se inghesuie si se suprapun."""
    _create_account_user("bt@test.com")
    _login(client, "bt@test.com")
    html = client.get("/?tab=acasa").text

    # Bloc media dedicat tabletei (range min-width:768 si max-width:1024px).
    assert "@media (min-width:768px) and (max-width:1024px)" in html, \
        "Lipseste blocul @media tableta (min-width:768px) and (max-width:1024px)"

    # Blocul tableta contine reguli pentru header sau elemente de header.
    idx = html.find("@media (min-width:768px) and (max-width:1024px)")
    bloc = html[idx:idx + 800]
    assert "header" in bloc or ".brand-logo" in bloc, \
        "Blocul tableta nu are reguli pentru header sau .brand-logo"


def test_header_elemente_nu_au_min_height_fix_pe_mobil(client):
    """US-008 RED: header-ul nu forteaza min-height:92px pe tableta si mobil.
    Regula de baza (desktop) are min-height:92px; pe tableta (768-1024px)
    lipseste resetarea -> inghesuire garantata la ~820px."""
    _create_account_user("mh@test.com")
    _login(client, "mh@test.com")
    html = client.get("/?tab=acasa").text

    # Regula de baza desktop are min-height:92px (sa nu dispara).
    assert "min-height:92px" in html, "Regula desktop min-height:92px a disparut"

    # Blocul tableta (768-1024px) trebuie sa reseteze min-height pe header.
    idx_t = html.find("@media (min-width:768px) and (max-width:1024px)")
    assert idx_t != -1, "Lipseste blocul @media tableta (768-1024px)"
    tableta = html[idx_t:idx_t + 800]
    assert "min-height:0" in tableta, \
        "Blocul tableta nu reseteaza min-height pentru header"

    # Blocul mobil (<768px) reseteaza si el min-height (regresie: nu a disparut).
    mobil = _bloc_mobil_principal(html)
    assert "min-height:0" in mobil, "Blocul mobil a pierdut resetarea min-height pe header"


def test_modal_full_screen_pe_mobil(client):
    """US-008 D#13 verificare: regula full-screen mobil pentru modal exista in base.html
    (@media max-width:767px) si se aplica modalului global prin clasa modal-overlay.
    VERIFICA prezenta regulii, NU re-implementa."""
    _create_account_user("mfp@test.com")
    _login(client, "mfp@test.com")
    html = client.get("/?tab=acasa").text

    # Regula CSS full-screen exista in blocul @media (max-width:767px) {.
    mobil = _bloc_mobil_principal(html)
    assert "100vw" in mobil, "Dialogul nu are latime 100vw pe mobil"
    assert "100vh" in mobil, "Dialogul nu are inaltime 100vh pe mobil"
    # Butonul de inchidere >=44px (tinta touch) pe mobil.
    assert "44px" in mobil, "Butonul modal-close nu are tinta touch 44px pe mobil"

    # Modalul global din HTML foloseste clasa modal-overlay -> prinde regula CSS.
    assert 'class="modal-overlay"' in html, \
        "Modalul global nu are class=modal-overlay (nu prinde regula full-screen)"
    # Target swap pentru editare preview (US-006) exista in DOM.
    assert 'id="detaliu-modal-body"' in html, \
        "Target #detaliu-modal-body lipseste din base.html"


# ============================================================
# PRD 5.13: guard-uri responsive card mobil + sistem actiuni + stepper compact
# ============================================================


def test_card_mobil_fara_break_vertical_120px(client):
    """P0: blocul mobil principal NU mai forteaza min-width:120px pe eticheta
    (cauza break-ului vertical caracter-cu-caracter). Eticheta stivuita deasupra
    valorii: td::before cu display:block. Checkbox + # ascunse pe card."""
    _create_account_user("card120@test.com")
    _login(client, "card120@test.com")
    html = client.get("/?tab=acasa").text
    bloc = _bloc_mobil_principal(html)

    # min-width:120px nu mai exista in blocul mobil.
    assert "min-width:120px" not in bloc, \
        "min-width:120px inca prezent in blocul mobil — cauzeaza break vertical"

    # td::before stivuieste eticheta deasupra valorii (display:block).
    assert ".tabel-trimiteri td::before" in bloc, \
        ".tabel-trimiteri td::before lipseste din blocul mobil"
    assert "display:block" in bloc, \
        "display:block lipseste din blocul mobil (eticheta nu e stivuita)"

    # col-chk si col-id ascunse pe card (nu ocupa spatiu).
    assert ".tabel-trimiteri td.col-chk, .tabel-trimiteri td.col-id { display:none; }" in bloc, \
        "col-chk/col-id nu sunt ascunse in blocul mobil"


def test_sistem_act_desktop_text_mobil_icon(client):
    """Sistemul .act: desktop = iconita ascunsa (text vizibil),
    mobil = text ascuns, iconita vizibila 44px (tinta touch)."""
    _create_account_user("act_sys@test.com")
    _login(client, "act_sys@test.com")
    html = client.get("/?tab=acasa").text
    bloc = _bloc_mobil_principal(html)

    # Desktop: act-ic ascunsa implicit (display:none in regula de baza).
    assert ".act .act-ic { width:18px; height:18px; display:none; }" in html, \
        "Regula desktop care ascunde .act-ic lipseste din CSS"

    # Mobil: text ascuns, iconita vizibila.
    assert ".act .act-tx { display:none; }" in bloc, \
        ".act .act-tx nu e ascuns in blocul mobil"
    assert ".act .act-ic { display:inline-block; }" in bloc, \
        ".act .act-ic nu devine vizibila in blocul mobil"

    # .act are tinta touch >=44px pe mobil.
    assert ".act { min-width:44px" in bloc, \
        ".act nu are min-width:44px in blocul mobil"


def _seed_saved_mapping_responsive(acct_id: int) -> None:
    """Insereaza o mapare salvata in operations_mapping (pentru test act aria-label)."""
    from app.db import get_connection
    conn = get_connection()
    try:
        conn.execute(
            "INSERT INTO operations_mapping (account_id, cod_op_service, cod_prestatie, auto_send) "
            "VALUES (?, ?, ?, ?)",
            (acct_id, "OP-RESP-99", "OE-1", 1),
        )
        conn.commit()
    finally:
        conn.close()


def test_act_btn_aria_label_invariant(client):
    """Invariant a11y: butoanele .act din Mapari au mereu aria-label
    (accesibil in modul icon-only pe mobil) si iconita .act-ic."""
    acct = _create_account_user("actaria@test.com")
    _seed_saved_mapping_responsive(acct)
    _login(client, "actaria@test.com")

    html = client.get("/_fragments/mapari").text

    # Butoane cu clasa act exista in fragmentul de mapari.
    act_btns = re.findall(r'<button[^>]+class="act[^"]*"[^>]*>', html)
    assert act_btns, "Trebuie sa existe butoane cu clasa 'act' in fragmentul Mapari"

    # Fiecare buton .act are aria-label (accesibil cand textul e ascuns pe mobil).
    assert any("aria-label" in btn for btn in act_btns), \
        "Niciun buton .act nu are aria-label — inaccessibil in modul icon-only"

    # Iconita .act-ic prezenta in markup (afisata pe mobil).
    assert 'class="act-ic"' in html, \
        "Iconita .act-ic lipseste din fragmentul Mapari"


def test_stepper_compact_clase(client):
    """Stepper compact (5.13): clasele stepper-track, stepper-collapsed,
    stepper-progress prezente; textul 'Pasul N din 4' randat; vechile clase
    stepper-pas-- absente; comutarea la <1024px declarata in CSS."""
    _create_account_user("stepper@test.com")
    _login(client, "stepper@test.com")
    html = client.get("/?tab=acasa").text

    # Elementele de structura ale stepper-ului compact.
    assert "stepper-track" in html, "stepper-track lipseste din markup"
    assert "stepper-collapsed" in html, "stepper-collapsed lipseste din markup"
    assert "stepper-progress" in html, "stepper-progress lipseste din markup"

    # Textul de progres in forma colapsata (<1024px).
    assert "Pasul 1 din 4" in html, "Textul 'Pasul 1 din 4' lipseste din markup"

    # Vechile clase anti-pattern cu stepper-pas-- nu mai exista.
    assert "stepper-pas--" not in html, \
        "Clasa veche stepper-pas-- inca prezenta — curata markup-ul"

    # CSS declara comutarea la <1024px (track ascuns, collapsed afisat).
    assert "@media (max-width:1024px)" in html, \
        "Lipseste regula @media (max-width:1024px) pentru comutarea stepper-ului"


def test_liste_actionabile_o_coloana_pana_1024(client):
    """Guard scope (decizie 5.13): listele actionabile raman O COLOANA
    pana la 1024px — fara grila 2/rand (repeat(2)). Blocul tableta 768-1024px
    cardifica (thead ascuns, card per rand)."""
    _create_account_user("ocoloana@test.com")
    _login(client, "ocoloana@test.com")
    html = client.get("/?tab=acasa").text

    # Nicaieri in CSS nu apare grila 2/rand (repeat(2, ...)).
    assert "repeat(2" not in html, \
        "CSS contine repeat(2 — listele actionabile NU trebuie sa fie 2/rand pana la 1024px"

    # Exista blocul tableta (768-1024px).
    assert "@media (min-width:768px) and (max-width:1024px)" in html, \
        "Lipseste blocul @media tableta (min-width:768px) and (max-width:1024px)"

    # Blocul tableta cardifica listele (thead ascuns = card per rand, o coloana).
    assert ".tabel-trimiteri thead, .tabel-card thead { display:none; }" in html, \
        "Blocul tableta nu ascunde thead-ul pentru cardificare (o coloana)"


# ============================================================
# PRD 5.15 US-002: componente de design slim (CSS, fara consumatori)
# ============================================================


def _bloc_componente_slim(html: str) -> str:
    """Extrage blocul CSS dintre sentinelii SENTINEL-COMPONENTE-SLIM (inceput si sfarsit).
    Testeaza existenta ambilor sentineli si returneaza continutul dintre ei.
    """
    sentinel = "SENTINEL-COMPONENTE-SLIM"
    i = html.find(sentinel)
    assert i != -1, "Lipseste SENTINEL-COMPONENTE-SLIM in base.html (US-002 PRD 5.15)"
    i2 = html.find(sentinel, i + len(sentinel))
    assert i2 != -1, "Lipseste al doilea SENTINEL-COMPONENTE-SLIM (sfarsit bloc US-002)"
    return html[i : i2 + len(sentinel)]


def test_clasa_contor_card(client):
    """.contor-card: fundal --card2, bordura --line, radius 8px, padding.
    Sub-elemente: .contor-cifra (cifra mare bold), .contor-label (muted), .contor-sub (mono).
    """
    _create_account_user("cc2@test.com")
    _login(client, "cc2@test.com")
    html = client.get("/?tab=acasa").text

    assert ".contor-card" in html, ".contor-card lipseste din CSS (base.html)"

    bloc = _bloc_componente_slim(html)
    assert "var(--card2)" in bloc, ".contor-card nu foloseste var(--card2) ca fundal"
    assert "var(--line)" in bloc, ".contor-card nu are bordura var(--line)"
    assert "border-radius:8px" in bloc, ".contor-card lipseste border-radius:8px"
    assert ".contor-cifra" in bloc, "sub-elementul .contor-cifra lipseste din bloc"
    assert ".contor-label" in bloc, "sub-elementul .contor-label lipseste din bloc"
    assert "var(--muted)" in bloc, ".contor-label nu foloseste var(--muted)"


def test_clasa_lista_slim(client):
    """.lista-trimiteri-slim + .trimitere-slim: separator --line2, padding, tinta min-height:44px.
    Sub-elemente: .slim-vin (mono) si .slim-meta (muted 11px).
    """
    _create_account_user("ls2@test.com")
    _login(client, "ls2@test.com")
    html = client.get("/?tab=acasa").text

    assert ".lista-trimiteri-slim" in html, ".lista-trimiteri-slim lipseste din CSS (base.html)"
    assert ".trimitere-slim" in html, ".trimitere-slim lipseste din CSS (base.html)"

    bloc = _bloc_componente_slim(html)
    assert "var(--line2)" in bloc, ".trimitere-slim nu foloseste var(--line2) ca separator"
    assert "min-height:44px" in bloc, ".trimitere-slim nu are tinta min-height:44px"
    assert ".slim-vin" in bloc, ".slim-vin lipseste din bloc"
    assert ".slim-meta" in bloc, ".slim-meta lipseste din bloc"
    assert "var(--muted)" in bloc, ".slim-meta nu foloseste var(--muted)"


def test_clasa_camp_slim(client):
    """.camp-slim CSS exista cu fundal --card2.
    Macro-ul camp() din _macros.html suporta parametrul slim=False ca default.
    Default slim=False garanteaza ca randarea actuala ramane neschimbata.
    """
    _create_account_user("cslim2@test.com")
    _login(client, "cslim2@test.com")
    html = client.get("/?tab=acasa").text

    assert ".camp-slim" in html, ".camp-slim lipseste din CSS (base.html)"

    bloc = _bloc_componente_slim(html)
    assert "var(--card2)" in bloc, ".camp-slim nu foloseste var(--card2) ca fundal"

    # Macro-ul camp() din _macros.html trebuie sa aiba parametrul slim
    macros_path = os.path.join(
        os.path.dirname(__file__), "..", "app", "web", "templates", "_macros.html"
    )
    with open(macros_path, encoding="utf-8") as f:
        macros = f.read()
    assert "slim" in macros, "macro-ul camp() nu are parametrul slim in _macros.html"
    assert "slim=False" in macros, (
        "macro-ul camp() nu are slim=False ca default — randarea actuala poate fi rupta"
    )


def test_clasa_chips(client):
    """.chips (container) + .chip (item): accent 18%, font 10-11px.
    .chip-del: buton de stergere accesibil (element separat in CSS).
    """
    _create_account_user("chp2@test.com")
    _login(client, "chp2@test.com")
    html = client.get("/?tab=acasa").text

    assert ".chips" in html, ".chips lipseste din CSS (base.html)"
    assert ".chip" in html, ".chip lipseste din CSS (base.html)"

    bloc = _bloc_componente_slim(html)
    assert "var(--accent)" in bloc, ".chip nu foloseste var(--accent)"
    assert "18%" in bloc, ".chip nu are fundal accent 18% (color-mix accent 18%)"
    assert "11px" in bloc or "10px" in bloc, ".chip nu are font 10-11px"
    assert ".chip-del" in bloc, ".chip-del (buton de stergere) lipseste din bloc"


def test_fara_hex_in_componente_noi(client):
    """Zero hex literal in blocul CSS nou (SENTINEL-COMPONENTE-SLIM).
    Toate culorile folosesc EXCLUSIV var(--token), zero #rrggbb hardcodat.
    Ancorat pe SENTINEL ca sa nu scaneze blocul CSS vechi (unde exista #fff).
    """
    _create_account_user("hexfree2@test.com")
    _login(client, "hexfree2@test.com")
    html = client.get("/?tab=acasa").text

    bloc = _bloc_componente_slim(html)
    # Cauta hex literals in proprietati CSS de culoare
    hex_in_props = re.findall(
        r"(?:color|background|border(?:-color)?|fill|stroke)\s*:[^;{}]*?"
        r"(#[0-9a-fA-F]{3,8})",
        bloc,
    )
    assert not hex_in_props, (
        f"Hex literal gasit in componente noi US-002 — folositi var(--token): {hex_in_props}"
    )


# ============================================================
# PRD 5.15 US-004: lista slim trimiteri — layout consistent desktop + <=1024px
# ============================================================


def test_lista_slim_randeaza_si_are_tinta_touch(client):
    """US-004: lista slim randeaza cu .trimitere-slim; tinta touch >=44px
    e garantata de CSS (min-height:44px din blocul SENTINEL-COMPONENTE-SLIM).
    Cardurile .tabel-trimiteri din 5.8 nu regreseaza: regula tabel-trimiteri
    thead display:none (card pe mobil) exista in continuare in base.html.
    """
    acct = _create_account_user("slim_resp@test.com")
    _insert_submission(acct, status="sent")
    _login(client, "slim_resp@test.com")

    html = client.get("/?tab=acasa").text

    # Lista slim randeaza (elementele sunt injectate via hx-get="/_fragments/submissions"
    # -> testam ca clasele CSS sunt prezente in base.html, gata sa fie consumate)
    bloc = _bloc_componente_slim(html)
    assert "lista-trimiteri-slim" in bloc, \
        ".lista-trimiteri-slim lipseste din blocul CSS slim (US-002 prerequisite)"
    assert "trimitere-slim" in bloc, \
        ".trimitere-slim lipseste din blocul CSS slim"
    assert "min-height:44px" in bloc, \
        ".trimitere-slim nu are min-height:44px — tinta touch mobil garantata"

    # Regresie guard: regula card per rand 5.8 supravietuieste (o coloana pe mobil)
    mobil = _bloc_mobil_principal(html)
    assert ".tabel-trimiteri thead { display:none; }" in mobil, \
        "Regula card 5.8 (.tabel-trimiteri thead display:none) a disparut din CSS"


def test_lista_slim_layout_tableta_1024(client):
    """US-004: blocul tableta (768-1024px) nu rupe lista slim.
    .trimitere-slim e o lista stivuita (o coloana), fara grila 2/rand.
    Regula tableta cardifica listele existente (thead display:none) fara a elimina slim.
    """
    _create_account_user("slim_tab@test.com")
    _login(client, "slim_tab@test.com")
    html = client.get("/?tab=acasa").text

    # Blocul tableta exista (PRD 5.12/5.13 — pastrat)
    assert "@media (min-width:768px) and (max-width:1024px)" in html, \
        "Lipseste blocul @media tableta — regresia 5.12"

    idx_t = html.find("@media (min-width:768px) and (max-width:1024px)")
    tableta = html[idx_t:idx_t + 800]

    # Tableta ascunde thead (card per rand, o coloana) — lista slim e deja o coloana
    assert "thead" in tableta, \
        "Blocul tableta nu contine reguli pentru thead"

    # Lista slim (ul.lista-trimiteri-slim) e o coloana prin constructie (flex-direction:column
    # implicit pe ul); nu trebuie repeat(2) in CSS.
    assert "repeat(2" not in html, \
        "CSS contine repeat(2 — listele NU trebuie sa fie 2/rand pana la 1024px"


# ============================================================
# PRD 5.15 US-008: regresie componente slim + fara overflow orizontal
# ============================================================


def test_slim_list_fara_overflow_orizontal_css(client):
    """US-008: lista slim (.trimitere-slim) nu forteaza overflow orizontal pe 390px / 1280px.

    Verifica la nivel CSS / markup (nu browser): display:flex garanteaza adaptarea
    naturala la latimea containerului; niciun min-width fix mai mare de 390px pe elementele
    din blocul SENTINEL-COMPONENTE-SLIM (ar depasi viewport-ul mobil de 390px).
    Ancorare pe SENTINEL-COMPONENTE-SLIM — nu pe felii fixe din CSS global.
    """
    _create_account_user("noovf_slim@test.com")
    _login(client, "noovf_slim@test.com")
    html = client.get("/?tab=acasa").text

    bloc = _bloc_componente_slim(html)

    # display:flex pe .trimitere-slim asigura adaptarea la latimea oricarui viewport
    assert "display:flex" in bloc, (
        ".trimitere-slim nu are display:flex in SENTINEL-COMPONENTE-SLIM — "
        "layout nu se adapteaza la viewport; poate cauza overflow orizontal."
    )

    # nicio valoare min-width > 390 in blocul slim (ar depasi viewport-ul mobil 390px)
    min_widths = re.findall(r'min-width:(\d+)px', bloc)
    for w in min_widths:
        assert int(w) <= 390, (
            f"min-width:{w}px in SENTINEL-COMPONENTE-SLIM poate cauza overflow orizontal "
            f"pe viewport de 390px (mobil). Verificati daca e pe .trimitere-slim."
        )


def test_strip_sanatate_fara_hex_hardcodat():
    """US-008: _status.html (strip sanatate D6 + contoare-contor) nu contine hex literal de culoare.

    Garanteaza ca strip-ul adapteaza la temele luminoase (hartie/light) si intunecate (grafit/dark)
    exclusiv prin var(--token), NU prin valori hex hardcodate care ar ramane aceleasi
    indiferent de tema selectata.

    Complement la test_fara_hex_in_componente_noi (care verifica SENTINEL din base.html).
    Strip sanatate traieste in _status.html, deci e verificat separat.
    """
    from pathlib import Path
    templates_dir = Path(__file__).parent.parent / "app" / "web" / "templates"
    content = (templates_dir / "_status.html").read_text(encoding="utf-8")

    # Hex literals in context de proprietate CSS de culoare (color/background/border + inline style)
    hex_in_culori = re.findall(
        r'(?:color|background|border)\s*[:=][^;{}\n"\']*?(#[0-9a-fA-F]{6,8})\b',
        content,
    )
    assert not hex_in_culori, (
        f"Hex literal de culoare in _status.html — strip sanatate va arata gresit pe "
        f"tema hartie (luminoasa) / light. Folositi var(--token). Gasite: {hex_in_culori}"
    )


# ============================================================
# PRD 5.16 US-010: Titlu ROA AUTOPASS + account_name in antet
# ============================================================

def test_titlu_romfast_autopass(client):
    """US-010 (PRD 5.16): titlul din antet si tag-ul <title> sunt 'ROA AUTOPASS',
    nu 'Gateway RAR AUTOPASS'."""
    _create_account_user("titlutest@test.com", name="Service Titlu")
    _login(client, "titlutest@test.com")
    html = client.get("/?tab=acasa").text

    assert "ROA AUTOPASS" in html, \
        "Titlul 'ROA AUTOPASS' lipseste din antet (US-010 PRD 5.16)"
    assert "Gateway RAR AUTOPASS" not in html, \
        "Titlul vechi 'Gateway RAR AUTOPASS' inca prezent — inlocuieste cu 'ROA AUTOPASS'"


def test_header_arata_nume_service_logat(client):
    """US-010 (PRD 5.16): cand utilizatorul e logat, antetul afiseaza numele service-ului
    (accounts.name) ca sub-titlu cu clasa .h-sub."""
    _create_account_user("numeservice@test.com", name="Service Auto Cluj SRL")
    _login(client, "numeservice@test.com")
    html = client.get("/?tab=acasa").text

    assert "Service Auto Cluj SRL" in html, \
        "Numele service-ului nu apare in antet (US-010 PRD 5.16) — verifica .h-sub"
    assert "h-sub" in html, \
        "Clasa .h-sub lipseste din antet (US-010 PRD 5.16) — sub-titlul account_name lipseste"


def test_login_branded_nu_schelet(client):
    """/login e branduit (card .login-shell, titlu 'ROA AUTOPASS') cu formular POST /login
    (CSRF intact), NU schelet gol. Loginul a fost simplificat la o coloana (commit
    'simplifica login') — nu mai cerem coloana de brand .login-aside."""
    resp = client.get("/login")
    assert resp.status_code == 200
    html = resp.text

    assert "login-shell" in html, \
        "Clasa .login-shell lipseste din /login — cardul de login branduit lipseste"
    assert "ROA AUTOPASS" in html, \
        "Titlul 'ROA AUTOPASS' lipseste din /login"
    # Formular intact: POST /login cu csrf_token
    assert 'action="/login"' in html, "Actiunea formularului /login s-a schimbat — CSRF route invalida"
    assert 'name="csrf_token"' in html, "csrf_token lipseste din formular — securitate compromisa"
    assert 'name="parola"' in html, "Campul 'parola' lipseste din formular"


# ============================================================
# PRD 5.17 T7 (US-007): landing copy — limita 60 + trial Pro
# PRD 5.16 US-012: Autentificare → /login
# ============================================================

def _citeste_landing() -> str:
    """Returneaza continutul landing.html (template static; variabilele Jinja2 nu
    afecteaza copy-ul de limita/plan/buton verificat mai jos)."""
    from pathlib import Path
    p = Path(__file__).parent.parent / "app" / "web" / "templates" / "landing.html"
    assert p.exists(), f"landing.html negasit la {p}"
    return p.read_text(encoding="utf-8")


def test_landing_limita_60():
    """5.17 T7 (US-007): limita planului Gratuit este 60/luna in landing, nu 100.
    Terminologia landing-ului a trecut de la 'prestații' la 'trimiteri' (rework landing
    ff9d0f4): copy-ul de limita e acum '60 de trimiteri/lună' (hero + cardul Gratuit)."""
    html = _citeste_landing()

    assert "100 de prestații" not in html, \
        "'100 de prestații' inca prezent in landing — limita trebuie sa fie 60 (5.17 T7)"
    assert "100 de trimiteri/lună" not in html, \
        "'100 de trimiteri/lună' prezent — limita planului Gratuit trebuie sa fie 60 (5.17 T7)"
    assert "60 de trimiteri" in html, \
        "'60 de trimiteri' lipseste din landing — verifica hero badge si cardul Gratuit (5.17 T7)"


def test_landing_trial_pro_nu_premium():
    """5.17 T7 (US-007): trial-ul de 30 de zile este pe Pro, NU pe Premium.
    Verifica sectiunea PRICING (subtitle) si sectiunea AUTH (lista beneficii)."""
    html = _citeste_landing()

    assert "Pro gratuit 30 de zile" in html, \
        "'Pro gratuit 30 de zile' lipseste din landing — verifica sectiunile PRICING + AUTH (5.17 T7)"
    assert "Premium gratuit 30 de zile" not in html, \
        "'Premium gratuit 30 de zile' inca in landing — trial-ul e pe Pro, nu Premium (5.17 T7)"


def test_landing_autentificare_link_login():
    """5.16 US-012: butonul 'Autentificare' din header-ul landing este un link <a href='/login'>
    cu clasa auth-login-link, NU un buton care deschide modalul de login.
    CSS-ul responsive (.lp-hactions) trebuie sa foloseasca noul selector, nu cel vechi."""
    html = _citeste_landing()

    # Link real catre /login in header (cu clasa de identificare)
    assert 'href="/login"' in html, \
        "href='/login' lipseste din landing — 'Autentificare' din header trebuie sa fie link (5.16 US-012)"
    assert "auth-login-link" in html, \
        "Clasa auth-login-link lipseste — header 'Autentificare' nu a fost convertit la <a> (5.16 US-012)"
    # CSS-ul responsive ascunde linkul pe <430px prin noul selector (nu cel vechi cu atribute)
    assert "a.auth-login-link" in html, \
        "Selectorul CSS 'a.auth-login-link' lipseste — CSS responsive neactualizat (5.16 US-012)"
    # Selectorul CSS vechi cu [data-act="auth"][data-tab="login"] nu mai exista in CSS
    assert '[data-act="auth"][data-tab="login"]' not in html, \
        "Selectorul CSS vechi [data-act='auth'][data-tab='login'] inca prezent (5.16 US-012)"


def test_contoare_desktop_ascunse_pe_mobil_fara_inline_display():
    """US-002 (PRD 5.16): pe <=560px se vad DOAR contoarele compacte, nu si cele 5 carduri mari.

    Regresie prinsa la VERIFY E2E (390px): un inline `style="display:flex"` pe `.contoare-desktop`
    batea regula `@media (max-width:560px) { .contoare-desktop { display:none } }` (inline > stylesheet)
    -> contoare DUPLICATE pe mobil. Lock: `display:flex` sta in CSS (nu inline pe element), iar media
    query-ul ascunde cardurile mari pe mobil.
    """
    from pathlib import Path

    tdir = Path(__file__).parent.parent / "app" / "web" / "templates"
    base = (tdir / "base.html").read_text(encoding="utf-8")
    status = (tdir / "_status.html").read_text(encoding="utf-8")

    # _status.html: containerul de carduri NU mai are inline display (altfel bate media query-ul).
    assert 'class="contoare-desktop" style="display:flex' not in status, (
        "containerul .contoare-desktop are inline display:flex -> media query-ul nu-l mai poate ascunde pe mobil"
    )
    # base.html: regula CSS default (display:flex) + ascunderea pe <=560px.
    assert re.search(r"\.contoare-desktop\s*\{[^}]*display:\s*flex", base), (
        "lipseste regula CSS .contoare-desktop { display:flex } in base.html"
    )
    assert re.search(r"\.contoare-desktop\s*\{[^}]*display:\s*none", base), (
        "lipseste ascunderea .contoare-desktop { display:none } (media <=560px) in base.html"
    )
