"""Weixin adapter secret-scope regression tests.

The adapter's WEIXIN_* credential reads must follow the Slack pattern
(#59739): under multiplexing a SCOPED miss is authoritative (no borrow from
``os.environ`` — that would be a cross-profile leak), while an UNSCOPED read
(default-profile startup/send path) falls back to ``os.environ``, which is
that profile's own value, instead of raising ``UnscopedSecretError``.
"""

import pytest

from agent import secret_scope
from gateway.config import PlatformConfig
from gateway.platforms.weixin import WeixinAdapter, _wx_secret


@pytest.fixture()
def multiplex_on():
    previous = secret_scope.is_multiplex_active()
    secret_scope.set_multiplex_active(True)
    try:
        yield
    finally:
        secret_scope.set_multiplex_active(previous)


class TestWxSecretHelper:
    def test_scoped_read_uses_scope_value(self, multiplex_on, monkeypatch):
        monkeypatch.setenv("WEIXIN_TOKEN", "default-profile-token")
        token = secret_scope.set_secret_scope({"WEIXIN_TOKEN": "scoped-token"})
        try:
            assert _wx_secret("WEIXIN_TOKEN") == "scoped-token"
        finally:
            secret_scope.reset_secret_scope(token)

    def test_scoped_miss_does_not_borrow_environ(self, multiplex_on, monkeypatch):
        """A secondary profile without WEIXIN_TOKEN must NOT inherit the
        default profile's process-env token."""
        monkeypatch.setenv("WEIXIN_TOKEN", "default-profile-token")
        token = secret_scope.set_secret_scope({"OTHER_KEY": "x"})
        try:
            assert _wx_secret("WEIXIN_TOKEN", "") == ""
            assert _wx_secret("WEIXIN_TOKEN") is None
        finally:
            secret_scope.reset_secret_scope(token)

    def test_unscoped_read_falls_back_to_environ(self, multiplex_on, monkeypatch):
        """The default profile's adapter runs unscoped under multiplexing;
        os.environ is its own value — fall back instead of raising."""
        monkeypatch.setenv("WEIXIN_TOKEN", "default-profile-token")
        token = secret_scope.set_secret_scope(None)
        try:
            assert _wx_secret("WEIXIN_TOKEN") == "default-profile-token"
        finally:
            secret_scope.reset_secret_scope(token)


class TestWeixinAdapterConstructionScope:
    def test_multiplex_scoped_construction_reads_scope_not_environ(
        self, multiplex_on, monkeypatch
    ):
        monkeypatch.setenv("WEIXIN_ACCOUNT_ID", "env-account")
        monkeypatch.setenv("WEIXIN_TOKEN", "env-token")
        token = secret_scope.set_secret_scope(
            {
                "WEIXIN_ACCOUNT_ID": "scoped-account",
                "WEIXIN_TOKEN": "scoped-token",
            }
        )
        try:
            adapter = WeixinAdapter(PlatformConfig(enabled=True))
        finally:
            secret_scope.reset_secret_scope(token)
        assert adapter._account_id == "scoped-account"
        assert adapter._token == "scoped-token"

    def test_multiplex_scoped_miss_yields_empty_not_environ_borrow(
        self, multiplex_on, monkeypatch
    ):
        monkeypatch.setenv("WEIXIN_ACCOUNT_ID", "env-account")
        monkeypatch.setenv("WEIXIN_TOKEN", "env-token")
        token = secret_scope.set_secret_scope({"SOMETHING_ELSE": "x"})
        try:
            adapter = WeixinAdapter(PlatformConfig(enabled=True))
        finally:
            secret_scope.reset_secret_scope(token)
        assert adapter._account_id == ""
        assert adapter._token == ""

    def test_multiplex_unscoped_construction_falls_back_to_environ(
        self, multiplex_on, monkeypatch
    ):
        """Regression for the bare get_secret reads: default-profile adapter
        construction under multiplexing must not raise UnscopedSecretError."""
        monkeypatch.setenv("WEIXIN_ACCOUNT_ID", "env-account")
        monkeypatch.setenv("WEIXIN_TOKEN", "env-token")
        token = secret_scope.set_secret_scope(None)
        try:
            adapter = WeixinAdapter(PlatformConfig(enabled=True))
        finally:
            secret_scope.reset_secret_scope(token)
        assert adapter._account_id == "env-account"
        assert adapter._token == "env-token"


class TestWeixinAdapterAuthzScope:
    """Authorization reads (dm_policy/allowlists) must follow the same
    scoped-secret rules as the credential reads above (#93522): a secondary
    profile's own scope is authoritative, and the default profile's
    process-env values must not leak into it."""

    def test_scoped_construction_reads_authz_from_scope_not_environ(
        self, multiplex_on, monkeypatch
    ):
        monkeypatch.setenv("WEIXIN_DM_POLICY", "pairing")
        monkeypatch.setenv("WEIXIN_ALLOWED_USERS", "default-user")
        monkeypatch.setenv("WEIXIN_GROUP_ALLOWED_USERS", "default-group-user")
        token = secret_scope.set_secret_scope(
            {
                "WEIXIN_DM_POLICY": "allowlist",
                "WEIXIN_ALLOWED_USERS": "scoped-user",
                "WEIXIN_GROUP_ALLOWED_USERS": "scoped-group-user",
            }
        )
        try:
            adapter = WeixinAdapter(PlatformConfig(enabled=True))
        finally:
            secret_scope.reset_secret_scope(token)
        assert adapter._dm_policy == "allowlist"
        assert adapter._allow_from == ["scoped-user"]
        assert adapter._group_allow_from == ["scoped-group-user"]
        assert adapter._is_dm_allowed("scoped-user") is True
        assert adapter._is_dm_allowed("default-user") is False

    def test_scoped_miss_does_not_admit_default_profiles_allow_all(
        self, multiplex_on, monkeypatch
    ):
        """A secondary profile with no allowlist of its own must fail closed,
        not inherit the default profile's env-only allowlist/opt-in."""
        monkeypatch.setenv("WEIXIN_DM_POLICY", "allowlist")
        monkeypatch.setenv("WEIXIN_ALLOWED_USERS", "default-user")
        token = secret_scope.set_secret_scope({"SOMETHING_ELSE": "x"})
        try:
            adapter = WeixinAdapter(PlatformConfig(enabled=True))
        finally:
            secret_scope.reset_secret_scope(token)
        assert adapter._dm_policy == "pairing"
        assert adapter._allow_from == []
        assert adapter._is_dm_allowed("default-user") is False


class TestWeixinAdapterSplitMultilineScope:
    """``split_multiline_messages`` must follow the same scoped-secret rules
    as every other WEIXIN_* tunable in this block (missed by the
    scoped-reader retrofit): a secondary profile's own scope is
    authoritative, and the default profile's process-env value must not
    leak into it."""

    def test_scoped_construction_reads_split_multiline_from_scope_not_environ(
        self, multiplex_on, monkeypatch
    ):
        monkeypatch.setenv("WEIXIN_SPLIT_MULTILINE_MESSAGES", "false")
        token = secret_scope.set_secret_scope(
            {"WEIXIN_SPLIT_MULTILINE_MESSAGES": "true"}
        )
        try:
            adapter = WeixinAdapter(PlatformConfig(enabled=True))
        finally:
            secret_scope.reset_secret_scope(token)
        assert adapter._split_multiline_messages is True

    def test_scoped_miss_does_not_borrow_default_profiles_split_multiline(
        self, multiplex_on, monkeypatch
    ):
        """A secondary profile with no split_multiline_messages of its own
        must fall back to the coded default, not inherit the default
        profile's env-only value."""
        monkeypatch.setenv("WEIXIN_SPLIT_MULTILINE_MESSAGES", "true")
        token = secret_scope.set_secret_scope({"SOMETHING_ELSE": "x"})
        try:
            adapter = WeixinAdapter(PlatformConfig(enabled=True))
        finally:
            secret_scope.reset_secret_scope(token)
        assert adapter._split_multiline_messages is False
