"""Tests for the branch-worktree helper scripts (#38/#39).

Exercises path generation and refuse-to-overwrite via the scripts' ``--dry-run``
mode, so no real worktrees, branches, network, or deletions are involved.
"""
import os
import subprocess
import unittest
from pathlib import Path

REPO = Path(__file__).resolve().parent.parent
SCRIPTS = REPO / "scripts"
BRANCHES = REPO / "branches"


def run(script, *args):
    branch = None
    for arg in args:
        if not arg.startswith("-"):
            branch = arg
            break

    lock_dir_ctx = None
    extra_env = os.environ.copy()
    if script == "worktree-start" and branch:
        import re
        import tempfile
        import issue_lock_store

        m = re.search(r"issue-(\d+)", branch)
        if not m:
            m = re.search(r"pr-(\d+)", branch)
        issue_num = int(m.group(1)) if m else 999
        lock_dir_ctx = tempfile.TemporaryDirectory()
        extra_env["GITEA_ISSUE_LOCK_DIR"] = lock_dir_ctx.name
        record = {
            "issue_number": issue_num,
            "branch_name": branch,
            "remote": "prgs",
            "org": "Scaled-Tech-Consulting",
            "repo": "Gitea-Tools",
            "worktree_path": "/tmp/test-worktree",
            "work_lease": {
                "operation_type": "author_issue_work",
                "expires_at": "2999-01-01T00:00:00Z",
            },
        }
        path = issue_lock_store.lock_file_path(
            remote="prgs",
            org="Scaled-Tech-Consulting",
            repo="Gitea-Tools",
            issue_number=issue_num,
            lock_dir=lock_dir_ctx.name,
        )
        issue_lock_store.save_lock_file(path, record)

    try:
        proc = subprocess.run(
            ["bash", str(SCRIPTS / script), *args],
            capture_output=True, text=True, cwd=str(REPO),
            env=extra_env,
        )
        return proc.returncode, proc.stdout, proc.stderr
    finally:
        if lock_dir_ctx is not None:
            lock_dir_ctx.cleanup()


class TestWorktreeStart(unittest.TestCase):

    def test_dry_run_path_generation(self):
        rc, out, _ = run("worktree-start", "--dry-run", "fix/issue-123-example")
        self.assertEqual(rc, 0)
        self.assertIn("branches/fix-issue-123-example", out)
        self.assertIn("fix/issue-123-example", out)
        self.assertIn("prgs/master", out)  # default start-ref

    def test_bad_args_exit_2(self):
        rc, _, _ = run("worktree-start")
        self.assertEqual(rc, 2)

    def test_refuses_existing_worktree(self):
        branch = f"fix/issue-999-refuse-{os.getpid()}"
        slug = branch.replace("/", "-")
        target = BRANCHES / slug
        target.mkdir(parents=True, exist_ok=True)
        try:
            rc, _, err = run("worktree-start", "--dry-run", branch)
            self.assertEqual(rc, 1)
            self.assertIn("Refusing to reuse", err)
        finally:
            target.rmdir()

    # -- issue-linked branch validation (#48) --------------------------------

    def test_accepts_issue_linked_impl_branches(self):
        for branch in ("fix/issue-123-example", "feat/issue-123-example",
                       "docs/issue-123-example", "chore/issue-123-example"):
            rc, out, err = run("worktree-start", "--dry-run", branch)
            self.assertEqual(rc, 0, f"{branch}: {err}")
            self.assertIn(f"branches/{branch.replace('/', '-')}", out)

    def test_accepts_review_branch(self):
        rc, out, _ = run("worktree-start", "--dry-run", "review/pr-456-example")
        self.assertEqual(rc, 0)
        self.assertIn("branches/review-pr-456-example", out)

    def test_rejects_untraceable_branches(self):
        for branch in ("fix/random-name", "my-branch", "feat/no-issue-here",
                       "fix/issue-abc-x"):
            rc, _, err = run("worktree-start", "--dry-run", branch)
            self.assertEqual(rc, 2, branch)
            self.assertIn("Untraceable branch name", err)

    def test_allow_unlinked_override(self):
        rc, out, _ = run("worktree-start", "--dry-run", "--allow-unlinked", "my-branch")
        self.assertEqual(rc, 0)
        self.assertIn("branches/my-branch", out)


class TestWorktreeReview(unittest.TestCase):

    def test_dry_run_detached_review_path(self):
        rc, out, _ = run("worktree-review", "--dry-run", "fix/issue-9-x")
        self.assertEqual(rc, 0)
        self.assertIn("branches/review-fix-issue-9-x", out)
        self.assertIn("--detach", out)
        self.assertIn("prgs/fix/issue-9-x", out)  # default start-ref

    def test_refuses_existing_review_worktree(self):
        slug = f"review-zz-refuse-rev-{os.getpid()}"
        target = BRANCHES / slug
        target.mkdir(parents=True, exist_ok=True)
        try:
            # branch name maps to the same slug: review-<branch>
            rc, _, err = run("worktree-review", "--dry-run",
                             f"zz-refuse-rev-{os.getpid()}")
            self.assertEqual(rc, 1)
            self.assertIn("Refusing to reuse", err)
        finally:
            target.rmdir()


class TestWorktreeClean(unittest.TestCase):

    def test_missing_worktree_errors(self):
        rc, _, err = run("worktree-clean", "--dry-run", "does-not-exist-xyz")
        self.assertEqual(rc, 1)
        self.assertIn("No such worktree", err)

    def test_dry_run_does_not_delete(self):
        slug = f"zz-clean-{os.getpid()}"
        target = BRANCHES / slug
        target.mkdir(parents=True, exist_ok=True)
        try:
            rc, out, _ = run("worktree-clean", "--dry-run", slug)
            self.assertEqual(rc, 0)
            self.assertIn("worktree remove", out)
            self.assertTrue(target.is_dir())  # nothing removed in dry-run
        finally:
            target.rmdir()

    def test_dry_run_delete_branch_lists_branch_command(self):
        slug = f"zz-clean-b-{os.getpid()}"
        target = BRANCHES / slug
        target.mkdir(parents=True, exist_ok=True)
        try:
            rc, out, _ = run("worktree-clean", "--dry-run", "--delete-branch", slug)
            self.assertEqual(rc, 0)
            self.assertIn("branch -d", out)
        finally:
            target.rmdir()


if __name__ == "__main__":
    unittest.main()
