"""
Tests for Clerk authentication module.
"""

import hashlib
import os
from datetime import datetime, timedelta
from unittest.mock import patch

import pytest

# Set test environment variables before imports
os.environ.setdefault("NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY", "pk_test_dGVzdC1kb21haW4uY2xlcmsuYWNjb3VudHMuZGV2JA==")
os.environ.setdefault("CLERK_SECRET_KEY", "sk_test_mock_secret_key")
os.environ.setdefault("SUPABASE_URL", "https://test.supabase.co")
os.environ.setdefault("SUPABASE_ANON_KEY", "test-anon-key")
os.environ.setdefault("SUPABASE_SERVICE_KEY", "test-service-key")
os.environ.setdefault("GATEWAY_DEMO_API_KEY", "gw_demo_key_12345")


class TestClerkConfig:
    """Tests for Clerk configuration."""

    def test_config_from_env(self):
        """Test loading config from environment."""
        from verlex_server.gateway.backend.clerk_auth import ClerkConfig

        config = ClerkConfig.from_env()

        assert config.publishable_key.startswith("pk_test_")
        assert config.secret_key.startswith("sk_test_")
        assert config.api_url == "https://api.clerk.com/v1"

    def test_config_missing_keys(self):
        """Test error when keys are missing."""
        from verlex_server.gateway.backend.clerk_auth import ClerkConfig

        with patch.dict(os.environ, {"NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY": "", "CLERK_SECRET_KEY": ""}):
            with pytest.raises(ValueError, match="Missing Clerk configuration"):
                ClerkConfig.from_env()


class TestAPIKeyManager:
    """Tests for API key management."""

    def test_generate_key(self):
        """Test API key generation."""
        from verlex_server.gateway.backend.clerk_auth import APIKeyManager

        full_key, key_hash = APIKeyManager.generate_key("gw_live_")

        assert full_key.startswith("gw_live_")
        assert len(full_key) > 20
        assert len(key_hash) == 64  # SHA-256 hex digest

        # Verify hash is correct
        expected_hash = hashlib.sha256(full_key.encode()).hexdigest()
        assert key_hash == expected_hash

    def test_generate_test_key(self):
        """Test API key generation with test prefix."""
        from verlex_server.gateway.backend.clerk_auth import APIKeyManager

        full_key, key_hash = APIKeyManager.generate_key("gw_test_")

        assert full_key.startswith("gw_test_")

    def test_hash_key(self):
        """Test API key hashing."""
        from verlex_server.gateway.backend.clerk_auth import APIKeyManager

        key = "gw_live_test_key_12345"
        hash1 = APIKeyManager.hash_key(key)
        hash2 = APIKeyManager.hash_key(key)

        # Same key should produce same hash
        assert hash1 == hash2

        # Different key should produce different hash
        hash3 = APIKeyManager.hash_key("gw_live_different_key")
        assert hash1 != hash3

    def test_get_prefix(self):
        """Test prefix extraction."""
        from verlex_server.gateway.backend.clerk_auth import APIKeyManager

        assert APIKeyManager.get_prefix("gw_live_abc123") == "gw_live_"
        assert APIKeyManager.get_prefix("gw_test_xyz789") == "gw_test_"
        assert APIKeyManager.get_prefix("gw_old_style") == "gw_"
        assert APIKeyManager.get_prefix("invalid_key") == ""


class TestAPIKey:
    """Tests for APIKey dataclass."""

    def test_api_key_is_valid(self):
        """Test API key validity checks."""
        from verlex_server.gateway.backend.clerk_auth import APIKey

        # Active key with no expiration
        key = APIKey(
            id="key_123",
            user_id="user_456",
            key_prefix="gw_live_",
            key_hash="hash123",
            is_active=True,
        )
        assert key.is_valid()

        # Inactive key
        key.is_active = False
        assert not key.is_valid()

        # Expired key
        key.is_active = True
        key.expires_at = datetime.utcnow() - timedelta(hours=1)
        assert not key.is_valid()

        # Not yet expired
        key.expires_at = datetime.utcnow() + timedelta(hours=1)
        assert key.is_valid()

    def test_api_key_has_scope(self):
        """Test scope checking."""
        from verlex_server.gateway.backend.clerk_auth import APIKey

        key = APIKey(
            id="key_123",
            user_id="user_456",
            key_prefix="gw_live_",
            key_hash="hash123",
            scopes=["jobs:read", "jobs:write"],
        )

        assert key.has_scope("jobs:read")
        assert key.has_scope("jobs:write")
        assert not key.has_scope("admin:read")

        # Wildcard scope
        key.scopes = ["*"]
        assert key.has_scope("anything")


class TestAuthenticatedUser:
    """Tests for AuthenticatedUser dataclass."""

    def test_authenticated_user_creation(self):
        """Test creating an authenticated user."""
        from verlex_server.gateway.backend.clerk_auth import AuthenticatedUser

        user = AuthenticatedUser(
            user_id="user_123",
            email="test@example.com",
            tier="pro",
            auth_method="api_key",
            scopes=["jobs:read", "jobs:write"],
        )

        assert user.user_id == "user_123"
        assert user.email == "test@example.com"
        assert user.tier == "pro"
        assert user.auth_method == "api_key"


class TestRateLimiting:
    """Tests for rate limiting in the API."""

    def test_rate_limiter_allows_requests(self):
        """Test that rate limiter allows requests within limit."""
        from verlex_server.gateway.backend.api import RateLimiter

        limiter = RateLimiter()
        # Force in-memory mode for test isolation
        limiter._redis_available = False

        # Should allow first requests
        for _ in range(10):
            assert limiter.is_allowed("test_key", max_requests=20, window_seconds=60)

    def test_rate_limiter_blocks_excess_requests(self):
        """Test that rate limiter blocks requests over limit."""
        from verlex_server.gateway.backend.api import RateLimiter

        limiter = RateLimiter()
        # Force in-memory mode for test isolation
        limiter._redis_available = False

        # Fill up the limit
        for _ in range(5):
            assert limiter.is_allowed("test_key2", max_requests=5, window_seconds=60)

        # Next request should be blocked
        assert not limiter.is_allowed("test_key2", max_requests=5, window_seconds=60)

    def test_rate_limiter_different_keys(self):
        """Test that rate limiter tracks keys separately."""
        from verlex_server.gateway.backend.api import RateLimiter

        limiter = RateLimiter()
        # Force in-memory mode for test isolation
        limiter._redis_available = False

        # Fill up limit for key1
        for _ in range(3):
            limiter.is_allowed("key1", max_requests=3, window_seconds=60)

        # key1 should be blocked, but key2 should be allowed
        assert not limiter.is_allowed("key1", max_requests=3, window_seconds=60)
        assert limiter.is_allowed("key2", max_requests=3, window_seconds=60)


class TestWebhookVerification:
    """Tests for Clerk webhook verification."""

    def test_webhook_signature_verification_no_secret(self):
        """Test that verification passes when no secret is configured."""
        from verlex_server.gateway.backend.webhooks import verify_webhook_signature

        # Should return True (skip verification) when secret is empty
        result = verify_webhook_signature(
            payload=b'{"test": "data"}',
            signature="v1,invalid_signature",
            svix_id="msg_123",
            svix_timestamp="1234567890",
            secret="",
        )

        assert result is True


class TestAPIEndpoints:
    """Integration tests for API endpoints."""

    @pytest.fixture
    def client(self):
        """Create a test client."""
        from fastapi.testclient import TestClient

        from verlex_server.gateway.backend.api import create_app
        from verlex_server.gateway.backend.shared_auth import add_demo_user

        # Add demo users for testing (since env var may be set after module import)
        add_demo_user("gw_demo_key_12345", {
            "user_id": "user_demo",
            "id": "user_demo",
            "email": "demo@gateway.cloud",
            "tier": "free",
            "name": "Demo User",
        })
        add_demo_user("gw_test_mydevkey", {
            "user_id": "user_dev",
            "id": "user_dev",
            "email": "dev@gateway.cloud",
            "tier": "free",
            "name": "Dev User",
        })
        add_demo_user("gw_test_newuser", {
            "user_id": "user_new",
            "id": "user_new",
            "email": "new@gateway.cloud",
            "tier": "free",
            "name": "New User",
        })
        add_demo_user("gw_test_user", {
            "user_id": "user_test",
            "id": "user_test",
            "email": "test@gateway.cloud",
            "tier": "free",
            "name": "Test User",
        })

        app = create_app()
        return TestClient(app)

    def test_health_endpoint(self, client):
        """Test health check endpoint."""
        response = client.get("/health")
        assert response.status_code == 200
        data = response.json()
        assert data["status"] == "healthy"
        assert "timestamp" in data

    def test_validate_auth_with_demo_key(self, client):
        """Test auth validation with demo API key."""
        response = client.post(
            "/v1/auth/validate",
            headers={"Authorization": "Bearer gw_demo_key_12345"},
        )
        assert response.status_code == 200
        data = response.json()
        assert data["valid"] is True
        assert data["user_id"] == "user_demo"

    def test_validate_auth_with_dev_key(self, client):
        """Test auth validation with development API key."""
        response = client.post(
            "/v1/auth/validate",
            headers={"Authorization": "Bearer gw_test_mydevkey"},
        )
        assert response.status_code == 200
        data = response.json()
        assert data["valid"] is True

    def test_validate_auth_invalid_key(self, client):
        """Test auth validation with invalid key."""
        response = client.post(
            "/v1/auth/validate",
            headers={"Authorization": "Bearer invalid_key"},
        )
        assert response.status_code == 401

    def test_validate_auth_missing_header(self, client):
        """Test auth validation without auth header."""
        response = client.post("/v1/auth/validate")
        assert response.status_code == 401  # No auth = unauthorized

    def test_list_jobs_empty(self, client):
        """Test listing jobs when none exist."""
        response = client.get(
            "/v1/jobs",
            headers={"Authorization": "Bearer gw_test_newuser"},
        )
        assert response.status_code == 200
        data = response.json()
        assert data["jobs"] == []

    def test_get_usage_stats(self, client):
        """Test getting usage statistics."""
        response = client.get(
            "/v1/usage",
            headers={"Authorization": "Bearer gw_test_user"},
        )
        assert response.status_code == 200
        data = response.json()
        assert "summary" in data
        assert "by_provider" in data

    def test_webhook_test_endpoint(self, client):
        """Test webhook test endpoint."""
        response = client.get("/webhooks/clerk/test")
        assert response.status_code == 200
        data = response.json()
        assert data["status"] == "ok"


if __name__ == "__main__":
    pytest.main([__file__, "-v"])
