"""
Tests for Brynq Human Authentication — signup, login, JWT, sessions.
"""

import os
import time
import pytest


@pytest.fixture(autouse=True)
def _isolate(tmp_path, monkeypatch):
    auth_dir = tmp_path / "auth"
    auth_dir.mkdir(parents=True, exist_ok=True)

    import brynq.auth as a
    monkeypatch.setattr(a, "AUTH_DIR", auth_dir)
    monkeypatch.setattr(a, "DB_PATH", auth_dir / "users.db")
    monkeypatch.setattr(a, "JWT_SECRET", "")  # Force regeneration per test


@pytest.fixture
def auth(tmp_path):
    from brynq.auth import AuthManager
    return AuthManager(db_path=str(tmp_path / "auth" / "users.db"))


class TestPasswordHashing:
    def test_hash_and_verify(self):
        from brynq.auth import _hash_password, _verify_password
        h, s = _hash_password("mysecurepassword")
        assert _verify_password("mysecurepassword", h, s) is True

    def test_wrong_password(self):
        from brynq.auth import _hash_password, _verify_password
        h, s = _hash_password("correct")
        assert _verify_password("wrong", h, s) is False

    def test_different_salts(self):
        from brynq.auth import _hash_password
        h1, s1 = _hash_password("same")
        h2, s2 = _hash_password("same")
        assert s1 != s2  # Random salts


class TestJWT:
    def test_create_and_verify(self):
        from brynq.auth import _create_jwt, _verify_jwt
        payload = {"sub": "user-1", "exp": time.time() + 3600}
        token = _create_jwt(payload, "secret123")
        result = _verify_jwt(token, "secret123")
        assert result is not None
        assert result["sub"] == "user-1"

    def test_expired_token(self):
        from brynq.auth import _create_jwt, _verify_jwt
        payload = {"sub": "user-1", "exp": time.time() - 1}
        token = _create_jwt(payload, "secret")
        assert _verify_jwt(token, "secret") is None

    def test_wrong_secret(self):
        from brynq.auth import _create_jwt, _verify_jwt
        payload = {"sub": "user-1", "exp": time.time() + 3600}
        token = _create_jwt(payload, "secret1")
        assert _verify_jwt(token, "secret2") is None

    def test_tampered_token(self):
        from brynq.auth import _create_jwt, _verify_jwt
        payload = {"sub": "user-1", "exp": time.time() + 3600}
        token = _create_jwt(payload, "secret")
        tampered = token[:-5] + "xxxxx"
        assert _verify_jwt(tampered, "secret") is None

    def test_malformed_token(self):
        from brynq.auth import _verify_jwt
        assert _verify_jwt("not.a.jwt.at.all", "secret") is None
        assert _verify_jwt("", "secret") is None


class TestSignup:
    def test_signup_success(self, auth):
        result = auth.signup("test@brynq.ai", "Test User", "password123")
        assert result["ok"] is True
        assert result["email"] == "test@brynq.ai"
        assert result["user_id"]

    def test_signup_duplicate_email(self, auth):
        auth.signup("dup@brynq.ai", "User1", "password123")
        result = auth.signup("dup@brynq.ai", "User2", "password456")
        assert result["ok"] is False
        assert "already registered" in result["error"]

    def test_signup_invalid_email(self, auth):
        result = auth.signup("not-an-email", "Name", "password123")
        assert result["ok"] is False

    def test_signup_short_password(self, auth):
        result = auth.signup("test@x.com", "Name", "short")
        assert result["ok"] is False
        assert "8 characters" in result["error"]

    def test_signup_empty_name(self, auth):
        result = auth.signup("test@x.com", "  ", "password123")
        assert result["ok"] is False

    def test_signup_normalizes_email(self, auth):
        auth.signup("Test@Brynq.AI", "User", "password123")
        result = auth.signup("test@brynq.ai", "Other", "password456")
        assert result["ok"] is False  # Duplicate after normalization


class TestLogin:
    def test_login_success(self, auth):
        auth.signup("login@test.com", "Login User", "securepass1")
        result = auth.login("login@test.com", "securepass1")
        assert result["ok"] is True
        assert "access_token" in result
        assert result["user"]["email"] == "login@test.com"

    def test_login_wrong_password(self, auth):
        auth.signup("wrong@test.com", "User", "correctpass")
        result = auth.login("wrong@test.com", "wrongpass")
        assert result["ok"] is False

    def test_login_nonexistent_user(self, auth):
        result = auth.login("ghost@test.com", "password")
        assert result["ok"] is False

    def test_admin_login(self, auth):
        result = auth.login("grewal@duck.com", "admin")
        assert result["ok"] is True
        assert result["user"]["role"] == "ceo"


class TestTokenVerification:
    def test_verify_valid_token(self, auth):
        auth.signup("verify@test.com", "V", "password123")
        login = auth.login("verify@test.com", "password123")
        identity = auth.verify_token(login["access_token"])
        assert identity is not None
        assert identity["email"] == "verify@test.com"

    def test_verify_invalid_token(self, auth):
        assert auth.verify_token("garbage.token.here") is None

    def test_logout_revokes_token(self, auth):
        auth.signup("logout@test.com", "L", "password123")
        login = auth.login("logout@test.com", "password123")
        token = login["access_token"]
        assert auth.verify_token(token) is not None
        auth.logout(token)
        assert auth.verify_token(token) is None


class TestUserManagement:
    def test_get_user(self, auth):
        result = auth.signup("getme@test.com", "Get Me", "password123")
        user = auth.get_user(result["user_id"])
        assert user is not None
        assert user["email"] == "getme@test.com"

    def test_get_nonexistent(self, auth):
        assert auth.get_user("nope") is None

    def test_list_users(self, auth):
        auth.signup("a@test.com", "A", "password123")
        auth.signup("b@test.com", "B", "password123")
        users = auth.list_users()
        emails = [u["email"] for u in users]
        assert "a@test.com" in emails
        assert "b@test.com" in emails

    def test_update_tier(self, auth):
        result = auth.signup("tier@test.com", "T", "password123")
        assert auth.update_tier(result["user_id"], "pro") is True
        user = auth.get_user(result["user_id"])
        assert user["tier"] == "pro"

    def test_update_invalid_tier(self, auth):
        result = auth.signup("bad@test.com", "B", "password123")
        assert auth.update_tier(result["user_id"], "platinum") is False
