# region TestClientHeaders [TYPE Module]
# @RELATION  BELONGS_TO -> SrcRoot
# @SEMANTICS: tests, llm-client, openrouter, headers
# @PURPOSE: Verify OpenRouter client initialization includes provider-specific headers.

from src.plugins.llm_analysis.models import LLMProviderType
from src.plugins.llm_analysis.service import LLMClient


# region test_openrouter_client_includes_referer_and_title_headers [TYPE Function]
# @RELATION  BINDS_TO -> TestClientHeaders
# @PURPOSE: OpenRouter requests should carry site/app attribution headers for compatibility.
# @PRE  Client is initialized for OPENROUTER provider.
# @POST  Async client headers include Authorization, HTTP-Referer, and X-Title.
def test_openrouter_client_includes_referer_and_title_headers(monkeypatch):
    monkeypatch.setenv("OPENROUTER_SITE_URL", "http://localhost:8000")
    monkeypatch.setenv("OPENROUTER_APP_NAME", "ss-tools-test")

    client = LLMClient(
        provider_type=LLMProviderType.OPENROUTER,
        api_key="sk-test-provider-key-123456",
        base_url="https://openrouter.ai/api/v1",
        default_model="nvidia/nemotron-nano-12b-v2-vl:free",
    )

    headers = dict(client.client.default_headers)
    assert headers["Authorization"] == "Bearer sk-test-provider-key-123456"
    assert headers["HTTP-Referer"] == "http://localhost:8000"
    assert headers["X-Title"] == "ss-tools-test"
# endregion test_openrouter_client_includes_referer_and_title_headers


# region test_litellm_client_uses_default_bearer_auth [TYPE Function]
# @RELATION  BINDS_TO -> TestClientHeaders
# @PURPOSE: LiteLLM proxy uses standard OpenAI-compatible Bearer auth — no special headers needed.
# @PRE  Client is initialized for LITELLM provider.
# @POST  Async client headers include only Authorization — no extra provider-specific headers.
def test_litellm_client_uses_default_bearer_auth():
    """Verify LiteLLM client initialization uses standard Bearer auth without extra headers."""
    client = LLMClient(
        provider_type=LLMProviderType.LITELLM,
        api_key="sk-litellm-key-123456",
        base_url="http://localhost:4000/v1",
        default_model="gpt-4o",
    )

    headers = dict(client.client.default_headers)
    assert headers["Authorization"] == "Bearer sk-litellm-key-123456"
    # LiteLLM proxy uses standard OpenAI-compatible auth — no special headers
    assert "HTTP-Referer" not in headers
    assert "X-Title" not in headers
    assert "Authentication" not in headers
    assert "X-API-Key" not in headers
# endregion test_litellm_client_uses_default_bearer_auth


# region test_get_ssl_verify_default_context [TYPE Function]
# @RELATION  BINDS_TO -> TestClientHeaders
# @PURPOSE: _get_ssl_verify returns SSLContext with CERT_REQUIRED by default.
# @PRE  LLM_SSL_VERIFY env var is not set.
# @POST  Returns SSLContext with verify_mode=CERT_REQUIRED.
def test_get_ssl_verify_default_context():
    """Verify default returns SSLContext with CERT_REQUIRED."""
    import ssl
    result = LLMClient._get_ssl_verify()
    assert isinstance(result, ssl.SSLContext), "Expected SSLContext, not bool"
    assert result.verify_mode == ssl.CERT_REQUIRED
# endregion test_get_ssl_verify_default_context


# region test_get_ssl_verify_false_values [TYPE Function]
# @RELATION  BINDS_TO -> TestClientHeaders
# @PURPOSE: _get_ssl_verify returns False for "false"/"0"/"no"/"off".
# @PRE  LLM_SSL_VERIFY env var is set to each falsy value.
# @POST  Returns False.
def test_get_ssl_verify_false_values(monkeypatch):
    for val in ("false", "False", "0", "no", "off"):
        monkeypatch.setenv("LLM_SSL_VERIFY", val)
        assert LLMClient._get_ssl_verify() is False, f"Expected False for LLM_SSL_VERIFY={val}"
# endregion test_get_ssl_verify_false_values


# region test_get_ssl_verify_true_returns_context [TYPE Function]
# @RELATION  BINDS_TO -> TestClientHeaders
# @PURPOSE: _get_ssl_verify returns SSLContext for truthy env values.
# @PRE  LLM_SSL_VERIFY env var is set to truthy values.
# @POST  Returns SSLContext with CERT_REQUIRED.
def test_get_ssl_verify_true_returns_context(monkeypatch):
    import ssl
    for val in ("TRUE", "1", "yes", "on", "true"):
        monkeypatch.setenv("LLM_SSL_VERIFY", val)
        result = LLMClient._get_ssl_verify()
        assert isinstance(result, ssl.SSLContext), f"Expected SSLContext for LLM_SSL_VERIFY={val}"
        assert result.verify_mode == ssl.CERT_REQUIRED
# endregion test_get_ssl_verify_true_returns_path


# region test_format_connection_error_no_cause [TYPE Function]
# @RELATION  BINDS_TO -> TestClientHeaders
# @PURPOSE: _format_connection_error with single exception (no cause chain).
# @PRE  Exception has no __cause__ or __context__.
# @POST  Returns formatted string with only the top-level exception.
def test_format_connection_error_no_cause():
    exc = ValueError("test error")
    result = LLMClient._format_connection_error(exc)
    assert "ValueError: test error" in result
    assert "└─" not in result
# endregion test_format_connection_error_no_cause


# region test_format_connection_error_with_cause [TYPE Function]
# @RELATION  BINDS_TO -> TestClientHeaders
# @PURPOSE: _format_connection_error with chained exception (__cause__).
# @PRE  Exception has a __cause__.
# @POST  Returns formatted string with chain.
def test_format_connection_error_with_cause():
    inner = ConnectionRefusedError("Connection refused")
    outer = RuntimeError("API call failed")
    outer.__cause__ = inner
    result = LLMClient._format_connection_error(outer)
    assert "RuntimeError: API call failed" in result
    assert "└─" in result
    assert "ConnectionRefusedError: Connection refused" in result
# endregion test_format_connection_error_with_cause


# region test_format_connection_error_deep_chain [TYPE Function]
# @RELATION  BINDS_TO -> TestClientHeaders
# @PURPOSE: _format_connection_error with 3-level chain.
# @PRE  Exception has nested __cause__ chain.
# @POST  Returns formatted string with all levels.
def test_format_connection_error_deep_chain():
    dns = Exception("Name or service not known")
    ssl = ConnectionError("SSL: CERTIFICATE_VERIFY_FAILED")
    ssl.__cause__ = dns
    outer = RuntimeError("Connection error")
    outer.__cause__ = ssl
    result = LLMClient._format_connection_error(outer)
    assert "RuntimeError: Connection error" in result
    assert "ConnectionError: SSL: CERTIFICATE_VERIFY_FAILED" in result
    assert "Exception: Name or service not known" in result
# endregion test_format_connection_error_deep_chain


# region test_litellm_client_verify_default [TYPE Function]
# @RELATION  BINDS_TO -> TestClientHeaders
# @PURPOSE: LLMClient passes verify=True to httpx.AsyncClient by default.
# @PRE  LLM_SSL_VERIFY env var is not set.
# @POST  httpcore pool SSL context has verify_mode=CERT_REQUIRED (2).
def test_litellm_client_verify_default():
    """Verify default SSL context with CERT_REQUIRED when LLM_SSL_VERIFY unset."""
    import ssl
    client = LLMClient(
        provider_type=LLMProviderType.LITELLM,
        api_key="sk-test-verify-key",
        base_url="http://localhost:4000/v1",
        default_model="gpt-4o",
    )
    pool = client.client._client._transport._pool
    assert pool._ssl_context.verify_mode == ssl.CERT_REQUIRED, \
        "verify=True should set verify_mode to CERT_REQUIRED"
# endregion test_litellm_client_verify_default


# region test_litellm_client_verify_disabled [TYPE Function]
# @RELATION  BINDS_TO -> TestClientHeaders
# @PURPOSE: LLMClient passes verify=False when LLM_SSL_VERIFY=false.
# @PRE  LLM_SSL_VERIFY is set to "false".
# @POST  httpcore pool SSL context has verify_mode=CERT_NONE (0).
def test_litellm_client_verify_disabled(monkeypatch):
    monkeypatch.setenv("LLM_SSL_VERIFY", "false")
    client = LLMClient(
        provider_type=LLMProviderType.LITELLM,
        api_key="sk-test-verify-key",
        base_url="http://localhost:4000/v1",
        default_model="gpt-4o",
    )
    import ssl
    pool = client.client._client._transport._pool
    assert pool._ssl_context.verify_mode == ssl.CERT_NONE, \
        "verify=False should set verify_mode to CERT_NONE"
# endregion test_litellm_client_verify_disabled
# endregion TestClientHeaders
