"""Taking a change back out — the thing a person needs most when they can't read the code."""
import subprocess
from pathlib import Path

import pytest

from scout import build as b
from scout.build import undo_idea
from scout.projects import Project


def git(repo: Path, *args: str) -> str:
    return subprocess.run(["git", "-C", str(repo), *args], capture_output=True, text=True,
                          check=True).stdout.strip()


def make_repo(tmp_path: Path) -> Path:
    repo = tmp_path / "repo"
    repo.mkdir()
    subprocess.run(["git", "init", "-q", "-b", "main", str(repo)], check=True)
    git(repo, "config", "user.email", "t@t")
    git(repo, "config", "user.name", "t")
    (repo / "README.md").write_text("hello\n")
    git(repo, "add", ".")
    git(repo, "commit", "-q", "-m", "init")
    return repo


def a_project(repo: Path) -> Project:
    return Project(key="terminal", name="Test App", folder=repo, test_command="true")


def ship_something(repo: Path, store, name="feature.txt") -> tuple[int, str]:
    """Put a change into the project the way a completed build does, and note where things stood."""
    before = git(repo, "rev-parse", "HEAD")
    iid = store.add_idea("terminal", "A feature", "s", evidence=[{"url": "https://e", "note": ""}])
    store.set_status(iid, "built", build_branch="scout/1-a-feature", merge_base_sha=before)
    (repo / name).write_text("new thing\n")
    git(repo, "add", ".")
    git(repo, "commit", "-q", "-m", "Merge scout/1-a-feature (Feature Scout #1)")
    store.set_status(iid, "shipped")
    return iid, before


@pytest.fixture(autouse=True)
def scratch_dirs(tmp_path, monkeypatch):
    monkeypatch.setattr(b, "WORKTREE_DIR", tmp_path / "wt")
    monkeypatch.setattr(b, "LOG_DIR", tmp_path / "logs")


def test_a_change_can_be_taken_straight_back_out(tmp_path, store):
    repo = make_repo(tmp_path)
    iid, before = ship_something(repo, store)

    result = undo_idea(a_project(repo), store.idea(iid), store)

    assert result["ok"] and "taken back out" in result["message"]
    assert "back exactly as it was" in result["message"]
    assert git(repo, "rev-parse", "HEAD") == before
    assert not (repo / "feature.txt").exists()
    assert store.idea(iid)["status"] == "rejected"


def test_work_done_since_is_kept(tmp_path, store):
    repo = make_repo(tmp_path)
    iid, before = ship_something(repo, store)
    (repo / "mine.txt").write_text("my own work\n")
    git(repo, "add", ".")
    git(repo, "commit", "-q", "-m", "something I did myself")

    result = undo_idea(a_project(repo), store.idea(iid), store)

    assert result["ok"] and "untouched" in result["message"]
    assert (repo / "mine.txt").exists(), "the person's own work was destroyed"
    assert not (repo / "feature.txt").exists(), "the change was not actually taken out"
    assert git(repo, "rev-parse", "HEAD") != before


def test_nothing_is_touched_when_there_are_unsaved_changes(tmp_path, store):
    repo = make_repo(tmp_path)
    iid, _ = ship_something(repo, store)
    (repo / "README.md").write_text("half-finished edit\n")

    result = undo_idea(a_project(repo), store.idea(iid), store)

    assert not result["ok"] and "unsaved changes" in result["message"]
    assert (repo / "README.md").read_text() == "half-finished edit\n"
    assert store.idea(iid)["status"] == "shipped"


def test_something_that_was_never_added_cannot_be_taken_back(tmp_path, store):
    repo = make_repo(tmp_path)
    iid = store.add_idea("terminal", "Not built", "s", evidence=[{"url": "https://e", "note": ""}])
    result = undo_idea(a_project(repo), store.idea(iid), store)
    assert not result["ok"] and "nothing to take back" in result["message"]


def test_a_build_records_where_things_stood_before_it_was_added(tmp_path, store):
    """Without this, there is nothing to go back to."""
    repo = make_repo(tmp_path)
    before = git(repo, "rev-parse", "HEAD")
    iid = store.add_idea("terminal", "A feature", "s", evidence=[{"url": "https://e", "note": ""}])
    git(repo, "checkout", "-q", "-b", "scout/1-a-feature")
    (repo / "feature.txt").write_text("x\n")
    git(repo, "add", ".")
    git(repo, "commit", "-q", "-m", "built")
    git(repo, "checkout", "-q", "main")
    store.set_status(iid, "built", build_branch="scout/1-a-feature")

    b.ship_idea(a_project(repo), store.idea(iid), store, tester=lambda p, c: (True, "ok"))

    assert store.idea(iid)["merge_base_sha"] == before
    assert store.idea(iid)["status"] == "shipped"
    assert undo_idea(a_project(repo), store.idea(iid), store)["ok"]
    assert git(repo, "rev-parse", "HEAD") == before
