"""Teste US-007 (PRD 5.11) — Post-commit: lista Trimiteri apare + refresh auto.

TDD RED: testele sunt scrise inainte de implementare.
Verifica:
  1. Raspunsul confirma emite header HX-Trigger: trimiteriChanged.
  2. Raspunsul confirma include OOB swap al #trimiteri-section cu submissions-wrap.
  3. Prima vizita (first-run, zero trimiteri) randeaza placeholder #trimiteri-section in DOM.
  4. Mesajul de succes este onest: contine numarul de prezentari puse in coada.
"""

from __future__ import annotations

import csv
import io
import os
import re
import tempfile

import pytest
from fastapi.testclient import TestClient


# --------------------------------------------------------------------------- #
# Fixture client cu DB izolat                                                   #
# --------------------------------------------------------------------------- #

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


# --------------------------------------------------------------------------- #
# Utilitare                                                                     #
# --------------------------------------------------------------------------- #

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


def _seed_nomenclator(client: TestClient, cod_prestatie: str = "R-FRANE", cod_op: str = "OP-FRANE") -> None:
    """Semeaza nomenclatorul si o mapare operatie->cod RAR (pentru randuri ok)."""
    from app.db import get_connection
    conn = get_connection()
    try:
        conn.execute(
            "INSERT OR REPLACE INTO nomenclator_rar (cod_prestatie, nume_prestatie) VALUES (?,?)",
            (cod_prestatie, "Reparatie frane"),
        )
        conn.execute(
            "INSERT OR IGNORE INTO operations_mapping "
            "(account_id, cod_op_service, cod_prestatie, auto_send) VALUES (1,?,?,1)",
            (cod_op, cod_prestatie),
        )
        conn.commit()
    finally:
        conn.close()


def _upload_preview_si_commit(client: TestClient, rows: list[dict]) -> tuple[int, object]:
    """Parcurge fluxul web complet: upload -> mapare coloane -> confirma.

    Intoarce (import_id, raspuns_confirma).
    Presupune: nomenclatorul si maparea operatiei sunt deja semanate.
    """
    data = _csv_bytes(rows)
    r = client.post(
        "/_import/upload",
        files={"file": ("test.csv", io.BytesIO(data), "text/csv")},
    )
    assert r.status_code == 200, r.text

    m = re.search(r"/_import/(\d+)/", r.text)
    assert m, f"import_id negasit in raspunsul de upload: {r.text[:400]}"
    iid = int(m.group(1))

    if f"/_import/{iid}/mapare-coloane" in r.text:
        r2 = client.post(
            f"/_import/{iid}/mapare-coloane",
            data={
                "colname": ["VIN", "Nr", "Data", "KM", "Operatie"],
                "canon":   ["vin", "nr_inmatriculare", "data_prestatie", "odometru_final", "operatie"],
                "format_data": "YYYY-MM-DD",
            },
        )
        assert r2.status_code == 200, r2.text

    # GET preview pentru a afla n_ok — citit din atributul value al inputului #n-confirmat.
    # (Regex generic pe "ok" ar putea prinde valori din CSS like min-height:36px -> 36.)
    rp = client.get(f"/_import/{iid}/preview")
    assert rp.status_code == 200, rp.text
    m_ok = re.search(r'id="n-confirmat"[^>]*?value="(\d+)"', rp.text)
    n_ok = int(m_ok.group(1)) if m_ok else len(rows)

    r_conf = client.post(
        f"/_import/{iid}/confirma",
        data={
            "csrf_token": "",
            "n_confirmat": str(n_ok),
            "confirmed_by": "test@us007.ro",
        },
    )
    return iid, r_conf


# Date fixture: un singur rand ok
_ROWS_OK = [
    {
        "VIN": "WVWZZZ1KZAW007001",
        "Nr": "B007TST",
        "Data": "2026-06-15",
        "KM": "77000",
        "Operatie": "OP-FRANE",
    },
]


# --------------------------------------------------------------------------- #
# Teste RED → GREEN                                                              #
# --------------------------------------------------------------------------- #

def test_confirma_emite_hx_trigger(client):
    """Raspunsul confirma include header HX-Trigger: trimiteriChanged.

    Necesar pentru ca HTMX sa emita evenimentul pe <body>, pe care alte
    elemente abonate (submissions-wrap) sa il prinda si sa se reimprospateze.
    """
    _seed_nomenclator(client)
    _, r = _upload_preview_si_commit(client, _ROWS_OK)

    assert r.status_code == 200, r.text
    hx_trigger = r.headers.get("HX-Trigger", "")
    assert "trimiteriChanged" in hx_trigger, (
        f"Header HX-Trigger lipseste sau nu contine 'trimiteriChanged'. "
        f"Primit: {hx_trigger!r}"
    )


def test_confirma_oob_trimiteri_section(client):
    """Raspunsul confirma include OOB swap al #trimiteri-section.

    Elementul <section id='trimiteri-section'> apare in raspuns cu atributul
    hx-swap-oob, astfel incat HTMX sa il injecteze in DOM fara reload complet.
    La first-run, #trimiteri-section era absent din DOM (zero trimiteri anterior);
    OOB-ul il populeaza si-l face vizibil.
    """
    _seed_nomenclator(client)
    _, r = _upload_preview_si_commit(client, _ROWS_OK)

    assert r.status_code == 200, r.text
    html = r.text

    assert "hx-swap-oob" in html, (
        "Atributul hx-swap-oob lipseste din raspuns — OOB swap nu e emis"
    )
    assert 'id="trimiteri-section"' in html or "id='trimiteri-section'" in html, (
        "#trimiteri-section lipseste din raspuns OOB"
    )
    assert "submissions-wrap" in html, (
        "#submissions-wrap lipseste din OOB — lista Trimiteri nu va aparea"
    )


def test_confirma_mesaj_succes_onest(client):
    """Mesajul de succes mentioneaza numarul de prezentari puse in coada.

    'Onest' inseamna ca mesajul reflecta exact numarul de randuri enqueue-uite,
    nu o formulare vaga (ex. 'succes'). Permite utilizatorului sa verifice.
    """
    _seed_nomenclator(client)
    _, r = _upload_preview_si_commit(client, _ROWS_OK)

    assert r.status_code == 200, r.text
    html = r.text

    # Mesajul trebuie sa contina cel putin numarul '1' (un rand ok in fixture)
    # si sa indice ca randurile sunt "in coada" sau "prezentari".
    assert re.search(r"\b1\b", html), "Numarul de prezentari (1) lipseste din raspuns"
    # Cel putin unul din cuvintele cheie care indica succes de incarcare in coada
    assert any(kw in html.lower() for kw in ("coada", "prezenta", "trimiter")), (
        "Mesajul de succes nu contine cuvinte cheie despre enqueue (coada/prezentari/trimiteri)"
    )


def test_commit_actualizeaza_status_bar(client):
    """Raspunsul confirma include OOB swap al #status-bar cu trigger trimiteriChanged.

    Verifica doua lucruri:
      1. #status-bar apare in raspunsul confirma cu hx-swap-oob (actualizare imediata
         a contorului 'In asteptare' fara a astepta poll-ul de 15s).
      2. Markup-ul #status-bar contine 'trimiteriChanged' in hx-trigger, deci la
         urmatoarele evenimente trimiteriChanged (mapeaza, corectie etc.) bara se
         re-incarca imediat, nu abia la 15s.
    """
    _seed_nomenclator(client)
    _, r = _upload_preview_si_commit(client, _ROWS_OK)

    assert r.status_code == 200, r.text
    html = r.text

    assert 'id="status-bar"' in html or "id='status-bar'" in html, (
        "#status-bar lipseste din raspuns — OOB swap nu va actualiza contoarele"
    )
    assert "hx-swap-oob" in html, (
        "Atributul hx-swap-oob lipseste — cel putin un OOB swap trebuie emis"
    )
    assert "trimiteriChanged" in html, (
        "trimiteriChanged lipseste din raspuns — #status-bar nu va reactiona "
        "la eveniment si se va actualiza abia la urmatorul poll de 15s"
    )

    # Verifica direct ca fragmentul /_fragments/status contine trigger-ul in markup.
    r_status = client.get("/_fragments/status")
    assert r_status.status_code == 200, r_status.text
    assert "trimiteriChanged" in r_status.text, (
        "/_fragments/status nu contine 'trimiteriChanged' in hx-trigger — "
        "bara nu va reactiona la evenimentul emis de confirma"
    )


def test_acasa_placeholder_trimiteri_first_run(client):
    """GET / (zero trimiteri) randeaza elementul #trimiteri-section in DOM.

    La first-run (niciun submission anterior), #trimiteri-section trebuia sa
    existe in HTML ca placeholder gol/ascuns, astfel incat OOB swap-ul de la
    confirma sa aiba tinta valida. Fara placeholder, HTMX ignora silentios OOB-ul
    si lista Trimiteri nu apare dupa commit.
    """
    r = client.get("/")
    assert r.status_code == 200, r.text
    html = r.text

    assert 'id="trimiteri-section"' in html or "id='trimiteri-section'" in html, (
        "#trimiteri-section lipseste din DOM la first-run — "
        "OOB swap-ul de la confirma nu va gasi tinta si lista nu va aparea"
    )
