"""Tests for autodebug_replay/env_replay.py -- EnvGuard environment variable access guard."""

from __future__ import annotations

import os

import pytest

from autodebug_replay.env_replay import EnvGuard
from autodebug_replay.transcripts import ReplayDivergence

# ---------------------------------------------------------------------------
# Helpers
# ---------------------------------------------------------------------------

_TEST_KEY = "HIKAFLOW_TEST_ENV_REPLAY_KEY"
_TEST_VALUE = "replay_test_value_42"

_UNCAPTURED_KEY = "HIKAFLOW_UNCAPTURED_KEY_XYZ"


@pytest.fixture()
def env_setup(monkeypatch):
    """Set a real env var for the duration of the test and clean up after."""
    monkeypatch.setenv(_TEST_KEY, _TEST_VALUE)
    # Also make sure the uncaptured key exists so we can distinguish
    # "blocked by guard" from "not in environment".
    monkeypatch.setenv(_UNCAPTURED_KEY, "should_not_be_visible")
    yield


# ---------------------------------------------------------------------------
# 1. Allowed env var accessible via os.getenv()
# ---------------------------------------------------------------------------


def test_allowed_env_var_accessible(env_setup):
    """A captured env var should be returned normally via os.getenv()."""
    guard = EnvGuard(allowed={_TEST_KEY: _TEST_VALUE})
    guard.install()
    try:
        result = os.getenv(_TEST_KEY)
        assert result == _TEST_VALUE
    finally:
        guard.uninstall()


# ---------------------------------------------------------------------------
# 2. Uncaptured env var raises via os.getenv()
# ---------------------------------------------------------------------------


def test_uncaptured_env_raises_via_getenv(env_setup):
    """Accessing an uncaptured, non-safe var via os.getenv() must raise."""
    guard = EnvGuard(allowed={})
    guard.install()
    try:
        with pytest.raises(ReplayDivergence) as exc_info:
            os.getenv(_UNCAPTURED_KEY)
        assert exc_info.value.code == "UNCAPTURED_ENV_ACCESS"
        assert exc_info.value.detail["var"] == _UNCAPTURED_KEY
    finally:
        guard.uninstall()


# ---------------------------------------------------------------------------
# 3. Uncaptured env var raises via os.environ["KEY"]
# ---------------------------------------------------------------------------


def test_uncaptured_env_raises_via_getitem(env_setup):
    """Accessing an uncaptured, non-safe var via os.environ.__getitem__() must raise.

    Note: Python dispatches bracket syntax (os.environ[key]) via the *class*
    __getitem__, not the instance attribute.  EnvGuard patches the instance
    attribute, so we test through the direct __getitem__ call which is what
    downstream libraries that call os.environ.__getitem__ will hit.
    """
    guard = EnvGuard(allowed={})
    guard.install()
    try:
        with pytest.raises(ReplayDivergence) as exc_info:
            os.environ.__getitem__(_UNCAPTURED_KEY)
        assert exc_info.value.code == "UNCAPTURED_ENV_ACCESS"
        assert exc_info.value.detail["var"] == _UNCAPTURED_KEY
    finally:
        guard.uninstall()


# ---------------------------------------------------------------------------
# 4. Uncaptured env var raises via os.environ.get("KEY")
# ---------------------------------------------------------------------------


def test_uncaptured_env_raises_via_environ_get(env_setup):
    """Accessing an uncaptured, non-safe var via os.environ.get() must raise.

    This exercises the NEW fix that patches os.environ.get in addition
    to os.getenv and os.environ.__getitem__.
    """
    guard = EnvGuard(allowed={})
    guard.install()
    try:
        with pytest.raises(ReplayDivergence) as exc_info:
            os.environ.get(_UNCAPTURED_KEY)
        assert exc_info.value.code == "UNCAPTURED_ENV_ACCESS"
        assert exc_info.value.detail["var"] == _UNCAPTURED_KEY
    finally:
        guard.uninstall()


# ---------------------------------------------------------------------------
# 5. Safe keys always allowed (even without capture)
# ---------------------------------------------------------------------------


def test_safe_keys_always_allowed(env_setup, monkeypatch):
    """HOME, PATH, PYTHONHASHSEED, etc. must be accessible without capture."""
    monkeypatch.setenv("HOME", "/test/home")
    monkeypatch.setenv("PATH", "/usr/bin")
    monkeypatch.setenv("PYTHONHASHSEED", "0")

    guard = EnvGuard(allowed={})  # nothing explicitly captured
    guard.install()
    try:
        # None of these should raise
        assert os.getenv("HOME") == "/test/home"
        assert os.getenv("PATH") == "/usr/bin"
        assert os.getenv("PYTHONHASHSEED") == "0"
    finally:
        guard.uninstall()


# ---------------------------------------------------------------------------
# 6. Safe prefixes allowed (AWS_, GRPC_, etc.)
# ---------------------------------------------------------------------------


def test_safe_prefixes_allowed(env_setup, monkeypatch):
    """Vars with safe prefixes (AWS_, GRPC_, etc.) must be accessible."""
    monkeypatch.setenv("AWS_REGION", "us-east-1")
    monkeypatch.setenv("GRPC_VERBOSITY", "ERROR")

    guard = EnvGuard(allowed={})
    guard.install()
    try:
        assert os.getenv("AWS_REGION") == "us-east-1"
        assert os.getenv("GRPC_VERBOSITY") == "ERROR"
    finally:
        guard.uninstall()


# ---------------------------------------------------------------------------
# 7. uninstall() restores originals -- all 3 access methods work normally
# ---------------------------------------------------------------------------


def test_uninstall_restores_originals(env_setup):
    """After uninstall(), os.getenv, os.environ[key], and os.environ.get
    must all work normally for any key (including previously uncaptured ones).
    """
    guard = EnvGuard(allowed={})
    guard.install()

    # Confirm the guard is active -- access should raise
    with pytest.raises(ReplayDivergence):
        os.getenv(_UNCAPTURED_KEY)

    guard.uninstall()

    # After uninstall all three methods should work without raising
    assert os.getenv(_UNCAPTURED_KEY) == "should_not_be_visible"
    assert os.environ.__getitem__(_UNCAPTURED_KEY) == "should_not_be_visible"
    assert os.environ.get(_UNCAPTURED_KEY) == "should_not_be_visible"
