"""Unit tests for the OAuth provider builder."""

from ims_mcp.auth.oauth import build_oauth_provider
from ims_mcp.config import RosettaConfig


def _make_config(**overrides) -> RosettaConfig:
    defaults = dict(
        server_url="http://localhost:80",
        version="r1",
        api_key="test-key",
        posthog_api_key="",
        posthog_host="https://us.i.posthog.com",
        debug=False,
        root_filter=[],
        transport="stdio",
        http_host="0.0.0.0",
        http_port=8000,
        redis_url=None,
        fernet_key=None,
        allowed_origins=[],
        oauth_authorization_endpoint="",
        oauth_token_endpoint="",
        oauth_introspection_endpoint="",
        oauth_client_id="",
        oauth_client_secret="",
        oauth_base_url="",
        oauth_callback_path="/auth/callback",
        oauth_scope="",
        oauth_revocation_endpoint="",
        oauth_jwt_signing_key=None,
        read_policy="all",
        write_policy="all",
        user_email="rosetta@griddynamics.net",
        invite_emails=[],
        plan_ttl_days=5,
    )
    defaults.update(overrides)
    return RosettaConfig(**defaults)


def test_returns_none_for_stdio():
    cfg = _make_config(transport="stdio")
    assert build_oauth_provider(cfg) is None


def test_returns_none_when_oauth_incomplete():
    cfg = _make_config(
        transport="http",
        oauth_authorization_endpoint="https://kc.example.com/auth",
        # missing token/introspection/client_id/client_secret
    )
    assert build_oauth_provider(cfg) is None


def test_returns_none_when_all_empty():
    cfg = _make_config(transport="http")
    assert build_oauth_provider(cfg) is None


def test_returns_provider_when_fully_configured():
    cfg = _make_config(
        transport="http",
        oauth_authorization_endpoint="https://kc.example.com/auth",
        oauth_token_endpoint="https://kc.example.com/token",
        oauth_introspection_endpoint="https://kc.example.com/introspect",
        oauth_client_id="my-client",
        oauth_client_secret="my-secret",
        oauth_base_url="https://rosetta.example.com",
    )
    provider = build_oauth_provider(cfg)
    assert provider is not None


def test_oauth_configured_property():
    cfg = _make_config(
        oauth_authorization_endpoint="https://kc/auth",
        oauth_token_endpoint="https://kc/token",
        oauth_introspection_endpoint="https://kc/introspect",
        oauth_client_id="cid",
        oauth_client_secret="csec",
    )
    assert cfg.oauth_configured is True

    cfg2 = _make_config()
    assert cfg2.oauth_configured is False


def test_resolve_oauth_base_url_from_env():
    cfg = _make_config(oauth_base_url="https://rosetta.example.com/")
    assert cfg.resolve_oauth_base_url() == "https://rosetta.example.com"


def test_resolve_oauth_base_url_from_host_port():
    cfg = _make_config(http_host="0.0.0.0", http_port=9000)
    assert cfg.resolve_oauth_base_url() == "http://0.0.0.0:9000"


def test_uses_callback_path_from_config():
    cfg = _make_config(
        transport="http",
        oauth_authorization_endpoint="https://kc.example.com/auth",
        oauth_token_endpoint="https://kc.example.com/token",
        oauth_introspection_endpoint="https://kc.example.com/introspect",
        oauth_client_id="my-client",
        oauth_client_secret="my-secret",
        oauth_callback_path="oauth/cb",
    )
    provider = build_oauth_provider(cfg)
    assert provider is not None
    assert getattr(provider, "_redirect_path", None) == "/oauth/cb"


def _make_full_http_config(**overrides):
    return _make_config(
        transport="http",
        oauth_authorization_endpoint="https://kc.example.com/auth",
        oauth_token_endpoint="https://kc.example.com/token",
        oauth_introspection_endpoint="https://kc.example.com/introspect",
        oauth_client_id="my-client",
        oauth_client_secret="my-secret",
        **overrides,
    )


def test_extra_authorize_params_set_when_scope_configured():
    cfg = _make_full_http_config(oauth_scope="openid email offline_access")
    provider = build_oauth_provider(cfg)
    assert provider is not None
    params = getattr(provider, "_extra_authorize_params", {})
    assert params.get("scope") == "openid email offline_access"


def test_no_extra_authorize_params_when_scope_empty():
    cfg = _make_full_http_config(oauth_scope="")
    provider = build_oauth_provider(cfg)
    assert provider is not None
    params = getattr(provider, "_extra_authorize_params", {})
    assert "scope" not in params


def test_revocation_endpoint_passed_when_set():
    cfg = _make_full_http_config(oauth_revocation_endpoint="https://kc.example.com/revoke")
    provider = build_oauth_provider(cfg)
    assert provider is not None
    assert getattr(provider, "_upstream_revocation_endpoint", None) == "https://kc.example.com/revoke"


def test_revocation_endpoint_none_when_empty():
    cfg = _make_full_http_config(oauth_revocation_endpoint="")
    provider = build_oauth_provider(cfg)
    assert provider is not None
    assert getattr(provider, "_upstream_revocation_endpoint", None) is None


def test_jwt_signing_key_passed_when_set():
    cfg = _make_full_http_config(oauth_jwt_signing_key="my-independent-secret-key-32chars!!")
    provider = build_oauth_provider(cfg)
    assert provider is not None
    # Key is stored as bytes after derivation; presence is sufficient
    assert getattr(provider, "_jwt_signing_key", None) is not None


def test_jwt_signing_key_derived_from_secret_when_unset():
    cfg_no_key = _make_full_http_config(oauth_jwt_signing_key=None)
    cfg_with_key = _make_full_http_config(oauth_jwt_signing_key="explicit-key")
    p1 = build_oauth_provider(cfg_no_key)
    p2 = build_oauth_provider(cfg_with_key)
    # Keys should differ when explicitly provided vs derived
    assert p1._jwt_signing_key != p2._jwt_signing_key


def test_cached_verifier_used():
    from ims_mcp.auth.introspection_cache import CachedIntrospectionTokenVerifier
    cfg = _make_full_http_config()
    provider = build_oauth_provider(cfg)
    assert provider is not None
    verifier = getattr(provider, "_token_validator", None)
    assert isinstance(verifier, CachedIntrospectionTokenVerifier)
