"""Native Responses preflight must count the checkpoint-pruned wire (#96155)."""

from types import SimpleNamespace

from agent.codex_responses_adapter import estimate_native_responses_preflight_tokens
from agent.model_metadata import estimate_request_tokens_rough
from agent.turn_context import _preflight_request_tokens


def _codex_agent(**over):
    agent = SimpleNamespace(
        api_mode="codex_responses",
        provider="openai-codex",
        model="gpt-5.6",
        base_url="https://chatgpt.com/backend-api/codex",
        _base_url_hostname="chatgpt.com",
        _base_url_lower="https://chatgpt.com/backend-api/codex",
        codex_responses_native_compaction=True,
        compression_enabled=True,
        _codex_reasoning_replay_enabled=True,
        context_compressor=SimpleNamespace(threshold_tokens=765_000),
        tools=None,
    )
    for key, value in over.items():
        setattr(agent, key, value)
    return agent


def _history_with_checkpoint():
    # Pre-checkpoint assistant/tool rows are dropped from the wire; user
    # asks are retained. A durable estimate that counts those dropped rows
    # is what falsely tripped local compression in #96155.
    pre = []
    for i in range(30):
        pre.append({"role": "user", "content": f"ask {i}"})
        pre.append({"role": "assistant", "content": "working " + ("tool output " * 400)})
        pre.append(
            {
                "role": "tool",
                "content": "result " + ("payload " * 400),
                "tool_call_id": f"call-{i}",
            }
        )
    checkpoint_turn = {
        "role": "assistant",
        "content": "checkpointed turn",
        "codex_reasoning_items": [
            {
                "type": "compaction",
                "encrypted_content": "blob",
                "_issuer_kind": "codex_backend",
            }
        ],
    }
    tail = [{"role": "user", "content": "follow-up after checkpoint"}]
    return pre + [checkpoint_turn] + tail


def test_returns_none_when_api_mode_is_not_codex_responses():
    agent = _codex_agent(api_mode="chat_completions")
    assert estimate_native_responses_preflight_tokens(agent, _history_with_checkpoint()) is None


def test_returns_none_when_native_compaction_is_disabled():
    agent = _codex_agent(codex_responses_native_compaction=False)
    assert estimate_native_responses_preflight_tokens(agent, _history_with_checkpoint()) is None




def test_pruned_estimate_is_far_below_durable_transcript():
    agent = _codex_agent()
    messages = _history_with_checkpoint()
    generic = estimate_request_tokens_rough(messages)
    native = estimate_native_responses_preflight_tokens(agent, messages)

    assert native is not None
    assert generic > native * 2
    assert native < 8_000


def test_preflight_wrapper_uses_pruned_estimate_when_eligible():
    agent = _codex_agent()
    messages = _history_with_checkpoint()
    native = estimate_native_responses_preflight_tokens(agent, messages)
    wrapped = _preflight_request_tokens(agent, messages, "")

    assert native is not None
    assert wrapped == native


def test_preflight_wrapper_falls_back_to_generic_when_ineligible():
    agent = _codex_agent(api_mode="chat_completions")
    messages = _history_with_checkpoint()
    generic = estimate_request_tokens_rough(messages)

    assert _preflight_request_tokens(agent, messages, "") == generic


# ── Mid-turn pre-API guard parity (#96995) ────────────────────────────────
# The mid-turn guard in conversation_loop must measure the same pruned wire
# payload the turn-prologue preflight does (#96644/#96155); before #96995 it
# used the generic durable-history estimate and false-tripped 600s local
# compression on compacted native-Codex sessions.


def test_midturn_pressure_uses_pruned_estimate_when_eligible():
    from agent.conversation_loop import _midturn_request_pressure_tokens
    from agent.model_metadata import estimate_messages_tokens_rough

    agent = _codex_agent()
    messages = [{"role": "system", "content": "be brief"}] + _history_with_checkpoint()
    native = estimate_native_responses_preflight_tokens(
        agent, messages, system_prompt="be brief"
    )
    generic = estimate_messages_tokens_rough(messages)

    assert native is not None
    assert generic > native * 2
    # The assembled api_messages carry the system row; the helper must not
    # double-count it (converter skips system rows, system_prompt adds it once).
    assert _midturn_request_pressure_tokens(
        agent, messages, "be brief", generic
    ) == native


def test_midturn_pressure_falls_back_to_generic_plus_tools_when_ineligible():
    from agent.conversation_loop import (
        _estimate_tools_tokens_rough,
        _midturn_request_pressure_tokens,
    )
    from agent.model_metadata import estimate_messages_tokens_rough

    agent = _codex_agent(
        api_mode="chat_completions",
        tools=[{"type": "function", "function": {"name": "t", "parameters": {}}}],
    )
    messages = [{"role": "system", "content": "be brief"}] + _history_with_checkpoint()
    approx = estimate_messages_tokens_rough(messages)

    assert _midturn_request_pressure_tokens(
        agent, messages, "be brief", approx
    ) == approx + _estimate_tools_tokens_rough(agent.tools)
