# -*- coding: utf-8 -
#
# This file is part of gunicorn released under the MIT license.
# See the NOTICE for more information.

"""Tests for HTTP/2 configuration settings."""

import pytest

from gunicorn import config
from gunicorn.config import Config


class TestHttpProtocolsConfig:
    """Test http_protocols configuration setting."""

    def test_default_is_h1(self):
        c = Config()
        assert c.http_protocols == ["h1"]

    def test_set_h1_only(self):
        c = Config()
        c.set("http_protocols", "h1")
        assert c.http_protocols == ["h1"]

    def test_set_h2_only(self):
        c = Config()
        c.set("http_protocols", "h2")
        assert c.http_protocols == ["h2"]

    def test_set_h1_and_h2(self):
        c = Config()
        c.set("http_protocols", "h2,h1")
        assert c.http_protocols == ["h2", "h1"]

    def test_set_h1_h2_order_preserved(self):
        c = Config()
        c.set("http_protocols", "h1,h2")
        assert c.http_protocols == ["h1", "h2"]

    def test_whitespace_handling(self):
        c = Config()
        c.set("http_protocols", " h1 , h2 ")
        assert c.http_protocols == ["h1", "h2"]

    def test_case_insensitive(self):
        c = Config()
        c.set("http_protocols", "H1,H2")
        assert c.http_protocols == ["h1", "h2"]

    def test_empty_string_defaults_to_h1(self):
        c = Config()
        c.set("http_protocols", "")
        assert c.http_protocols == ["h1"]

    def test_none_defaults_to_h1(self):
        c = Config()
        c.set("http_protocols", None)
        assert c.http_protocols == ["h1"]

    def test_invalid_protocol(self):
        c = Config()
        with pytest.raises(ValueError) as exc_info:
            c.set("http_protocols", "h4")
        assert "Invalid protocol" in str(exc_info.value)
        assert "h4" in str(exc_info.value)

    def test_invalid_type(self):
        c = Config()
        with pytest.raises(TypeError) as exc_info:
            c.set("http_protocols", 123)
        assert "must be a string" in str(exc_info.value)

    def test_invalid_type_list(self):
        c = Config()
        with pytest.raises(TypeError):
            c.set("http_protocols", ["h1", "h2"])

    def test_mixed_valid_invalid(self):
        c = Config()
        with pytest.raises(ValueError):
            c.set("http_protocols", "h1,invalid,h2")


class TestHttp2MaxConcurrentStreams:
    """Test http2_max_concurrent_streams configuration setting."""

    def test_default_value(self):
        c = Config()
        assert c.http2_max_concurrent_streams == 100

    def test_set_custom_value(self):
        c = Config()
        c.set("http2_max_concurrent_streams", 50)
        assert c.http2_max_concurrent_streams == 50

    def test_set_from_string(self):
        c = Config()
        c.set("http2_max_concurrent_streams", "200")
        assert c.http2_max_concurrent_streams == 200

    def test_set_high_value(self):
        c = Config()
        c.set("http2_max_concurrent_streams", 1000)
        assert c.http2_max_concurrent_streams == 1000

    def test_negative_value_raises(self):
        c = Config()
        with pytest.raises(ValueError):
            c.set("http2_max_concurrent_streams", -1)

    def test_zero_value(self):
        # Zero is technically valid for positive int validator
        # It may have special meaning (use h2 default)
        c = Config()
        c.set("http2_max_concurrent_streams", 0)
        assert c.http2_max_concurrent_streams == 0


class TestHttp2InitialWindowSize:
    """Test http2_initial_window_size configuration setting."""

    def test_default_value(self):
        c = Config()
        # Default per RFC 7540 is 65535
        assert c.http2_initial_window_size == 65535

    def test_set_custom_value(self):
        c = Config()
        c.set("http2_initial_window_size", 131072)
        assert c.http2_initial_window_size == 131072

    def test_set_from_string(self):
        c = Config()
        c.set("http2_initial_window_size", "32768")
        assert c.http2_initial_window_size == 32768

    def test_negative_value_raises(self):
        c = Config()
        with pytest.raises(ValueError):
            c.set("http2_initial_window_size", -1)


class TestHttp2MaxFrameSize:
    """Test http2_max_frame_size configuration setting."""

    def test_default_value(self):
        c = Config()
        # Default per RFC 7540 is 16384
        assert c.http2_max_frame_size == 16384

    def test_set_custom_value(self):
        c = Config()
        c.set("http2_max_frame_size", 32768)
        assert c.http2_max_frame_size == 32768

    def test_set_from_string(self):
        c = Config()
        c.set("http2_max_frame_size", "65536")
        assert c.http2_max_frame_size == 65536

    def test_valid_min_value(self):
        """RFC 7540 minimum is 16384 (2^14)."""
        c = Config()
        c.set("http2_max_frame_size", 16384)
        assert c.http2_max_frame_size == 16384

    def test_valid_max_value(self):
        """RFC 7540 maximum is 16777215 (2^24 - 1)."""
        c = Config()
        c.set("http2_max_frame_size", 16777215)
        assert c.http2_max_frame_size == 16777215

    def test_valid_mid_range_value(self):
        """Test a value in the middle of the valid range."""
        c = Config()
        c.set("http2_max_frame_size", 1048576)  # 1MB
        assert c.http2_max_frame_size == 1048576

    def test_below_min_raises(self):
        """Values below 16384 should raise ValueError per RFC 7540."""
        c = Config()
        with pytest.raises(ValueError) as exc_info:
            c.set("http2_max_frame_size", 16383)
        assert "must be between 16384 and 16777215" in str(exc_info.value)

    def test_above_max_raises(self):
        """Values above 16777215 should raise ValueError per RFC 7540."""
        c = Config()
        with pytest.raises(ValueError) as exc_info:
            c.set("http2_max_frame_size", 16777216)
        assert "must be between 16384 and 16777215" in str(exc_info.value)

    def test_negative_value_raises(self):
        c = Config()
        with pytest.raises(ValueError):
            c.set("http2_max_frame_size", -1)


class TestHttp2MaxHeaderListSize:
    """Test http2_max_header_list_size configuration setting."""

    def test_default_value(self):
        c = Config()
        assert c.http2_max_header_list_size == 65536

    def test_set_custom_value(self):
        c = Config()
        c.set("http2_max_header_list_size", 131072)
        assert c.http2_max_header_list_size == 131072

    def test_set_from_string(self):
        c = Config()
        c.set("http2_max_header_list_size", "262144")
        assert c.http2_max_header_list_size == 262144

    def test_negative_value_raises(self):
        c = Config()
        with pytest.raises(ValueError):
            c.set("http2_max_header_list_size", -1)


class TestHttp2ConfigPropertyAccess:
    """Test property access for HTTP/2 settings."""

    def test_all_http2_settings_accessible(self):
        c = Config()
        # These should not raise
        _ = c.http_protocols
        _ = c.http2_max_concurrent_streams
        _ = c.http2_initial_window_size
        _ = c.http2_max_frame_size
        _ = c.http2_max_header_list_size


class TestHttp2ConfigDefaults:
    """Test that defaults match HTTP/2 specification values."""

    def test_window_size_matches_rfc(self):
        """RFC 7540 default is 2^16-1 (65535)."""
        c = Config()
        assert c.http2_initial_window_size == 65535

    def test_max_frame_size_matches_rfc_minimum(self):
        """RFC 7540 minimum is 2^14 (16384)."""
        c = Config()
        assert c.http2_max_frame_size == 16384

    def test_concurrent_streams_reasonable_default(self):
        """Default should be reasonable for production use."""
        c = Config()
        assert 1 <= c.http2_max_concurrent_streams <= 1000


class TestValidateHttpProtocols:
    """Test the validate_http_protocols function directly."""

    def test_validate_none(self):
        result = config.validate_http_protocols(None)
        assert result == ["h1"]

    def test_validate_empty_string(self):
        result = config.validate_http_protocols("")
        assert result == ["h1"]

    def test_validate_whitespace_only(self):
        result = config.validate_http_protocols("   ")
        assert result == ["h1"]

    def test_validate_single_protocol(self):
        result = config.validate_http_protocols("h2")
        assert result == ["h2"]

    def test_validate_multiple_protocols(self):
        result = config.validate_http_protocols("h2,h1")
        assert result == ["h2", "h1"]

    def test_validate_with_spaces(self):
        result = config.validate_http_protocols("h2 , h1")
        assert result == ["h2", "h1"]

    def test_validate_uppercase(self):
        result = config.validate_http_protocols("H2,H1")
        assert result == ["h1", "h2"] or result == ["h2", "h1"]

    def test_validate_invalid_raises(self):
        with pytest.raises(ValueError):
            config.validate_http_protocols("http2")

    def test_validate_type_error(self):
        with pytest.raises(TypeError):
            config.validate_http_protocols(42)


class TestValidateHttp2FrameSize:
    """Test the validate_http2_frame_size function directly."""

    def test_validate_min_value(self):
        """RFC 7540 minimum is 16384 (2^14)."""
        result = config.validate_http2_frame_size(16384)
        assert result == 16384

    def test_validate_max_value(self):
        """RFC 7540 maximum is 16777215 (2^24 - 1)."""
        result = config.validate_http2_frame_size(16777215)
        assert result == 16777215

    def test_validate_mid_range(self):
        """Test a value in the middle of the valid range."""
        result = config.validate_http2_frame_size(1000000)
        assert result == 1000000

    def test_validate_from_string(self):
        """Test that string values are converted properly."""
        result = config.validate_http2_frame_size("32768")
        assert result == 32768

    def test_validate_hex_string(self):
        """Test hex string conversion."""
        result = config.validate_http2_frame_size("0x10000")  # 65536
        assert result == 65536

    def test_validate_below_min_raises(self):
        """Values below 16384 should raise ValueError."""
        with pytest.raises(ValueError) as exc_info:
            config.validate_http2_frame_size(16383)
        assert "must be between 16384 and 16777215" in str(exc_info.value)

    def test_validate_above_max_raises(self):
        """Values above 16777215 should raise ValueError."""
        with pytest.raises(ValueError) as exc_info:
            config.validate_http2_frame_size(16777216)
        assert "must be between 16384 and 16777215" in str(exc_info.value)

    def test_validate_zero_raises(self):
        """Zero is below minimum and should raise ValueError."""
        with pytest.raises(ValueError):
            config.validate_http2_frame_size(0)

    def test_validate_negative_raises(self):
        """Negative values should raise ValueError."""
        with pytest.raises(ValueError):
            config.validate_http2_frame_size(-1)
