"""Tests for src/git_worktree (ORCH-2 / S-4): isolated worktree per task/branch.

Uses real local git repos in tmp (a bare 'origin' + a working main clone) so that
`git fetch origin`, `git worktree add`, branch creation from origin/main, reuse and
removal are all exercised without network access.
"""
import os
import subprocess
import tempfile

import pytest

# Env must be set before importing app modules (same convention as the other suites).
_test_db = os.path.join(tempfile.gettempdir(), "test_orchestrator_wt.db")
os.environ["ORCH_DB_PATH"] = _test_db
os.environ["ORCH_REPOS_DIR"] = tempfile.gettempdir()
os.environ["ORCH_GITEA_TOKEN"] = "test-token"
os.environ["ORCH_PLANE_API_TOKEN"] = "test-token"

from src import git_worktree
from src.git_worktree import (
    _safe,
    get_worktree_path,
    ensure_worktree,
    remove_worktree,
)


def _git(cwd, *args):
    return subprocess.run(["git", "-C", cwd, *args], capture_output=True, text=True)


@pytest.fixture
def repos(tmp_path, monkeypatch):
    """Build a bare 'origin' with main + a feature branch, plus a main clone at repos_dir/<repo>.

    Returns the repo name. settings.repos_dir / worktrees_dir are pointed at tmp.
    """
    repo = "enduro-trails"
    repos_dir = tmp_path / "repos"
    wt_dir = tmp_path / "repos" / "_wt"
    repos_dir.mkdir(parents=True)

    monkeypatch.setattr(git_worktree.settings, "repos_dir", str(repos_dir))
    monkeypatch.setattr(git_worktree.settings, "worktrees_dir", str(wt_dir))

    # Bare origin
    origin = tmp_path / "origin.git"
    subprocess.run(["git", "init", "--bare", "-b", "main", str(origin)], capture_output=True)

    # Seed repo
    seed = tmp_path / "seed"
    seed.mkdir()
    _git(str(seed), "init", "-b", "main")
    _git(str(seed), "config", "user.email", "t@t")
    _git(str(seed), "config", "user.name", "t")
    (seed / "README.md").write_text("# seed\n")
    _git(str(seed), "add", ".")
    _git(str(seed), "commit", "-m", "init")
    _git(str(seed), "remote", "add", "origin", str(origin))
    _git(str(seed), "push", "origin", "main")
    # An existing feature branch on origin
    _git(str(seed), "checkout", "-b", "feature/existing")
    (seed / "f.txt").write_text("feature\n")
    _git(str(seed), "add", ".")
    _git(str(seed), "commit", "-m", "feat")
    _git(str(seed), "push", "origin", "feature/existing")

    # Main clone at repos_dir/<repo>
    main_clone = repos_dir / repo
    subprocess.run(["git", "clone", str(origin), str(main_clone)], capture_output=True)
    _git(str(main_clone), "config", "user.email", "t@t")
    _git(str(main_clone), "config", "user.name", "t")
    return repo


# ---------------------------------------------------------------------------
# _safe / get_worktree_path
# ---------------------------------------------------------------------------
class TestSafeAndPath:
    def test_safe_replaces_slashes_and_specials(self):
        assert _safe("feature/ET-001-x") == "feature_ET-001-x"
        assert _safe("a b/c:d") == "a_b_c_d"
        assert _safe("keep.dots-and_underscores") == "keep.dots-and_underscores"

    def test_get_worktree_path(self, monkeypatch):
        monkeypatch.setattr(git_worktree.settings, "worktrees_dir", "/repos/_wt")
        assert get_worktree_path("repo", "feature/x") == "/repos/_wt/repo/feature_x"


# ---------------------------------------------------------------------------
# ensure_worktree
# ---------------------------------------------------------------------------
class TestEnsureWorktree:
    def test_missing_main_repo_raises(self, tmp_path, monkeypatch):
        monkeypatch.setattr(git_worktree.settings, "repos_dir", str(tmp_path / "nope"))
        monkeypatch.setattr(git_worktree.settings, "worktrees_dir", str(tmp_path / "_wt"))
        with pytest.raises(FileNotFoundError):
            ensure_worktree("enduro-trails", "main")

    def test_creates_worktree_for_existing_branch(self, repos):
        wt = ensure_worktree(repos, "feature/existing")
        assert os.path.isdir(wt)
        assert wt == get_worktree_path(repos, "feature/existing")
        # On the right branch
        cur = _git(wt, "branch", "--show-current").stdout.strip()
        assert cur == "feature/existing"
        # Feature file from that branch is present (proves correct checkout)
        assert os.path.isfile(os.path.join(wt, "f.txt"))

    def test_creates_new_branch_from_origin_main(self, repos):
        wt = ensure_worktree(repos, "feature/brand-new")
        assert os.path.isdir(wt)
        cur = _git(wt, "branch", "--show-current").stdout.strip()
        assert cur == "feature/brand-new"
        # Based on main -> README present, no feature file
        assert os.path.isfile(os.path.join(wt, "README.md"))
        assert not os.path.isfile(os.path.join(wt, "f.txt"))

    def test_reuse_returns_same_path(self, repos):
        wt1 = ensure_worktree(repos, "feature/existing")
        wt2 = ensure_worktree(repos, "feature/existing")
        assert wt1 == wt2
        assert os.path.isdir(wt2)

    def test_two_branches_are_isolated(self, repos):
        a = ensure_worktree(repos, "feature/wt-A")
        b = ensure_worktree(repos, "feature/wt-B")
        assert a != b
        ba = _git(a, "branch", "--show-current").stdout.strip()
        bb = _git(b, "branch", "--show-current").stdout.strip()
        assert ba == "feature/wt-A"
        assert bb == "feature/wt-B"
        # Writing in A must not affect B
        with open(os.path.join(a, "only-a.txt"), "w") as f:
            f.write("a")
        assert not os.path.isfile(os.path.join(b, "only-a.txt"))


# ---------------------------------------------------------------------------
# remove_worktree
# ---------------------------------------------------------------------------
class TestRemoveWorktree:
    def test_remove_deletes_worktree_dir(self, repos):
        wt = ensure_worktree(repos, "feature/to-remove")
        assert os.path.isdir(wt)
        remove_worktree(repos, "feature/to-remove")
        assert not os.path.isdir(wt)

    def test_remove_nonexistent_is_noop(self, repos):
        # Should not raise even if the worktree was never created.
        remove_worktree(repos, "feature/never-made")
