"""
Cookie Security Tests

Tests for the dynamic cookie security behavior.

Security model:
- Secure flag is added iff wsgi.url_scheme == "https".
- ProxyFix (with x_proto=1) translates X-Forwarded-Proto into wsgi.url_scheme
  before SecureCookieMiddleware sees it, so requests served over HTTPS by a
  reverse proxy correctly receive the Secure flag.
- HTTP requests never get Secure regardless of source IP. Setting Secure on
  an HTTP response causes the browser to drop the cookie entirely, so doing
  so based on IP heuristics broke legitimate Docker/LAN access without
  providing any cryptographic protection (issue #3849).
- 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 local_deep_research.web.app_factory import create_app

    app, _ = create_app()
    app.config["TESTING"] = True
    app.config["WTF_CSRF_ENABLED"] = False
    return app


@pytest.fixture
def app_production_mode():
    """Create test application with production-like cookie security."""
    import os
    from local_deep_research.web.app_factory import 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
        app.config["WTF_CSRF_ENABLED"] = False
        app.config["LDR_TESTING_MODE"] = False
        app.config["PREFERRED_URL_SCHEME"] = "http"
        return app
    finally:
        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:
    """Localhost HTTP works without Secure flag."""

    def test_localhost_http_no_secure_flag(self, client):
        response = client.get("/auth/login")
        set_cookie = response.headers.get("Set-Cookie", "")
        assert response.status_code == 200
        assert "session=" in set_cookie

    def test_localhost_session_cookie_works(self, client):
        response1 = client.get("/auth/login")
        assert response1.status_code == 200
        response2 = client.get("/auth/login")
        assert response2.status_code == 200


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

    def test_localhost_http_no_secure_flag_in_production(
        self, app_production_mode
    ):
        app = app_production_mode
        with app.test_client() as client:
            response = client.get("/auth/login")
            set_cookie = response.headers.get("Set-Cookie", "")
            assert "; Secure" not in set_cookie, (
                f"Localhost HTTP should NOT have Secure flag. Got: {set_cookie}"
            )
            assert "session=" in set_cookie


class TestHttpCookieSecurity:
    """HTTP requests never get the Secure flag, regardless of source IP.

    This is the core fix for #3849: setting Secure on an HTTP response makes
    the browser drop the cookie, breaking sessions without adding any real
    security (the underlying transport is still plaintext).
    """

    @pytest.mark.parametrize(
        "remote_addr",
        [
            "127.0.0.1",
            "192.168.1.100",
            "10.0.0.50",
            "172.16.0.1",
            "172.17.0.2",  # Default Docker bridge
            "172.67.130.145",  # Docker Desktop NAT (the #3849 trigger)
            "8.8.8.8",
            "104.16.0.1",
        ],
    )
    def test_http_no_secure_flag(self, app_production_mode, remote_addr):
        app = app_production_mode
        with app.test_client() as client:
            response = client.get(
                "/auth/login",
                environ_base={"REMOTE_ADDR": remote_addr},
            )
            set_cookie = response.headers.get("Set-Cookie", "")
            assert "; Secure" not in set_cookie, (
                f"HTTP from {remote_addr} should NOT have Secure flag. "
                f"Got: {set_cookie}"
            )


class TestHttpsCookieSecurity:
    """HTTPS requests always get the Secure flag."""

    def test_https_via_x_forwarded_proto_gets_secure(self, app_production_mode):
        """A reverse proxy terminating HTTPS sets X-Forwarded-Proto: https.
        ProxyFix translates this into wsgi.url_scheme, after which Secure is
        added.
        """
        app = app_production_mode
        with app.test_client() as client:
            response = client.get(
                "/auth/login",
                headers={"X-Forwarded-Proto": "https"},
                environ_base={"REMOTE_ADDR": "10.0.0.1"},
            )
            set_cookie = response.headers.get("Set-Cookie", "")
            assert "; Secure" in set_cookie, (
                f"HTTPS via reverse proxy should add Secure. Got: {set_cookie}"
            )


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

    def test_testing_mode_no_secure_flag(self, app):
        app.config["LDR_TESTING_MODE"] = True
        with app.test_client() as client:
            response = client.get(
                "/auth/login",
                headers={"X-Forwarded-Proto": "https"},
            )
            set_cookie = response.headers.get("Set-Cookie", "")
            assert "; Secure" not in set_cookie


@pytest.mark.skip(reason="documentation/placeholder test - not implemented")
def test_cookie_security_summary():
    """
    Summary of cookie security behavior for CI validation.

    Expected behavior:
    | Scenario                      | wsgi.url_scheme | Secure Flag |
    |-------------------------------|-----------------|-------------|
    | HTTP from localhost           | http            | No          |
    | HTTP from LAN client          | http            | No          |
    | HTTP from Docker NAT gateway  | http            | No          |
    | HTTP from public IP           | http            | No          |
    | HTTPS via reverse proxy       | https           | Yes         |
    | Direct HTTPS                  | https           | Yes         |
    | TESTING=1 mode                | any             | No          |

    The decision is based purely on the protocol (post-ProxyFix), not the
    source IP. Setting Secure on HTTP responses doesn't add security and
    breaks the browser's ability to store the cookie.
    """
    assert True
