"""Pytest configuration and shared fixtures for all tests."""

import logging
from unittest.mock import Mock

import pytest

from src.server.services.auth_service import auth_service


def pytest_configure(config):
    """Register custom pytest marks."""
    config.addinivalue_line(
        "markers",
        "performance: mark test as a performance test"
    )
    config.addinivalue_line(
        "markers",
        "security: mark test as a security test"
    )
    config.addinivalue_line(
        "markers",
        "requires_clean_auth: test requires auth to NOT be configured initially"
    )


@pytest.fixture(autouse=True)
def reset_auth_and_rate_limits(request):
    """Reset authentication state and rate limits before each test.
    
    This ensures:
    1. Auth service state doesn't leak between tests
    2. Rate limit window is reset for test client IP
    3. Auth is configured with a default test password UNLESS the test
       is marked with @pytest.mark.requires_clean_auth
    Applied to all tests automatically via autouse=True.
    """
    # Reset auth service state
    auth_service._hash = None  # noqa: SLF001
    auth_service._failed.clear()  # noqa: SLF001
    
    # Check if test requires clean (unconfigured) auth state
    requires_clean_auth = request.node.get_closest_marker("requires_clean_auth")
    
    # Configure auth with a default test password so middleware allows requests
    # This prevents the SetupRedirectMiddleware from blocking all test requests
    # Skip this if the test explicitly needs clean auth state
    if not requires_clean_auth:
        try:
            auth_service.setup_master_password("TestPass123!")
        except Exception:
            # If setup fails (e.g., already set), that's okay
            pass
    
    # Reset rate limiter - clear rate limit dict if middleware exists
    # This prevents tests from hitting rate limits on auth endpoints
    try:
        from src.server.fastapi_app import app

        # Try to find and clear the rate limiter dict
        # Middleware is stored in app.middleware_stack or accessible
        # through app's internal structure
        if hasattr(app, 'middleware_stack'):
            # Try to find AuthMiddleware in the stack
            stack = app.middleware_stack
            while stack is not None:
                if hasattr(stack, 'cls'):
                    # This is a middleware class
                    pass
                if hasattr(stack, 'app') and hasattr(
                    stack, '_rate'
                ):  # noqa: SLF001
                    # Found a potential AuthMiddleware instance
                    stack._rate.clear()  # noqa: SLF001
                stack = getattr(stack, 'app', None)
    except BaseException:
        # If middleware reset fails, tests might hit rate limits
        # but we continue anyway - they're not critical
        pass
    
    
    yield
    
    # Clean up after test
    auth_service._hash = None  # noqa: SLF001
    auth_service._failed.clear()  # noqa: SLF001


@pytest.fixture(autouse=True)
def reset_service_singletons():
    """Reset all service singletons between tests.
    
    This prevents cross-test pollution from global state in
    dependencies.py (e.g., _series_app, _anime_service, _download_service).
    Also clears any FastAPI dependency overrides set by individual tests.
    Applied to all tests automatically via autouse=True.
    """
    from src.server.utils.dependencies import (
        reset_anime_service,
        reset_download_service,
        reset_series_app,
    )

    # Reset before test
    reset_series_app()
    reset_anime_service()
    reset_download_service()

    yield

    # Reset after test
    reset_series_app()
    reset_anime_service()
    reset_download_service()

    # Clear any dependency overrides
    try:
        from src.server.fastapi_app import app
        app.dependency_overrides.clear()
    except Exception:
        pass


@pytest.fixture(autouse=True)
def mock_series_app_download(monkeypatch):
    """Mock SeriesApp loader download to prevent real downloads in tests.
    
    This fixture automatically mocks all download operations to prevent
    tests from performing real network downloads.
    Applied to all tests automatically via autouse=True.
    """
    # Mock the loader download method
    try:
        from src.core.SeriesApp import SeriesApp

        # Patch the loader.download method for all SeriesApp instances
        original_init = SeriesApp.__init__
        
        def patched_init(self, *args, **kwargs):
            original_init(self, *args, **kwargs)
            # Mock the loader's download method
            if hasattr(self, 'loader'):
                self.loader.download = Mock(return_value=True)
        
        monkeypatch.setattr(SeriesApp, '__init__', patched_init)
        
    except ImportError:
        # If imports fail, tests will continue but may perform downloads
        pass
    
    yield


@pytest.fixture(autouse=True)
def reset_logging_state():
    """Reset logging handlers and propagate flags before and after each test.
    
    Tests that call setup_logging() or logging.config.dictConfig() may leave
    FileHandlers and propagate=False on various loggers. This fixture clears
    handlers and resets propagate for all relevant loggers before/after tests.
    """
    # All loggers that might have handlers or propagate changes from test setup
    logger_names = (
        "aniworld", "uvicorn", "uvicorn.access", "uvicorn.error",
        "watchfiles.main"
    )
    
    def clear_logger_state(logger_name):
        logger = logging.getLogger(logger_name)
        for handler in logger.handlers[:]:
            logger.removeHandler(handler)
            handler.close()
        # Reset propagate to default (True) for child loggers
        # Root logger propagate is always True by default
        if logger_name != "root":
            logger.propagate = True
    
    # Clear state BEFORE test
    for name in logger_names:
        clear_logger_state(name)
    
    yield
    
    # Clear state AFTER test
    for name in logger_names:
        clear_logger_state(name)
    
    # Also clear root handlers
    root = logging.getLogger()
    for handler in root.handlers[:]:
        root.removeHandler(handler)
        handler.close()

