"""Contract of the supervised launch: capture shape, exit passthrough, fail-open."""

from __future__ import annotations

import os
import sys
from pathlib import Path

import pytest

sys.path.insert(0, str(Path(__file__).resolve().parent.parent))

import stall_supervisor


def test_captures_merged_output_and_passes_exit_through(tmp_path: Path) -> None:
    log = tmp_path / "run.log"
    code = stall_supervisor.supervise(
        [sys.executable, "-c", "import sys; print('out'); print('err', file=sys.stderr); sys.exit(5)"],
        dict(os.environ),
        log,
    )
    assert code == 5
    # order is a stdio-buffering race (stdout block-buffered to a file, stderr not);
    # the contract is that BOTH streams land in the one log
    assert sorted(log.read_text().splitlines()) == ["err", "out"]


def test_child_gets_no_stdin(tmp_path: Path) -> None:
    log = tmp_path / "run.log"
    code = stall_supervisor.supervise(
        [sys.executable, "-c", "import sys; sys.exit(0 if sys.stdin.read() == '' else 1)"],
        dict(os.environ),
        log,
    )
    assert code == 0


def test_child_runs_in_its_own_session(tmp_path: Path) -> None:
    log = tmp_path / "run.log"
    stall_supervisor.supervise(
        [sys.executable, "-c", "import os; print(os.getpid() == os.getpgrp())"],
        dict(os.environ),
        log,
    )
    assert log.read_text().strip() == "True"


def test_env_is_the_one_supplied(tmp_path: Path) -> None:
    log = tmp_path / "run.log"
    env = dict(os.environ, SUPERVISOR_PROBE="marker")
    stall_supervisor.supervise(
        [sys.executable, "-c", "import os; print(os.environ['SUPERVISOR_PROBE'])"], env, log
    )
    assert log.read_text().strip() == "marker"


def test_detector_is_found_relative_to_this_checkout() -> None:
    # a $HOME-anchored path leaves every other checkout (CI runner, worktree)
    # silently unguarded, because a missing detector fails open
    assert stall_supervisor.STALL_GUARD.exists(), stall_supervisor.STALL_GUARD
    assert stall_supervisor.STALL_GUARD.is_relative_to(
        Path(stall_supervisor.__file__).resolve().parents[2]
    )


def test_fails_open_when_the_detector_cannot_be_loaded(
    tmp_path: Path, monkeypatch: pytest.MonkeyPatch, capsys: pytest.CaptureFixture[str]
) -> None:
    monkeypatch.setattr(stall_supervisor, "STALL_GUARD", tmp_path / "absent")
    log = tmp_path / "run.log"
    code = stall_supervisor.supervise([sys.executable, "-c", "print('ran')"], dict(os.environ), log)
    assert code == 0
    assert log.read_text().strip() == "ran"
    assert "UNGUARDED" in capsys.readouterr().err


def test_stall_kills_the_child_and_reports_the_stall_exit(
    tmp_path: Path, monkeypatch: pytest.MonkeyPatch, capsys: pytest.CaptureFixture[str]
) -> None:
    monkeypatch.setenv("CDX_STALL_IDLE_WINDOW", "0.1")
    monkeypatch.setattr(stall_supervisor, "SAMPLE_INTERVAL", 0.05)
    log = tmp_path / "run.log"
    code = stall_supervisor.supervise(
        [sys.executable, "-c", "import time; time.sleep(60)"], dict(os.environ), log, key="probe"
    )
    assert code == stall_supervisor.STALL_EXIT
    err = capsys.readouterr().err
    assert "STALL" in err
    assert "desktop notification suppressed" in err, (
        "the gate is off; this suite is popping the session"
    )


def test_output_ceiling_catches_a_run_that_only_burns_cpu(
    tmp_path: Path, monkeypatch: pytest.MonkeyPatch, capsys: pytest.CaptureFixture[str]
) -> None:
    # every-signal-flat can never fire here: cpu climbs forever while nothing prints
    monkeypatch.setenv("CDX_STALL_IDLE_WINDOW", "3600")
    monkeypatch.setenv("CDX_STALL_OUTPUT_CEILING", "0.5")
    monkeypatch.setattr(stall_supervisor, "SAMPLE_INTERVAL", 0.05)
    log = tmp_path / "run.log"
    code = stall_supervisor.supervise(
        [sys.executable, "-c", "\nwhile True:\n    pass\n"], dict(os.environ), log, key="probe"
    )
    assert code == stall_supervisor.STALL_EXIT
    assert "no output for" in capsys.readouterr().err


def test_slow_run_still_making_progress_is_warned_not_killed(
    tmp_path: Path, monkeypatch: pytest.MonkeyPatch, capsys: pytest.CaptureFixture[str]
) -> None:
    monkeypatch.setenv("CDX_STALL_IDLE_WINDOW", "3600")
    monkeypatch.setenv("CDX_STALL_WALL_NOTICE", "0.3")
    monkeypatch.setattr(stall_supervisor, "SAMPLE_INTERVAL", 0.05)
    log = tmp_path / "run.log"
    code = stall_supervisor.supervise(
        [sys.executable, "-c",
         "\nimport time\nfor _ in range(20):\n    print('waiting', flush=True)\n    time.sleep(0.05)\n"],
        dict(os.environ), log, key="probe",
    )
    assert code == 0
    err = capsys.readouterr().err
    assert err.count("cdx: SLOW") == 1
    assert "STALL" not in err


def test_observe_mode_reports_without_killing(
    tmp_path: Path, monkeypatch: pytest.MonkeyPatch, capsys: pytest.CaptureFixture[str]
) -> None:
    monkeypatch.setenv("CDX_STALL_IDLE_WINDOW", "0.1")
    monkeypatch.setenv("CDX_STALL_ENFORCE", "0")
    monkeypatch.setattr(stall_supervisor, "SAMPLE_INTERVAL", 0.05)
    log = tmp_path / "run.log"
    code = stall_supervisor.supervise(
        [sys.executable, "-c", "import time; time.sleep(1)"], dict(os.environ), log, key="probe"
    )
    assert code == 0
    assert "observe mode" in capsys.readouterr().err
