"""A prompt is not sent until the user turn proves it was accepted.

The chat app drops a click on an enabled send button while a large attachment is
still being processed. That is how every attachment-bearing run sat in the composer
until its timeout expired with no reply.
"""

from __future__ import annotations

import pytest

import chat
from browser import MarionetteError
from fake_browser import seat_for


@pytest.fixture
def fake(monkeypatch):
    monkeypatch.setattr(chat.time, "sleep", lambda *_: None)
    return seat_for([])[1]


def test_a_swallowed_send_is_clicked_again(fake):
    fake.drop_clicks = 3
    chat.submit(fake)
    assert fake.clicks == 4
    assert fake.user_turns == 1


def test_a_send_that_never_takes_is_an_error_not_a_silent_wait(fake, monkeypatch):
    fake.drop_clicks = 10**6
    clock = iter([0.0, 0.0, 200.0])
    monkeypatch.setattr(chat.time, "monotonic", lambda: next(clock))
    with pytest.raises(chat.ChatError) as caught:
        chat.submit(fake, timeout=120)
    assert "never accepted the prompt" in str(caught.value)


def test_one_click_is_enough_when_the_app_takes_it(fake):
    chat.submit(fake)
    assert fake.clicks == 1


def test_an_unavailable_send_button_is_reported(fake, monkeypatch):
    monkeypatch.setattr(fake, "click", lambda selector: False)
    monkeypatch.setattr(fake, "js_click", lambda selector: False)
    with pytest.raises(chat.ChatError) as caught:
        chat.submit(fake)
    assert "never became available" in str(caught.value)


def test_acceptance_after_two_minutes_still_gets_its_reply(fake, monkeypatch):
    """The original defect: submission outlasting a constant unrelated to the caller."""
    now = [0.0]
    monkeypatch.setattr(chat.time, "monotonic", lambda: now[0])
    monkeypatch.setattr(chat.time, "sleep", lambda seconds: now.__setitem__(0, now[0] + seconds))
    fake.drop_clicks = 40  # ~4 minutes of swallowed sends
    fake.replies.append({"blocks": [], "text": "late but here"})
    chat.send(fake, timeout=900)
    assert now[0] > 120, "the test did not actually cross the old 120s cliff"
    assert fake.user_turns == 1


def test_an_intercepted_click_presses_escape_and_retries(fake, monkeypatch):
    """A full-screen overlay intercepting the click is transient — dismiss it
    (Escape) and keep trying, rather than failing the whole turn."""
    real_click = fake.click
    calls = {"n": 0}

    def flaky_click(selector):
        calls["n"] += 1
        if calls["n"] == 1:
            raise MarionetteError("element click intercepted: some overlay")
        return real_click(selector)
    monkeypatch.setattr(fake, "click", flaky_click)
    pressed = []
    monkeypatch.setattr(fake, "press", lambda key="": pressed.append(key))

    chat.submit(fake)
    assert pressed, "Escape was never pressed to dismiss the overlay"
    assert fake.user_turns == 1


def test_a_non_intercept_click_error_is_not_swallowed(fake, monkeypatch):
    def broken_click(selector):
        raise MarionetteError("the session died")
    monkeypatch.setattr(fake, "click", broken_click)
    monkeypatch.setattr(fake, "js_click", broken_click)
    with pytest.raises(MarionetteError, match="the session died"):
        chat.submit(fake)


def test_a_timeout_names_what_the_page_showed():
    detail = chat.describe({"turns": 0, "imgs": 0, "text": 0, "stop": False})
    assert "reply turns 0" in detail and "stop button gone" in detail
