"""Event-loop liveness for webhook GitHub-comment delivery (Pattern A).

``_deliver_github_comment`` shells out to ``gh`` with a 30s timeout. Before
the off-loop fix it called ``subprocess.run`` inline from an ``async def``,
freezing the entire gateway event loop — every adapter, timer, and health
check — for up to the full timeout while the network call ran.

The contract under test is behavioral, not structural: while delivery is
awaiting the subprocess, OTHER coroutines on the same loop must keep
running.  A ticker coroutine sampled at the moment delivery returns proves
it — on the blocking implementation it manages ~1 tick during a 1-second
``gh`` run; off-loop it manages ~20.
"""

import asyncio
import os
import stat
import sys

import pytest

from gateway.platforms.webhook import WebhookAdapter


@pytest.fixture()
def fake_gh(tmp_path, monkeypatch):
    """A ``gh`` stub that sleeps 1s then succeeds, prepended to PATH."""
    if sys.platform.startswith("win"):
        pytest.skip("POSIX shell stub")
    gh = tmp_path / "gh"
    gh.write_text("#!/usr/bin/env bash\nsleep 1\necho posted\nexit 0\n", encoding="utf-8")
    gh.chmod(gh.stat().st_mode | stat.S_IXUSR)
    monkeypatch.setenv("PATH", f"{tmp_path}{os.pathsep}{os.environ['PATH']}")
    return gh


class TestGithubCommentDeliveryOffLoop:
    @pytest.mark.asyncio
    async def test_loop_stays_live_during_gh_subprocess(self, fake_gh):
        """Concurrent coroutines keep ticking while ``gh`` runs (~1s)."""
        adapter = WebhookAdapter.__new__(WebhookAdapter)

        ticks = []
        stop = False

        async def ticker():
            while not stop:
                ticks.append(1)
                await asyncio.sleep(0.05)

        task = asyncio.create_task(ticker())
        await asyncio.sleep(0)  # let the ticker take its first turn

        result = await adapter._deliver_github_comment(
            "test body",
            {"deliver_extra": {"repo": "owner/repo", "pr_number": "1"}},
        )
        ticks_during_delivery = len(ticks)

        stop = True
        try:
            await asyncio.wait_for(task, timeout=2)
        except asyncio.TimeoutError:
            task.cancel()

        assert result.success is True
        # Blocking implementation: ~1 tick (loop frozen for the 1s gh run).
        # Off-loop implementation: ~20 ticks. 10 is a comfortable midpoint
        # that stays robust on slow CI runners.
        assert ticks_during_delivery >= 10, (
            f"event loop starved during gh delivery: only "
            f"{ticks_during_delivery} ticker turns ran in ~1s"
        )


    @pytest.mark.asyncio
    async def test_invalid_inputs_still_rejected_before_subprocess(self):
        """Validation short-circuits stay synchronous and unchanged."""
        adapter = WebhookAdapter.__new__(WebhookAdapter)
        bad_repo = await adapter._deliver_github_comment(
            "body", {"deliver_extra": {"repo": "not a repo!", "pr_number": "1"}}
        )
        assert bad_repo.success is False
        bad_pr = await adapter._deliver_github_comment(
            "body", {"deliver_extra": {"repo": "owner/repo", "pr_number": "-2"}}
        )
        assert bad_pr.success is False
