"""Teste US-001 (PRD 3.3): tabela users + helper-e parole scrypt in app/users.py."""

from __future__ import annotations

import os
import sqlite3
import tempfile

import pytest


@pytest.fixture()
def conn(monkeypatch):
    tmp = tempfile.mkdtemp()
    monkeypatch.setenv("AUTOPASS_DB_PATH", os.path.join(tmp, "test_users.db"))
    from app.config import get_settings
    get_settings.cache_clear()
    from app.db import get_connection, init_db
    init_db()
    c = get_connection()
    yield c
    c.close()
    get_settings.cache_clear()


@pytest.fixture()
def account_id(conn):
    """Cont de test (nu default id=1)."""
    from app.accounts import create_account
    return create_account(conn, "Service Test")


def test_create_user_hash_nu_e_plaintext(conn, account_id):
    """password_hash din DB nu contine parola in clar si nu e egal cu ea."""
    from app.users import create_user
    parola = "parola_sigura_123"
    user_id = create_user(conn, account_id, "test@exemplu.ro", parola)
    assert isinstance(user_id, int)
    row = conn.execute(
        "SELECT password_hash, salt FROM users WHERE id=?", (user_id,)
    ).fetchone()
    assert row is not None
    assert row["password_hash"] != parola
    assert parola not in row["password_hash"]
    assert row["salt"] != parola


def test_verify_parola_corecta_si_gresita(conn, account_id):
    """verify_password intoarce account_id la parola corecta, None la cea gresita."""
    from app.users import create_user, verify_password
    create_user(conn, account_id, "user@exemplu.ro", "parola_corecta_99")
    result_ok = verify_password(conn, "user@exemplu.ro", "parola_corecta_99")
    assert result_ok == account_id
    result_gresit = verify_password(conn, "user@exemplu.ro", "parola_gresita_00")
    assert result_gresit is None
    result_inexistent = verify_password(conn, "inexistent@exemplu.ro", "parola_corecta_99")
    assert result_inexistent is None


def test_email_unic_global(conn, account_id):
    """Al doilea create_user cu acelasi email (diferit doar in case) ridica ValueError."""
    from app.users import create_user
    create_user(conn, account_id, "Unic@exemplu.ro", "parola_unica_001")
    with pytest.raises(ValueError, match="email deja folosit"):
        create_user(conn, account_id, "unic@exemplu.ro", "alta_parola_002")


def test_get_user_by_email(conn, account_id):
    """get_user_by_email intoarce metadate fara password_hash si salt."""
    from app.users import create_user, get_user_by_email
    create_user(conn, account_id, "meta@exemplu.ro", "parola_meta_xyz")
    user = get_user_by_email(conn, "meta@exemplu.ro")
    assert user is not None
    assert user["email"].lower() == "meta@exemplu.ro"
    assert user["account_id"] == account_id
    assert "id" in user
    assert "is_admin" in user
    assert "email_verified" in user
    assert "created_at" in user
    assert "password_hash" not in user
    assert "salt" not in user
    assert get_user_by_email(conn, "inexistent@exemplu.ro") is None


def test_parola_scurta_si_lunga_eroare(conn, account_id):
    """Parola < 10 caractere sau > 128 ridica ValueError (C9 anti-DoS)."""
    from app.users import create_user
    with pytest.raises(ValueError):
        create_user(conn, account_id, "scurta@ex.ro", "scurt")
    with pytest.raises(ValueError):
        create_user(conn, account_id, "lunga@ex.ro", "x" * 129)
    # exact 10 caractere — trebuie sa mearga
    uid = create_user(conn, account_id, "exact10@ex.ro", "a" * 10)
    assert uid > 0
    # exact 128 caractere — trebuie sa mearga
    uid2 = create_user(conn, account_id, "exact128@ex.ro", "b" * 128)
    assert uid2 > 0


def test_verify_honoreaza_scrypt_params(conn, account_id, monkeypatch):
    """verify_password foloseste parametrii din DB (scrypt_params), nu constantele globale.

    Simuleaza migrare cost: hash creat cu n=4 (vechi), constanta _N ridicata la 2**15 (nou).
    verify_password trebuie sa returneze account_id folosind n=4 din DB, nu _N global.
    """
    import hashlib
    import secrets as _secrets

    import app.users as users_mod

    email = "legacy@test.com"
    password = "parolasecreta"

    # Hash cu parametri "vechi" (n=4, rapid pentru teste)
    n_old, r_old, p_old = 4, 8, 1
    salt = _secrets.token_bytes(16)
    pw_hash = hashlib.scrypt(
        password.encode("utf-8"),
        salt=salt,
        n=n_old, r=r_old, p=p_old,
        maxmem=64 * 1024 * 1024,
        dklen=32,
    )
    conn.execute(
        "INSERT INTO users (account_id, email, password_hash, salt, scrypt_params) "
        "VALUES (?, ?, ?, ?, ?)",
        (account_id, email, pw_hash.hex(), salt.hex(), "n4_r8_p1"),
    )

    # Simuleaza cresterea costului: _N e acum mai mare
    monkeypatch.setattr(users_mod, "_N", 2**15)

    # verify_password trebuie sa onoreze n=4 din DB, nu sa foloseasca _N=2**15
    result = users_mod.verify_password(conn, email, password)
    assert result == account_id, "verify_password trebuia sa onoreze scrypt_params din DB"
    assert users_mod.verify_password(conn, email, "gresita123456") is None


def test_verify_params_corupti_return_none(conn, account_id):
    """scrypt_params corupt/necunoscut -> verify returneaza None (no crash)."""
    import hashlib
    import secrets as _secrets

    email = "corupt@test.com"
    password = "parolasecreta"
    salt = _secrets.token_bytes(16)
    pw_hash = hashlib.scrypt(password.encode(), salt=salt, n=4, r=8, p=1,
                             maxmem=64 * 1024 * 1024, dklen=32)
    conn.execute(
        "INSERT INTO users (account_id, email, password_hash, salt, scrypt_params) "
        "VALUES (?, ?, ?, ?, ?)",
        (account_id, email, pw_hash.hex(), salt.hex(), "FORMAT_NECUNOSCUT"),
    )

    from app.users import verify_password
    result = verify_password(conn, email, password)
    assert result is None, "Eticheta corupta trebuia sa returneze None, nu crash"


def test_init_db_pe_db_fara_users_creeaza_tabela(monkeypatch, tmp_path):
    """init_db pe o DB existenta fara tabela users o creeaza fara eroare (migrare idempotenta)."""
    db_path = tmp_path / "veche.db"
    monkeypatch.setenv("AUTOPASS_DB_PATH", str(db_path))
    from app.config import get_settings
    get_settings.cache_clear()

    # Creeaza DB fara tabela users (simuleaza DB veche)
    import sqlite3 as _sq
    conn_raw = _sq.connect(str(db_path))
    conn_raw.execute("PRAGMA journal_mode = WAL")
    conn_raw.execute("PRAGMA foreign_keys = ON")
    conn_raw.execute(
        "CREATE TABLE IF NOT EXISTS accounts "
        "(id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT NOT NULL, "
        "cui TEXT, active INTEGER NOT NULL DEFAULT 1, "
        "rar_creds_enc TEXT, created_at TEXT NOT NULL DEFAULT (datetime('now')))"
    )
    conn_raw.execute("INSERT OR IGNORE INTO accounts (id, name) VALUES (1, 'default')")
    conn_raw.commit()
    conn_raw.close()

    # init_db trebuie sa creeze tabela users fara eroare
    from app.db import init_db
    init_db()

    from app.db import get_connection
    c = get_connection()
    tables = {r[0] for r in c.execute(
        "SELECT name FROM sqlite_master WHERE type='table'"
    ).fetchall()}
    c.close()
    assert "users" in tables
    get_settings.cache_clear()
