# -*- coding: utf-8 -*-
"""Location: ./tests/security/test_security_cookies.py
Copyright 2026
SPDX-License-Identifier: Apache-2.0
Authors: Mihai Criveti

Security Cookie Testing.

This module contains tests for secure cookie configuration and handling.
"""

# Standard
from unittest.mock import patch

# Third-Party
from fastapi import Response
import pytest

# First-Party
from mcpgateway.config import settings
from mcpgateway.utils.security_cookies import clear_auth_cookie, clear_session_cookie, set_auth_cookie, set_session_cookie


class TestSecureCookies:
    """Test secure cookie configuration and attributes."""

    def test_set_auth_cookie_development(self):
        """Test auth cookie in development environment."""
        response = Response()

        with patch.object(settings, "environment", "development"):
            with patch.object(settings, "secure_cookies", False):
                set_auth_cookie(response, "test_token", remember_me=False)

        # Check that cookie was set
        set_cookie_header = response.headers.get("set-cookie", "")
        assert "jwt_token=test_token" in set_cookie_header
        assert "HttpOnly" in set_cookie_header
        assert "SameSite=lax" in set_cookie_header
        assert "Path=/" in set_cookie_header
        assert "Max-Age=3600" in set_cookie_header  # 1 hour

        # In development with secure_cookies=False, Secure flag should not be present
        assert "Secure" not in set_cookie_header

    def test_set_auth_cookie_production(self):
        """Test auth cookie in production environment."""
        response = Response()

        with patch.object(settings, "environment", "production"):
            set_auth_cookie(response, "test_token", remember_me=False)

        set_cookie_header = response.headers.get("set-cookie", "")
        assert "jwt_token=test_token" in set_cookie_header
        assert "HttpOnly" in set_cookie_header
        assert "Secure" in set_cookie_header  # Should be secure in production
        assert "SameSite=lax" in set_cookie_header

    def test_set_auth_cookie_remember_me(self):
        """Test auth cookie with remember_me option."""
        response = Response()

        set_auth_cookie(response, "test_token", remember_me=True)

        set_cookie_header = response.headers.get("set-cookie", "")
        # 30 days = 30 * 24 * 3600 = 2592000 seconds
        assert "Max-Age=2592000" in set_cookie_header

    def test_set_auth_cookie_custom_samesite(self):
        """Test auth cookie with custom SameSite setting."""
        response = Response()

        with patch.object(settings, "cookie_samesite", "strict"):
            set_auth_cookie(response, "test_token")

        set_cookie_header = response.headers.get("set-cookie", "")
        assert "SameSite=strict" in set_cookie_header

    def test_clear_auth_cookie(self):
        """Test clearing auth cookie."""
        response = Response()

        clear_auth_cookie(response)

        set_cookie_header = response.headers.get("set-cookie", "")
        assert "jwt_token=" in set_cookie_header  # Empty value
        assert "HttpOnly" in set_cookie_header
        assert "Path=/" in set_cookie_header

    def test_set_session_cookie(self):
        """Test setting session cookie."""
        response = Response()

        set_session_cookie(response, "session_123", max_age=7200)

        set_cookie_header = response.headers.get("set-cookie", "")
        assert "session_id=session_123" in set_cookie_header
        assert "HttpOnly" in set_cookie_header
        assert "SameSite=lax" in set_cookie_header
        assert "Max-Age=7200" in set_cookie_header

    def test_clear_session_cookie(self):
        """Test clearing session cookie."""
        response = Response()

        clear_session_cookie(response)

        set_cookie_header = response.headers.get("set-cookie", "")
        assert "session_id=" in set_cookie_header
        assert "HttpOnly" in set_cookie_header

    def test_secure_flag_with_explicit_setting(self):
        """Test secure flag behavior with explicit secure_cookies setting."""
        response = Response()

        # Test with secure_cookies=True in development
        with patch.object(settings, "environment", "development"):
            with patch.object(settings, "secure_cookies", True):
                set_auth_cookie(response, "test_token")

        set_cookie_header = response.headers.get("set-cookie", "")
        assert "Secure" in set_cookie_header  # Should be secure when explicitly enabled

    def test_cookie_attributes_consistency(self):
        """Test that cookie attributes are consistent between set and clear operations."""
        response_set = Response()
        response_clear = Response()

        with patch.object(settings, "environment", "production"):
            with patch.object(settings, "cookie_samesite", "strict"):
                set_auth_cookie(response_set, "test_token")
                clear_auth_cookie(response_clear)

        set_header = response_set.headers.get("set-cookie", "")
        clear_header = response_clear.headers.get("set-cookie", "")

        # Both should have same security attributes
        for attr in ["HttpOnly", "Secure", "SameSite=strict", "Path=/"]:
            assert attr in set_header
            assert attr in clear_header


class TestCookieSecurityConfiguration:
    """Test cookie security configuration under different scenarios."""

    @pytest.mark.parametrize(
        "environment,secure_cookies,expected_secure",
        [
            ("development", False, False),
            ("development", True, True),
            ("production", False, True),  # Production always uses secure
            ("production", True, True),
        ],
    )
    def test_secure_flag_combinations(self, environment: str, secure_cookies: bool, expected_secure: bool):
        """Test secure flag under different environment and configuration combinations."""
        response = Response()

        with patch.object(settings, "environment", environment):
            with patch.object(settings, "secure_cookies", secure_cookies):
                set_auth_cookie(response, "test_token")

        set_cookie_header = response.headers.get("set-cookie", "")

        if expected_secure:
            assert "Secure" in set_cookie_header
        else:
            assert "Secure" not in set_cookie_header

    @pytest.mark.parametrize("samesite_value", ["strict", "lax", "none"])
    def test_samesite_options(self, samesite_value: str):
        """Test different SameSite options."""
        response = Response()

        with patch.object(settings, "cookie_samesite", samesite_value):
            set_auth_cookie(response, "test_token")

        set_cookie_header = response.headers.get("set-cookie", "")
        assert f"SameSite={samesite_value}" in set_cookie_header

    def test_cookie_httponly_always_set(self):
        """Test that HttpOnly is always set regardless of configuration."""
        response = Response()

        # Test in various configurations
        configurations = [
            {"environment": "development", "secure_cookies": False},
            {"environment": "development", "secure_cookies": True},
            {"environment": "production", "secure_cookies": False},
            {"environment": "production", "secure_cookies": True},
        ]

        for config in configurations:
            response = Response()  # Fresh response for each test
            with patch.multiple(settings, **config):
                set_auth_cookie(response, "test_token")

            set_cookie_header = response.headers.get("set-cookie", "")
            assert "HttpOnly" in set_cookie_header, f"HttpOnly missing in config: {config}"

    def test_cookie_path_always_set(self):
        """Test that Path is always set to root."""
        response = Response()

        set_auth_cookie(response, "test_token")

        set_cookie_header = response.headers.get("set-cookie", "")
        assert "Path=/" in set_cookie_header

    def test_multiple_cookies_do_not_interfere(self):
        """Test that setting multiple different cookies doesn't interfere."""
        response = Response()

        set_auth_cookie(response, "auth_token")
        set_session_cookie(response, "session_id", max_age=1800)

        # Response should have multiple set-cookie headers
        set_cookie_headers = response.headers.getlist("set-cookie")
        assert len(set_cookie_headers) == 2

        # Check that both cookies are present
        all_headers = " ".join(set_cookie_headers)
        assert "jwt_token=auth_token" in all_headers
        assert "session_id=session_id" in all_headers
