from __future__ import annotations

import json
import subprocess
from datetime import datetime, timedelta
from pathlib import Path

from recoil.pipeline.tools.autonomy import preflight


REPO = Path(__file__).resolve().parents[4]


class StubRunner:
    def __init__(self, *, dirty="", branch="main", who="", tmux="", observe=None, raise_on=None):
        self.dirty = dirty
        self.branch = branch
        self.who = who
        self.tmux = tmux
        self.observe = observe or {
            "same_host_checkout_conflict": False,
            "mutating_shared_checkout": False,
        }
        self.raise_on = raise_on

    def __call__(self, args, **kwargs):
        command = " ".join(args)
        if self.raise_on and self.raise_on in command:
            raise RuntimeError(f"boom: {self.raise_on}")

        if args[:3] == ["git", "-C", str(REPO)] and args[3:] == [
            "status",
            "--porcelain",
        ]:
            return _completed(args, stdout=self.dirty)
        if args[:3] == ["git", "-C", str(REPO)] and args[3:] == [
            "rev-parse",
            "--abbrev-ref",
            "HEAD",
        ]:
            return _completed(args, stdout=f"{self.branch}\n")
        if args == ["who"]:
            return _completed(args, stdout=self.who)
        if args == ["tmux", "ls"]:
            if self.tmux == "__no_server__":
                return _completed(args, returncode=1, stderr="no server running\n")
            return _completed(args, stdout=self.tmux)
        if args[-2:] == ["observe", "--json"]:
            return _completed(args, stdout=json.dumps(self.observe))
        raise AssertionError(f"unexpected command: {args!r}")


def _completed(args, *, stdout="", stderr="", returncode=0):
    return subprocess.CompletedProcess(args, returncode, stdout=stdout, stderr=stderr)


def test_preflight_missing_linear_api_key(monkeypatch):
    monkeypatch.setattr(preflight.shutil, "which", lambda name: f"/usr/bin/{name}")
    monkeypatch.setattr(preflight.Path, "exists", lambda self: True)
    monkeypatch.setattr(preflight.os, "access", lambda path, mode: True)
    monkeypatch.setattr(preflight.constants, "ensure_state_dir", lambda: Path("/tmp/state"))

    ok, reason = preflight.preflight(
        environ={"AUTONOMY_LINEAR_TEAM": "Recoil"},
        hostname="Joes-Mac-Studio-70960.local",
    )

    assert ok is False
    assert reason == "missing LINEAR_API_KEY"


def test_human_active_dirty_tree():
    active, reason = preflight.human_active(REPO, runner=StubRunner(dirty=" M file.py\n"))

    assert active is True
    assert reason == "canonical_dirty"


def test_human_active_off_main():
    active, reason = preflight.human_active(REPO, runner=StubRunner(branch="feature/x"))

    assert active is True
    assert reason == "canonical_off_main"


def test_human_active_recent_login():
    now = datetime(2026, 6, 7, 12, 0)
    active, reason = preflight.human_active(
        REPO,
        runner=StubRunner(who="jt console 2026-06-07 11:55\n"),
        now=now,
    )

    assert active is True
    assert reason == "recent_login"


def test_human_active_non_autonomy_tmux_session():
    active, reason = preflight.human_active(
        REPO,
        runner=StubRunner(tmux="work: 1 windows (created Sun Jun 7 12:00:00 2026)\n"),
    )

    assert active is True
    assert reason == "tmux_active"


def test_human_active_workspace_conflict():
    active, reason = preflight.human_active(
        REPO,
        runner=StubRunner(observe={"mutating_shared_checkout": True}),
    )

    assert active is True
    assert reason == "workspace_conflict"


def test_human_active_clean_main_no_login_no_tmux():
    old_login = datetime(2026, 6, 7, 12, 0) - timedelta(hours=1)
    runner = StubRunner(
        who=f"jt console {old_login:%Y-%m-%d %H:%M}\n",
        tmux="autonomy-tick: 1 windows (created Sun Jun 7 12:00:00 2026)\n",
    )

    active, reason = preflight.human_active(
        REPO,
        runner=runner,
        now=datetime(2026, 6, 7, 12, 0),
    )

    assert active is False
    assert reason == "idle"


def test_human_active_no_tmux_server_is_idle():
    active, reason = preflight.human_active(
        REPO,
        runner=StubRunner(tmux="__no_server__"),
        now=datetime(2026, 6, 7, 12, 0),
    )

    assert active is False
    assert reason == "idle"


def test_human_active_runner_exception_fails_safe():
    active, reason = preflight.human_active(
        REPO,
        runner=StubRunner(raise_on="status --porcelain"),
    )

    assert active is True
    assert reason == "git_status_error"
