"""Nexus — Step 0: Infrastructure Tests (R8: Redis + WebSocket + Session leak)

Tests:
- R8: Redis BlockingConnectionPool configuration
- R8: WebSocket ConnectionManager
- D7: DB Session middleware
"""

import pytest
import asyncio
from unittest.mock import AsyncMock, MagicMock, patch


class TestRedisClient:
    """R8: Redis client tests"""

    def test_get_redis_raises_before_init(self):
        """get_redis() raises RuntimeError if called before init_redis()"""
        from server.infrastructure.redis.client import get_redis
        # Reset module state
        import server.infrastructure.redis.client as redis_mod
        redis_mod._redis = None

        with pytest.raises(RuntimeError, match="Redis not initialized"):
            get_redis()

    def test_get_redis_sync_returns_none_before_init(self):
        """get_redis_sync() returns None if Redis not initialized"""
        import server.infrastructure.redis.client as redis_mod
        redis_mod._redis = None

        from server.infrastructure.redis.client import get_redis_sync
        assert get_redis_sync() is None

    @pytest.mark.asyncio
    async def test_init_redis_exits_on_empty_url(self):
        """init_redis() raises SystemExit if REDIS_URL is empty"""
        from server.infrastructure.redis.client import init_redis
        import server.config as config_mod

        original_url = config_mod.settings.REDIS_URL
        config_mod.settings.REDIS_URL = ""

        with pytest.raises(SystemExit, match="REDIS_URL must be set"):
            await init_redis(MagicMock())

        config_mod.settings.REDIS_URL = original_url


class TestWebSocketManager:
    """R8: WebSocket ConnectionManager tests"""

    def test_manager_initial_state(self):
        """ConnectionManager starts with zero connections"""
        from server.api.websocket import ConnectionManager
        manager = ConnectionManager()
        assert manager.client_count == 0

    def test_manager_disconnect_nonexistent(self):
        """Disconnecting a non-existent client is a no-op"""
        from server.api.websocket import ConnectionManager
        manager = ConnectionManager()
        manager.disconnect("nonexistent")  # Should not raise
        assert manager.client_count == 0

    def test_manager_update_pong(self):
        """update_pong updates last_pong_time for existing connection"""
        from server.api.websocket import ConnectionManager
        import time
        manager = ConnectionManager()
        ws_mock = MagicMock()
        manager._connections["test_client"] = {
            "websocket": ws_mock,
            "last_pong": time.monotonic() - 10,
        }

        before = manager._connections["test_client"]["last_pong"]
        manager.update_pong("test_client")
        after = manager._connections["test_client"]["last_pong"]

        assert after > before


class TestDbSessionMiddleware:
    """D7: DB Session middleware tests"""

    @pytest.mark.asyncio
    async def test_middleware_skips_non_api_routes(self):
        """Middleware skips non-API routes (WebSocket, health, static)"""
        from server.main import DbSessionMiddleware
        from starlette.responses import Response

        middleware = DbSessionMiddleware(app=MagicMock())

        # Create a proper ASGI scope for a non-API path
        scope = {
            "type": "http",
            "method": "GET",
            "path": "/favicon.ico",
            "query_string": b"",
            "headers": [],
        }

        # Use a real Request object with a proper scope
        from starlette.requests import Request
        request = Request(scope)

        call_next = AsyncMock(return_value=Response())
        response = await middleware.dispatch(request, call_next)
        assert call_next.called


class TestJWTAuth:
    """A5: JWT authentication tests"""

    def test_public_path_detection(self):
        """Public paths are correctly identified"""
        from server.api.auth_jwt import _is_public_path

        assert _is_public_path("/api/auth/login") is True
        assert _is_public_path("/api/auth/refresh") is True
        assert _is_public_path("/api/agent/heartbeat") is True
        assert _is_public_path("/health") is True
        assert _is_public_path("/ws/alerts") is True

    def test_protected_path_detection(self):
        """Protected paths are correctly identified"""
        from server.api.auth_jwt import _is_public_path

        assert _is_public_path("/api/servers/") is False
        assert _is_public_path("/api/settings/") is False
        assert _is_public_path("/api/sync/files") is False
        assert _is_public_path("/api/assets/platforms") is False
        assert _is_public_path("/api/auth/me") is False
        assert _is_public_path("/api/auth/totp/setup") is False


class TestAsyncSSHPool:
    """W7: asyncssh pool tests"""

    def test_pool_initial_state(self):
        """Pool starts with zero connections"""
        from server.infrastructure.ssh.asyncssh_pool import AsyncSSHPool
        pool = AsyncSSHPool()
        assert pool.stats == {"total": 0, "active": 0, "idle": 0}

    def test_pool_config(self):
        """Pool configuration is reasonable"""
        from server.infrastructure.ssh.asyncssh_pool import AsyncSSHPool
        pool = AsyncSSHPool()
        assert pool.MAX_CONNECTIONS == 100
        assert pool.IDLE_TIMEOUT == 300
        assert pool.CONNECT_TIMEOUT == 10