"""
Tests for Milestone 2 production hardening features.

Verifies:
1. Security headers middleware
2. Request ID middleware
3. Request timeouts
4. Writable health check
5. Structured logging
"""

from __future__ import annotations

from pathlib import Path

from fastapi.testclient import TestClient


class TestSecurityHeadersMiddleware:
    """Test that security headers are added to responses."""

    def test_x_content_type_options(self, client: TestClient) -> None:
        """Verify X-Content-Type-Options header."""
        response = client.get("/healthz")
        assert response.headers.get("X-Content-Type-Options") == "nosniff"

    def test_x_frame_options(self, client: TestClient) -> None:
        """Verify X-Frame-Options header."""
        response = client.get("/healthz")
        assert response.headers.get("X-Frame-Options") == "DENY"

    def test_x_xss_protection(self, client: TestClient) -> None:
        """Verify X-XSS-Protection header."""
        response = client.get("/healthz")
        assert response.headers.get("X-XSS-Protection") == "1; mode=block"

    def test_content_security_policy(self, client: TestClient) -> None:
        """Verify Content-Security-Policy header."""
        response = client.get("/healthz")
        assert response.headers.get("Content-Security-Policy") == "default-src 'self'"

    def test_referrer_policy(self, client: TestClient) -> None:
        """Verify Referrer-Policy header."""
        response = client.get("/healthz")
        assert response.headers.get("Referrer-Policy") == "strict-origin-when-cross-origin"

    def test_permissions_policy(self, client: TestClient) -> None:
        """Verify Permissions-Policy header."""
        response = client.get("/healthz")
        assert "geolocation=()" in response.headers.get("Permissions-Policy", "")


class TestRequestIdMiddleware:
    """Test that request IDs are generated and propagated."""

    def test_request_id_generated(self, client: TestClient) -> None:
        """Verify request ID is generated if not provided."""
        response = client.get("/healthz")
        request_id = response.headers.get("X-Request-ID")
        assert request_id is not None
        # Should be a UUID-like string
        assert len(request_id) == 36  # UUID format: 8-4-4-4-12

    def test_request_id_propagated(self, client: TestClient) -> None:
        """Verify client-provided request ID is propagated."""
        custom_id = "my-custom-request-id-12345"
        response = client.get(
            "/healthz",
            headers={"X-Request-ID": custom_id},
        )
        assert response.headers.get("X-Request-ID") == custom_id

    def test_correlation_id_accepted(self, client: TestClient) -> None:
        """Verify X-Correlation-ID is also accepted."""
        custom_id = "correlation-id-67890"
        response = client.get(
            "/healthz",
            headers={"X-Correlation-ID": custom_id},
        )
        assert response.headers.get("X-Request-ID") == custom_id


class TestWritableHealthCheck:
    """Test that health check verifies actual writability."""

    def test_healthz_returns_writable_true(self, client: TestClient, temp_sandbox: Path) -> None:
        """Verify health check reports writable=true when sandbox is writable."""
        response = client.get("/healthz")
        assert response.status_code == 200
        data = response.json()
        assert data["writable"] is True
        assert "write_error" not in data

    def test_healthz_version_updated(self, client: TestClient) -> None:
        """Verify version matches package version."""
        from aperion_fsal import __version__

        response = client.get("/healthz")
        data = response.json()
        assert data["version"] == __version__


class TestRequestTimeouts:
    """Test that request timeouts are configured."""

    def test_timeout_returns_504(self, client: TestClient, temp_sandbox: Path) -> None:
        """Verify timeout returns 504 Gateway Timeout."""
        # We can't easily test actual timeouts without slow operations,
        # but we can verify the error handling works
        from aperion_fsal.service.app import REQUEST_TIMEOUT

        assert REQUEST_TIMEOUT == 30.0  # Default


class TestStructuredLogging:
    """Test structured logging configuration."""

    def test_logging_module_imports(self) -> None:
        """Verify logging module imports correctly."""
        from aperion_fsal.service.logging import (
            bind_request_context,
            clear_request_context,
            configure_logging,
            get_logger,
            security_log,
        )

        # All should be callable/usable
        assert callable(configure_logging)
        assert callable(get_logger)
        assert callable(bind_request_context)
        assert callable(clear_request_context)
        assert hasattr(security_log, "auth_success")

    def test_get_logger_returns_bound_logger(self) -> None:
        """Verify get_logger returns a structlog logger."""
        from aperion_fsal.service.logging import get_logger

        log = get_logger("test")
        assert hasattr(log, "info")
        assert hasattr(log, "warning")
        assert hasattr(log, "error")


class TestMiddlewareModule:
    """Test middleware module directly."""

    def test_security_headers_middleware_exists(self) -> None:
        """Verify SecurityHeadersMiddleware exists."""
        from aperion_fsal.service.middleware import SecurityHeadersMiddleware

        assert SecurityHeadersMiddleware is not None

    def test_request_id_middleware_exists(self) -> None:
        """Verify RequestIdMiddleware exists."""
        from aperion_fsal.service.middleware import RequestIdMiddleware

        assert RequestIdMiddleware is not None


class TestGunicornConfiguration:
    """Test Dockerfile Gunicorn configuration."""

    def test_dockerfile_uses_gunicorn(self) -> None:
        """Verify Dockerfile uses gunicorn."""
        dockerfile_path = Path(__file__).parent.parent.parent / "Dockerfile"
        if dockerfile_path.exists():
            content = dockerfile_path.read_text()
            assert "gunicorn" in content
            assert "UvicornWorker" in content
            assert "GUNICORN_WORKERS" in content
