"""
Cookie Security Tests

Tests for the dynamic cookie security behavior that allows localhost HTTP
while requiring HTTPS for non-localhost connections.

Security model:
- Localhost HTTP (127.0.0.1, ::1): Cookies work without Secure flag
- Proxied requests (X-Forwarded-For): Always get Secure flag
- Non-localhost HTTP: Get Secure flag (will fail, by design)
- HTTPS: Always get Secure flag
- TESTING mode: Never get Secure flag (for CI/development)
"""

import pytest
from tests.test_utils import add_src_to_path

add_src_to_path()


@pytest.fixture
def app():
    """Create test application with TESTING mode enabled (default for tests)."""
    from src.local_deep_research.web.app_factory import create_app

    app, _ = create_app()
    app.config["TESTING"] = True
    app.config["WTF_CSRF_ENABLED"] = False  # Disable CSRF for these tests
    # LDR_TESTING_MODE is set by create_app based on env vars
    # For most tests, this is True (CI environment)
    return app


@pytest.fixture
def app_production_mode():
    """Create test application with production-like cookie security."""
    import os
    from src.local_deep_research.web.app_factory import create_app

    # Temporarily unset ALL testing env vars to simulate production
    # Note: PYTEST_CURRENT_TEST is also checked by create_app
    old_testing = os.environ.pop("TESTING", None)
    old_ci = os.environ.pop("CI", None)
    old_pytest = os.environ.pop("PYTEST_CURRENT_TEST", None)

    try:
        app, _ = create_app()
        app.config["TESTING"] = True  # Flask testing mode for test client
        app.config["WTF_CSRF_ENABLED"] = False
        # Explicitly disable LDR testing mode to test production cookie behavior
        app.config["LDR_TESTING_MODE"] = False
        # Set to http to properly test HTTP behavior (default is https for URL generation)
        # In real production with HTTPS, wsgi.url_scheme is set by the server
        app.config["PREFERRED_URL_SCHEME"] = "http"
        return app
    finally:
        # Restore env vars
        if old_testing is not None:
            os.environ["TESTING"] = old_testing
        if old_ci is not None:
            os.environ["CI"] = old_ci
        if old_pytest is not None:
            os.environ["PYTEST_CURRENT_TEST"] = old_pytest


@pytest.fixture
def client(app):
    """Create test client."""
    return app.test_client()


class TestLocalhostCookieSecurity:
    """Test that localhost HTTP connections work without Secure flag."""

    def test_localhost_http_no_secure_flag(self, client):
        """Localhost HTTP requests should NOT have Secure flag on cookies."""
        # Default test client simulates localhost (127.0.0.1)
        response = client.get("/auth/login")

        # Check Set-Cookie header
        set_cookie = response.headers.get("Set-Cookie", "")

        # Should NOT have Secure flag for localhost HTTP
        # Note: In TESTING mode, Secure is never added
        assert response.status_code == 200
        # The cookie should be set (session cookie)
        assert "session=" in set_cookie

    def test_localhost_session_cookie_works(self, client):
        """Verify session cookies are sent back on subsequent requests from localhost."""
        # Get login page (establishes session)
        response1 = client.get("/auth/login")
        assert response1.status_code == 200

        # Make another request - session should persist
        response2 = client.get("/auth/login")
        assert response2.status_code == 200

        # Session should be maintained (cookie sent back)
        # This would fail if Secure flag was set but we're on HTTP


class TestLocalhostProductionMode:
    """Test localhost HTTP works in production mode (non-testing)."""

    def test_localhost_http_no_secure_flag_in_production(
        self, app_production_mode
    ):
        """Localhost HTTP requests should NOT have Secure flag even in production mode."""
        app = app_production_mode

        with app.test_client() as client:
            # Direct localhost request (no X-Forwarded-For)
            response = client.get("/auth/login")

            set_cookie = response.headers.get("Set-Cookie", "")

            # Should NOT have Secure flag for direct localhost HTTP
            assert "; Secure" not in set_cookie, (
                f"Localhost HTTP should NOT have Secure flag. Got: {set_cookie}"
            )
            assert "session=" in set_cookie  # Cookie should still be set


class TestProxiedRequestSecurity:
    """Test that proxied requests always get Secure flag."""

    def test_proxied_request_gets_secure_flag(self, app_production_mode):
        """Requests with X-Forwarded-For should get Secure flag."""
        app = app_production_mode

        with app.test_client() as client:
            # Simulate proxied request with X-Forwarded-For header
            # Note: Flask test client always has REMOTE_ADDR=127.0.0.1
            # but our code checks for X-Forwarded-For header presence
            response = client.get(
                "/auth/login",
                headers={"X-Forwarded-For": "192.168.1.100"},
            )

            set_cookie = response.headers.get("Set-Cookie", "")

            # Should have Secure flag because X-Forwarded-For header is present
            # This indicates request came through a proxy
            assert "; Secure" in set_cookie, (
                f"Proxied requests should have Secure flag. Got: {set_cookie}"
            )

    def test_spoofed_localhost_via_xff_still_secure(self, app_production_mode):
        """X-Forwarded-For: 127.0.0.1 should still get Secure flag (anti-spoofing)."""
        app = app_production_mode

        with app.test_client() as client:
            # Attacker tries to spoof localhost via X-Forwarded-For
            response = client.get(
                "/auth/login",
                headers={"X-Forwarded-For": "127.0.0.1"},
            )

            set_cookie = response.headers.get("Set-Cookie", "")

            # Should STILL have Secure flag - we detect the proxy header
            # and treat all proxied requests as production
            assert "; Secure" in set_cookie, (
                f"Spoofed localhost via X-Forwarded-For should still get Secure. Got: {set_cookie}"
            )


class TestTestingModeBehavior:
    """Test that TESTING mode disables Secure flag entirely."""

    def test_testing_mode_no_secure_flag(self, app):
        """In TESTING mode, Secure flag should never be added."""
        app.config["LDR_TESTING_MODE"] = True

        with app.test_client() as client:
            # Even with X-Forwarded-For, TESTING mode skips Secure
            response = client.get(
                "/auth/login",
                headers={"X-Forwarded-For": "192.168.1.100"},
            )

            set_cookie = response.headers.get("Set-Cookie", "")

            # In TESTING mode, no Secure flag
            assert "; Secure" not in set_cookie


class TestCSRFErrorMessage:
    """Test that CSRF errors provide helpful messages for cookie issues."""

    def test_csrf_error_message_is_helpful(self, app):
        """CSRF error for non-localhost HTTP should explain the issue."""
        app.config["WTF_CSRF_ENABLED"] = True
        app.config["LDR_TESTING_MODE"] = False

        with app.test_client() as client:
            # Get login page to establish session
            client.get("/auth/login")

            # Try to POST without proper CSRF token
            # Simulating non-localhost by adding X-Forwarded-For
            response = client.post(
                "/auth/login",
                data={"username": "test", "password": "test"},
                headers={"X-Forwarded-For": "192.168.1.100"},
            )

            # Should get a 400 error with helpful message
            assert response.status_code == 400

            # Check for helpful error message
            data = response.get_json()
            if data and "error" in data:
                error_msg = data["error"]
                # Should mention the security issue
                assert (
                    "Session cookie error" in error_msg or "CSRF" in error_msg
                )


class TestCookieSecurityDocumentation:
    """Documentation tests explaining the security model."""

    def test_security_model_documentation(self):
        """
        Document the cookie security model.

        WHY THIS EXISTS:
        - Session cookies with Secure flag only work over HTTPS
        - Local development typically uses HTTP
        - We want security in production but usability in development

        HOW IT WORKS:
        1. Check if request has X-Forwarded-For header (proxy indicator)
        2. If proxied: Always add Secure flag (production behind proxy)
        3. If not proxied: Check request.remote_addr
           - 127.0.0.1 or ::1: Skip Secure flag (localhost development)
           - Other IPs: Add Secure flag (direct non-localhost = needs HTTPS)
        4. TESTING mode: Never add Secure flag (CI/test environments)

        SECURITY PROPERTIES:
        - Localhost HTTP: ✅ Works - traffic is local only
        - Proxied requests: ✅ Secure - always requires HTTPS
        - X-Forwarded-For spoofing: ✅ Blocked - proxy presence = Secure
        - Non-localhost HTTP: ✅ Blocked - requires HTTPS
        - TESTING mode: ⚠️ Insecure but explicit opt-in

        ATTACK PREVENTION:
        - Session hijacking on public networks: Prevented by Secure flag
        - X-Forwarded-For spoofing: Prevented by checking for proxy header
        - Development convenience: Preserved for localhost only
        """
        assert True  # Documentation test


def test_cookie_security_summary():
    """
    Summary of cookie security behavior for CI validation.

    Expected behavior:
    | Scenario                    | Secure Flag | Should Work? |
    |-----------------------------|-------------|--------------|
    | localhost:5000 (direct)     | No          | ✅ Yes       |
    | 127.0.0.1:5000 (direct)     | No          | ✅ Yes       |
    | 192.168.x.x:5000 (direct)   | Yes         | ❌ No (HTTP) |
    | Behind proxy (any)          | Yes         | ✅ If HTTPS  |
    | X-Forwarded-For spoof       | Yes         | ❌ Blocked   |
    | TESTING=1 mode              | No          | ✅ Yes       |
    """
    assert True  # Documentation/summary test
