"""Tests for uncovered branches in security_headers.py."""

import flask
import pytest

from local_deep_research.security.security_headers import SecurityHeaders


def _make_app(**config_overrides):
    """Create a minimal Flask app with SecurityHeaders."""
    app = flask.Flask(__name__)
    app.config["TESTING"] = True
    for k, v in config_overrides.items():
        app.config[k] = v
    SecurityHeaders(app)
    return app


class TestIsApiRoute:
    def test_api_prefix(self):
        assert SecurityHeaders._is_api_route("/api/research") is True

    def test_research_api_prefix(self):
        assert SecurityHeaders._is_api_route("/research/api/test") is True

    def test_history_api_prefix(self):
        assert SecurityHeaders._is_api_route("/history/api") is True

    def test_static_not_api(self):
        assert SecurityHeaders._is_api_route("/static/file.js") is False

    def test_root_not_api(self):
        assert SecurityHeaders._is_api_route("/") is False

    def test_settings_not_api(self):
        assert SecurityHeaders._is_api_route("/settings/") is False


class TestValidateCorsConfig:
    def test_credentials_with_wildcard_raises(self):
        with pytest.raises(
            ValueError, match="Cannot use credentials with wildcard"
        ):
            _make_app(
                SECURITY_CORS_ALLOWED_ORIGINS="*",
                SECURITY_CORS_ALLOW_CREDENTIALS=True,
            )

    def test_credentials_with_specific_origin_ok(self):
        app = _make_app(
            SECURITY_CORS_ALLOWED_ORIGINS="https://example.com",
            SECURITY_CORS_ALLOW_CREDENTIALS=True,
        )
        assert app is not None

    def test_cors_disabled_skips_validation(self):
        # Should not raise even with bad config when CORS is disabled
        app = _make_app(
            SECURITY_CORS_ENABLED=False,
            SECURITY_CORS_ALLOWED_ORIGINS="*",
            SECURITY_CORS_ALLOW_CREDENTIALS=True,
        )
        assert app is not None

    def test_multi_origin_with_credentials_logs_info(self):
        app = _make_app(
            SECURITY_CORS_ALLOWED_ORIGINS="https://a.com,https://b.com",
            SECURITY_CORS_ALLOW_CREDENTIALS=True,
        )
        assert app is not None


class TestAddSecurityHeaders:
    def test_basic_headers_present(self):
        app = _make_app()
        with app.test_client() as client:

            @app.route("/test")
            def test_route():
                return "ok"

            resp = client.get("/test")
            assert "Content-Security-Policy" in resp.headers
            assert "X-Frame-Options" in resp.headers
            assert "X-Content-Type-Options" in resp.headers
            assert "Permissions-Policy" in resp.headers
            assert "Referrer-Policy" in resp.headers

    def test_hsts_not_set_on_http(self):
        app = _make_app()
        with app.test_client() as client:

            @app.route("/test")
            def test_route():
                return "ok"

            resp = client.get("/test")
            assert "Strict-Transport-Security" not in resp.headers

    def test_static_path_no_cache_control(self):
        """Static paths should NOT get no-cache headers."""
        app = _make_app()

        @app.route("/static/test.js")
        def static_test():
            return "js"

        with app.test_client() as client:
            resp = client.get("/static/test.js")
            cache = resp.headers.get("Cache-Control", "")
            assert "no-store" not in cache

    def test_non_static_path_gets_no_cache(self):
        app = _make_app()

        @app.route("/page")
        def page():
            return "html"

        with app.test_client() as client:
            resp = client.get("/page")
            assert "no-store" in resp.headers.get("Cache-Control", "")

    def test_coep_policy_from_config(self):
        app = _make_app(SECURITY_COEP_POLICY="require-corp")

        @app.route("/test")
        def test_route():
            return "ok"

        with app.test_client() as client:
            resp = client.get("/test")
            assert (
                resp.headers["Cross-Origin-Embedder-Policy"] == "require-corp"
            )


class TestCorsHeaders:
    def test_wildcard_origin(self):
        app = _make_app(SECURITY_CORS_ALLOWED_ORIGINS="*")

        @app.route("/api/test")
        def api_test():
            return "ok"

        with app.test_client() as client:
            resp = client.get("/api/test")
            assert resp.headers["Access-Control-Allow-Origin"] == "*"
            assert "Access-Control-Allow-Credentials" not in resp.headers

    def test_single_origin(self):
        app = _make_app(
            SECURITY_CORS_ALLOWED_ORIGINS="https://example.com",
            SECURITY_CORS_ALLOW_CREDENTIALS=True,
        )

        @app.route("/api/test")
        def api_test():
            return "ok"

        with app.test_client() as client:
            resp = client.get("/api/test")
            assert (
                resp.headers["Access-Control-Allow-Origin"]
                == "https://example.com"
            )
            assert resp.headers["Access-Control-Allow-Credentials"] == "true"

    def test_multi_origin_reflects_matching(self):
        app = _make_app(
            SECURITY_CORS_ALLOWED_ORIGINS="https://a.com,https://b.com",
            SECURITY_CORS_ALLOW_CREDENTIALS=True,
        )

        @app.route("/api/test")
        def api_test():
            return "ok"

        with app.test_client() as client:
            resp = client.get("/api/test", headers={"Origin": "https://b.com"})
            assert (
                resp.headers["Access-Control-Allow-Origin"] == "https://b.com"
            )
            assert resp.headers["Access-Control-Allow-Credentials"] == "true"

    def test_multi_origin_non_matching_omits_acao(self):
        app = _make_app(
            SECURITY_CORS_ALLOWED_ORIGINS="https://a.com,https://b.com",
        )

        @app.route("/api/test")
        def api_test():
            return "ok"

        with app.test_client() as client:
            resp = client.get(
                "/api/test", headers={"Origin": "https://evil.com"}
            )
            assert "Access-Control-Allow-Origin" not in resp.headers

    def test_non_api_route_no_cors(self):
        app = _make_app(SECURITY_CORS_ALLOWED_ORIGINS="*")

        @app.route("/page")
        def page():
            return "ok"

        with app.test_client() as client:
            resp = client.get("/page")
            assert "Access-Control-Allow-Origin" not in resp.headers

    def test_max_age_header(self):
        app = _make_app(SECURITY_CORS_ALLOWED_ORIGINS="*")

        @app.route("/api/test")
        def api_test():
            return "ok"

        with app.test_client() as client:
            resp = client.get("/api/test")
            assert resp.headers["Access-Control-Max-Age"] == "3600"

    def test_vary_origin_present_when_acao_is_dynamic(self):
        """A reflected (non-wildcard) ACAO must carry Vary: Origin so shared
        caches don't serve one origin's ACAO to another."""
        app = _make_app(
            SECURITY_CORS_ALLOWED_ORIGINS="https://a.com,https://b.com",
        )

        @app.route("/api/test")
        def api_test():
            return "ok"

        with app.test_client() as client:
            resp = client.get("/api/test", headers={"Origin": "https://b.com"})
            assert (
                resp.headers["Access-Control-Allow-Origin"] == "https://b.com"
            )
            vary_tokens = [
                v.strip().lower()
                for v in resp.headers.get("Vary", "").split(",")
            ]
            assert "origin" in vary_tokens

    def test_vary_origin_absent_for_wildcard(self):
        """A fixed wildcard ACAO is the same for every origin, so it must NOT
        add Vary: Origin (which would needlessly fragment caches)."""
        app = _make_app(SECURITY_CORS_ALLOWED_ORIGINS="*")

        @app.route("/api/test")
        def api_test():
            return "ok"

        with app.test_client() as client:
            resp = client.get("/api/test", headers={"Origin": "https://x.com"})
            assert resp.headers["Access-Control-Allow-Origin"] == "*"
            vary_tokens = [
                v.strip().lower()
                for v in resp.headers.get("Vary", "").split(",")
            ]
            assert "origin" not in vary_tokens

    def test_vary_origin_not_duplicated(self):
        """When the response already carries a Vary header, Origin is appended
        once rather than duplicated."""
        from flask import make_response

        app = _make_app(
            SECURITY_CORS_ALLOWED_ORIGINS="https://a.com,https://b.com",
        )

        @app.route("/api/test")
        def api_test():
            resp = make_response("ok")
            resp.headers["Vary"] = "Accept-Encoding"
            return resp

        with app.test_client() as client:
            resp = client.get("/api/test", headers={"Origin": "https://b.com"})
            vary_tokens = [
                v.strip().lower()
                for v in resp.headers.get("Vary", "").split(",")
                if v.strip()
            ]
            assert vary_tokens.count("origin") == 1
            assert "accept-encoding" in vary_tokens


class TestGetCspPolicy:
    def test_default_connect_src(self):
        app = _make_app()
        sh = SecurityHeaders()
        sh.app = app
        csp = sh.get_csp_policy()
        assert "connect-src 'self'" in csp

    def test_custom_connect_src(self):
        app = _make_app(
            SECURITY_CSP_CONNECT_SRC="'self' https://api.example.com"
        )
        sh = SecurityHeaders()
        sh.app = app
        csp = sh.get_csp_policy()
        assert "connect-src 'self' https://api.example.com" in csp

    def test_csp_contains_required_directives(self):
        app = _make_app()
        sh = SecurityHeaders()
        sh.app = app
        csp = sh.get_csp_policy()
        for directive in [
            "default-src",
            "script-src",
            "style-src",
            "img-src",
            "object-src 'none'",
        ]:
            assert directive in csp


class TestGetPermissionsPolicy:
    def test_disables_dangerous_features(self):
        policy = SecurityHeaders.get_permissions_policy()
        for feature in ["geolocation", "camera", "microphone", "payment"]:
            assert f"{feature}=()" in policy
