import pytest
from httpx import AsyncClient

from src.services.auth.security import create_access_token, hash_password, verify_password
from src.domain.entities import UserRole


class TestPasswordHashing:
    def test_hash_and_verify(self):
        password = "SecurePass123!"
        hashed = hash_password(password)
        assert hashed != password
        assert verify_password(password, hashed)

    def test_verify_wrong_password(self):
        hashed = hash_password("CorrectPass123!")
        assert not verify_password("WrongPass123!", hashed)


class TestJWT:
    def test_create_access_token(self):
        token = create_access_token(
            subject="test-user-id",
            role=UserRole.ADMIN,
            scopes=["*"],
        )
        assert isinstance(token, str)
        assert len(token) > 0

    def test_decode_access_token(self):
        from src.services.auth.security import decode_token, validate_token_type, TokenData

        token = create_access_token(
            subject="test-user-id",
            role=UserRole.VIEWER,
            scopes=["pharmacies:read"],
        )
        payload = decode_token(token)
        assert payload["sub"] == "test-user-id"
        assert payload["role"] == "viewer"
        assert payload["type"] == "access"

        validated = validate_token_type(payload, "access")
        assert validated is not None

    def test_invalid_token_raises(self):
        from src.core.exceptions import UnauthorizedException
        from src.services.auth.security import decode_token

        with pytest.raises(UnauthorizedException):
            decode_token("invalid.token.here")


class TestRBAC:
    def test_superadmin_can_access_all(self):
        from src.services.auth.security import RBACGuard, TokenData

        token_data = TokenData({"sub": "1", "role": "superadmin", "scopes": ["*"], "type": "access", "iss": "pip-platform", "jti": "test"})
        assert RBACGuard.has_role(token_data, UserRole.ADMIN)
        assert RBACGuard.has_role(token_data, UserRole.VIEWER)
        assert RBACGuard.has_role(token_data, UserRole.OPERATOR)

    def test_viewer_cannot_access_admin(self):
        from src.services.auth.security import RBACGuard, TokenData

        token_data = TokenData({"sub": "2", "role": "viewer", "scopes": [], "type": "access", "iss": "pip-platform", "jti": "test"})
        assert not RBACGuard.has_role(token_data, UserRole.ADMIN)

    def test_wildcard_scope_grants_all(self):
        from src.services.auth.security import RBACGuard, TokenData

        token_data = TokenData({"sub": "1", "role": "superadmin", "scopes": ["*"], "type": "access", "iss": "pip-platform", "jti": "test"})
        assert RBACGuard.has_scope(token_data, "pharmacies:read")
        assert RBACGuard.has_scope(token_data, "pharmacies:write")
        assert RBACGuard.has_scope(token_data, "anything:anything")

    def test_specific_scope(self):
        from src.services.auth.security import RBACGuard, TokenData

        token_data = TokenData({"sub": "2", "role": "viewer", "scopes": ["pharmacies:read"], "type": "access", "iss": "pip-platform", "jti": "test"})
        assert RBACGuard.has_scope(token_data, "pharmacies:read")
        assert not RBACGuard.has_scope(token_data, "pharmacies:write")


class TestAPIKey:
    def test_generate_api_key(self):
        from src.services.auth.security import generate_api_key, verify_api_key

        raw_key, prefix, key_hash = generate_api_key()
        assert len(raw_key) > 20
        assert len(prefix) == 8
        assert raw_key[:8] == prefix
        assert verify_api_key(raw_key, key_hash)
        assert not verify_api_key("wrong-key", key_hash)
