"""US-011 (PRD 5.15): account-scope pe GET-urile de listare web (securitate).

Verifica:
  - /_fragments/submissions: un cont nu vede randurile altui cont
  - /_fragments/trimitere/{id}: 404-before-leak pe id strain
  - /_fragments/nomenclator: necesita autentificare (fragment dashboard, nu endpoint public)
  - /_fragments/trimiteri-versiune: necesita autentificare
  - Unauthenticated access -> redirect 303 pe ORICE fragment cu date

Legatura cu implementare: mecanismul de scope existent (require_login +
account_scope_clause) se reutilizeaza fara logica noua. Nomenclatorul primeste
require_login din US-011 (fragment dashboard, nu endpoint public).
"""

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 _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 negasit in pagina de login"
    resp = client.post("/login", data={"email": email, "parola": password, "csrf_token": m.group(1)})
    assert resp.status_code == 303


def _insert_submission(
    acct: int,
    status: str = "sent",
    vin: str = "WVWZZZ1JZXW000777",
    nr: str = "B777TST",
) -> int:
    from app.db import get_connection
    conn = get_connection()
    try:
        p = {
            "vin": vin,
            "nr_inmatriculare": nr,
            "data_prestatie": "2026-06-18",
            "odometru_final": "50000",
            "prestatii": [{"cod_prestatie": "OE-1", "denumire": "Revizie"}],
        }
        cur = conn.execute(
            "INSERT INTO submissions (idempotency_key, account_id, status, payload_json) "
            "VALUES (?, ?, ?, ?)",
            (f"scope-{acct}-{os.urandom(4).hex()}", acct, status, json.dumps(p)),
        )
        conn.commit()
        rid = cur.lastrowid
        assert rid is not None
        return int(rid)
    finally:
        conn.close()


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


# ---------------------------------------------------------------------------
# Test 1: cross-account isolation pe listare submissions
# ---------------------------------------------------------------------------

def test_get_listare_scoped_cont(client):
    """Un cont NU vede randurile (VIN/PII) ale altui cont in /_fragments/submissions.

    Contul A are un submission cu nr. inmatriculare unic NR_A; contul B nu trebuie
    sa vada NR_A in listarea sa. Verificam izolarea atat prin nr. cat si prin VIN
    (ultimele 6 caractere afisate ca vin_scurt in template).
    """
    NR_A = "BV01SCO"
    NR_B = "BV02SCO"
    VIN_A = "WVWZZZ1JZXW111AAA"  # vin_scurt va fi '...111AAA'
    VIN_B = "WVWZZZ1JZXW222BBB"  # vin_scurt va fi '...222BBB'

    acct_a = _create_account_user("scope-a@test.com", name="ContA")
    acct_b = _create_account_user("scope-b@test.com", name="ContB")
    _insert_submission(acct_a, vin=VIN_A, nr=NR_A)
    _insert_submission(acct_b, vin=VIN_B, nr=NR_B)

    # Login ca cont B
    _login(client, "scope-b@test.com")

    resp = client.get("/_fragments/submissions")
    assert resp.status_code == 200
    html = resp.text

    # Contul B vede propriul nr inmatriculare
    assert NR_B in html, "Contul B ar trebui sa vada propriul nr inmatriculare"
    # Contul B NU vede nr inmatriculare si VIN (vin_scurt) ale contului A
    assert NR_A not in html, "Scurgere cross-account: nr_inmatriculare contului A vizibil contului B"
    assert "111AAA" not in html, "Scurgere cross-account: VIN (vin_scurt) contului A vizibil contului B"


# ---------------------------------------------------------------------------
# Test 2: unauthenticated -> redirect pe listare submissions
# ---------------------------------------------------------------------------

def test_get_listare_neautentificat_redirect_submissions(client):
    """Fara sesiune activa, /_fragments/submissions returneaza 303 (redirect /login)."""
    resp = client.get("/_fragments/submissions")
    assert resp.status_code == 303, (
        f"Asteptat 303 redirect, primit {resp.status_code}. "
        "/_fragments/submissions trebuie sa necesite autentificare."
    )


# ---------------------------------------------------------------------------
# Test 3: 404-before-leak pe detaliu id strain
# ---------------------------------------------------------------------------

def test_get_detaliu_scoped_404(client):
    """Detaliul unui submission apartinand altui cont returneaza 404 (fara leak).

    Acelasi 404 pentru id inexistent = nu confirmam existenta.
    """
    acct_a = _create_account_user("detscope-a@test.com", name="DetA")
    _create_account_user("detscope-b@test.com", name="DetB")
    sid_a = _insert_submission(acct_a, vin="WVWZZZ1JZXWAAA111")

    # Login ca cont B
    _login(client, "detscope-b@test.com")

    # Cerere detaliu pe submission-ul contului A
    resp = client.get(f"/_fragments/trimitere/{sid_a}")
    assert resp.status_code == 404, (
        f"Asteptat 404, primit {resp.status_code}. "
        "Nu trebuie confirmata existenta unui submission al altui cont."
    )

    # Id inexistent -> acelasi 404 (nu confirmam existenta)
    resp2 = client.get("/_fragments/trimitere/999999")
    assert resp2.status_code == 404


# ---------------------------------------------------------------------------
# Test 4: nomenclator necesita autentificare (RED inainte de fix)
# ---------------------------------------------------------------------------

def test_get_nomenclator_neautentificat_redirect(client):
    """/_fragments/nomenclator este un fragment al dashboard-ului autentificat.

    Fara sesiune, trebuie sa returneze 303 redirect la /login.
    RED inainte de fix: in prezent fragmentul nu apeleaza require_login
    si returneaza 200 chiar fara autentificare.
    """
    resp = client.get("/_fragments/nomenclator")
    assert resp.status_code == 303, (
        f"Asteptat 303 redirect pentru fragment dashboard neautentificat, "
        f"primit {resp.status_code}. "
        "/_fragments/nomenclator trebuie sa necesite autentificare."
    )


def test_get_nomenclator_autentificat_ok(client):
    """/_fragments/nomenclator accesibil dupa autentificare."""
    _create_account_user("nom-auth@test.com", name="NomAuth")
    _login(client, "nom-auth@test.com")

    resp = client.get("/_fragments/nomenclator")
    assert resp.status_code == 200


# ---------------------------------------------------------------------------
# Test 5: trimiteri-versiune necesita autentificare
# ---------------------------------------------------------------------------

def test_get_trimiteri_versiune_neautentificat_redirect(client):
    """/_fragments/trimiteri-versiune necesita autentificare (redirect 303 fara sesiune)."""
    resp = client.get("/_fragments/trimiteri-versiune")
    assert resp.status_code == 303


# ---------------------------------------------------------------------------
# Test 6: izolare paginare - filtru nu poate scoate randuri strain
# ---------------------------------------------------------------------------

def test_get_listare_filtru_nu_sparge_scope(client):
    """Filtrele (status, vehicul) nu pot scoate randuri din alt cont.

    Un cont B cu filtru vehicul=VIN_A nu trebuie sa vada niciodata VIN_A.
    """
    VIN_A = "WVWZZZ1JZXW333CCC"

    NR_A = "BV03FLT"
    acct_a = _create_account_user("filtru-a@test.com", name="FiltrA")
    _create_account_user("filtru-b@test.com", name="FiltrB")
    _insert_submission(acct_a, vin=VIN_A, nr=NR_A)

    _login(client, "filtru-b@test.com")

    # Cont B incearca sa filtreze dupa nr inmatriculare al contului A
    resp = client.get(f"/_fragments/submissions?vehicul={NR_A}")
    assert resp.status_code == 200
    assert NR_A not in resp.text, (
        "Filtrul vehicul a scos date din alt cont (scurgere cross-account prin filtru)."
    )
