"""Approval prompt-send TIMEOUT must not trigger the re-ask/fallback lane.

Observed in live relay testing: `send_exec_approval`'s scheduling future can
hit its 15s `.result(timeout=...)` while the card HAS already posted to the
platform — the connector's ack simply arrives after the deadline (slow
platform API call, transient backpressure, event-loop stall). run.py treated
the timeout like a definitive send failure and ran the text fallback, so the
user saw the same approval multiple times; tapping an older card resolved a
prompt whose turn had already moved on ("/approve: nothing pending").

Contract under test (boundary rule — every prompt caller crossing the
send-timeout boundary): concurrent.futures.TimeoutError from the approval
send is AMBIGUOUS (possibly delivered). The gateway must NOT fall back /
re-send; the prompt registration stays live so the user's tap on the
(probably rendered) card still resolves. A definitive error (SendResult
success=False, or a non-timeout exception) keeps today's fallback.
"""

import concurrent.futures
from unittest.mock import MagicMock

import pytest

from gateway.run import _approval_send_outcome


class _Result:
    def __init__(self, success, error=None):
        self.success = success
        self.error = error


def test_timeout_is_ambiguous_not_failure():
    fut = MagicMock()
    fut.result.side_effect = concurrent.futures.TimeoutError()
    outcome = _approval_send_outcome(fut, timeout=0.01)
    assert outcome == "ambiguous", (
        "a send timeout re-ran the fallback — this is the duplicate-approval "
        "re-pop (card posted, ack late); ambiguous must suppress the re-ask"
    )


def test_success_is_sent():
    fut = MagicMock()
    fut.result.return_value = _Result(True)
    assert _approval_send_outcome(fut, timeout=1) == "sent"


def test_definitive_error_result_is_failed():
    fut = MagicMock()
    fut.result.return_value = _Result(False, "relay prompt op unavailable")
    assert _approval_send_outcome(fut, timeout=1) == "failed"


def test_non_timeout_exception_is_failed():
    fut = MagicMock()
    fut.result.side_effect = RuntimeError("loop unavailable")
    assert _approval_send_outcome(fut, timeout=1) == "failed"


def test_missing_future_is_failed():
    assert _approval_send_outcome(None, timeout=1) == "failed"
