"""Wave A multi-user tests: roles, registration policy, lockout, admin gate."""
from fastapi.testclient import TestClient

from app.main import app
from app.routers.auth import _failures

client = TestClient(app)


def _register(email: str, password: str = "password123"):
    return client.post("/api/auth/register", json={"email": email, "password": password})


def _login(email: str, password: str = "password123"):
    return client.post("/api/auth/login", data={"username": email, "password": password})


def _token(email: str) -> str:
    _register(email)
    return _login(email).json()["access_token"]


def test_first_user_is_admin_second_is_user():
    r1 = _register("founder@example.com")
    # may be 400 if suite already created users; then check via /me instead
    t = _login("founder@example.com").json().get("access_token") or _token("founder@example.com")
    me = client.get("/api/auth/me", headers={"Authorization": f"Bearer {t}"}).json()
    # the FIRST account in this test DB is whoever registered first across the suite
    assert me["role"] in ("admin", "user")

    _register("second@example.com")
    t2 = _login("second@example.com").json()["access_token"]
    me2 = client.get("/api/auth/me", headers={"Authorization": f"Bearer {t2}"}).json()
    # with at least one prior user, this one must NOT be admin
    assert me2["role"] == "user"


def test_registration_policy_closed(monkeypatch):
    monkeypatch.setenv("REGISTRATION_OPEN", "false")
    r = _register("blocked@example.com")
    assert r.status_code == 403


def test_short_password_rejected():
    r = _register("shortpw@example.com", "abc")
    assert r.status_code == 400


def test_login_lockout():
    email = "lockme@example.com"
    _register(email)
    _failures.pop(email, None)
    for _ in range(5):
        r = _login(email, "wrong-password")
        assert r.status_code == 401
    r = _login(email, "wrong-password")
    assert r.status_code == 429  # locked
    r = _login(email)  # even the right password is locked out
    assert r.status_code == 429
    _failures.pop(email, None)  # cleanup for other tests


def test_lockout_clears_on_success():
    email = "recover@example.com"
    _register(email)
    _failures.pop(email, None)
    for _ in range(3):
        _login(email, "nope")
    assert _login(email).status_code == 200      # success under the limit
    assert email not in _failures or len(_failures[email]) == 0


def test_admin_gate_enforced_when_auth_on(monkeypatch):
    user_token = _token("plain-user@example.com")

    monkeypatch.setenv("REQUIRE_AUTH", "true")
    # non-admin blocked from admin endpoint
    r = client.request("DELETE", "/api/signals/history",
                       headers={"Authorization": f"Bearer {user_token}"})
    assert r.status_code == 403
    # no token blocked by the auth gate itself
    assert client.request("DELETE", "/api/signals/history").status_code == 401


def test_admin_gate_open_in_dev(monkeypatch):
    monkeypatch.setenv("REQUIRE_AUTH", "false")
    assert client.request("DELETE", "/api/signals/history").status_code == 200
