from __future__ import annotations

import os
from pathlib import Path

import pytest

import retire_runtime_compat_link as retire_mod


@pytest.fixture
def link_and_target(tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> tuple[Path, Path]:
    target = tmp_path / "runtime"
    target.mkdir()
    link = tmp_path / ".systray-ai"
    link.symlink_to(target)
    monkeypatch.setenv("SYSTRAY_RUNTIME_DIR", str(target))
    monkeypatch.setenv("SYSTRAY_RUNTIME_COMPAT_LINK", str(link))
    return link, target


def _proc_root(tmp_path: Path, cmdlines: dict[int, str]) -> Path:
    root = tmp_path / "proc"
    root.mkdir()
    (root / "not-a-pid").mkdir()
    for pid, cmdline in cmdlines.items():
        entry = root / str(pid)
        entry.mkdir()
        (entry / "cmdline").write_bytes(cmdline.replace(" ", "\0").encode())
    return root


def test_retires_the_link_when_nothing_references_it(
    link_and_target: tuple[Path, Path], tmp_path: Path
) -> None:
    link, target = link_and_target
    calls: list[str] = []
    result = retire_mod.retire(
        proc_root=_proc_root(tmp_path, {4242: "/usr/bin/python3 unrelated.py"}),
        disable_timer=lambda: calls.append("disabled"),
    )
    assert result == "retired"
    assert not link.exists()
    assert target.is_dir()
    assert calls == ["disabled"]


def test_keeps_the_link_while_a_process_still_names_it(
    link_and_target: tuple[Path, Path], tmp_path: Path
) -> None:
    link, _ = link_and_target
    calls: list[str] = []
    proc_root = _proc_root(tmp_path, {4242: f"claude --settings {link}/settings.json"})
    result = retire_mod.retire(proc_root=proc_root, disable_timer=lambda: calls.append("disabled"))
    assert result == "held:1"
    assert link.is_symlink()
    assert calls == []


def test_ignores_its_own_process(link_and_target: tuple[Path, Path], tmp_path: Path) -> None:
    link, _ = link_and_target
    proc_root = _proc_root(tmp_path, {os.getpid(): f"python3 retire {link}"})
    assert retire_mod.retire(proc_root=proc_root, disable_timer=lambda: None) == "retired"


def test_leaves_a_link_pointing_somewhere_else_alone(
    link_and_target: tuple[Path, Path], tmp_path: Path, monkeypatch: pytest.MonkeyPatch
) -> None:
    link, _ = link_and_target
    elsewhere = tmp_path / "elsewhere"
    elsewhere.mkdir()
    monkeypatch.setenv("SYSTRAY_RUNTIME_DIR", str(elsewhere))
    calls: list[str] = []
    result = retire_mod.retire(
        proc_root=_proc_root(tmp_path, {}), disable_timer=lambda: calls.append("disabled")
    )
    assert result == "foreign-target"
    assert link.is_symlink()
    assert calls == []


def test_reports_absent_and_stops_the_timer_when_already_retired(
    link_and_target: tuple[Path, Path], tmp_path: Path
) -> None:
    link, _ = link_and_target
    link.unlink()
    calls: list[str] = []
    result = retire_mod.retire(
        proc_root=_proc_root(tmp_path, {}), disable_timer=lambda: calls.append("disabled")
    )
    assert result == "absent"
    assert calls == ["disabled"]


def test_unreadable_proc_is_treated_as_held(
    link_and_target: tuple[Path, Path], tmp_path: Path
) -> None:
    link, _ = link_and_target
    result = retire_mod.retire(proc_root=tmp_path / "missing-proc", disable_timer=lambda: None)
    assert result == "held:1"
    assert link.is_symlink()


def test_main_rejects_arguments() -> None:
    assert retire_mod.main(["retire", "--now"]) == 2
