"""Tests for issue-lock worktree validation (#249)."""

import os
import sys
import unittest
from unittest.mock import patch

sys.path.insert(0, str(__import__("pathlib").Path(__file__).resolve().parent.parent))

import issue_lock_worktree  # noqa: E402


class TestIssueLockWorktreeAssessment(unittest.TestCase):
    def test_clean_base_branch_passes(self):
        result = issue_lock_worktree.assess_issue_lock_worktree(
            worktree_path="/scratch/wt",
            current_branch="master",
            porcelain_status="",
        )
        self.assertTrue(result["proven"])
        self.assertFalse(result["block"])

    def test_dirty_tracked_files_fail(self):
        result = issue_lock_worktree.assess_issue_lock_worktree(
            worktree_path="/scratch/wt",
            current_branch="master",
            porcelain_status=" M gitea_mcp_server.py\n",
        )
        self.assertFalse(result["proven"])
        self.assertIn("tracked file edits exist before issue lock", result["reasons"][0])

    def test_feature_branch_fails(self):
        result = issue_lock_worktree.assess_issue_lock_worktree(
            worktree_path="/scratch/wt",
            current_branch="feat/issue-243-forbidden-git-gaps",
            porcelain_status="",
        )
        self.assertFalse(result["proven"])
        self.assertIn("base-equivalence could not be proven", result["reasons"][0])

    def test_base_equivalent_feature_branch_passes(self):
        result = issue_lock_worktree.assess_issue_lock_worktree(
            worktree_path="/repo/branches/issue-275",
            current_branch="feat/issue-275-claim-lock-branches-worktree",
            porcelain_status="",
            base_equivalent=True,
            inspected_git_root="/repo/branches/issue-275",
            base_branch="origin/master",
        )
        self.assertTrue(result["proven"])
        self.assertFalse(result["block"])
        self.assertEqual(result["base_branch"], "origin/master")

    def test_non_base_equivalent_branch_fails(self):
        result = issue_lock_worktree.assess_issue_lock_worktree(
            worktree_path="/repo/branches/issue-275",
            current_branch="feat/issue-275-claim-lock-branches-worktree",
            porcelain_status="",
            base_equivalent=False,
            inspected_git_root="/repo/branches/issue-275",
            base_branch=None,
        )
        self.assertFalse(result["proven"])
        self.assertIn("must be base-equivalent", result["reasons"][0])

    def test_untracked_files_do_not_block(self):
        result = issue_lock_worktree.assess_issue_lock_worktree(
            worktree_path="/scratch/wt",
            current_branch="main",
            porcelain_status="?? notes.txt\n",
        )
        self.assertTrue(result["proven"])


class TestStackedBaseEquivalence(unittest.TestCase):
    """extra_bases (an approved stacked base) can anchor base-equivalence (#484)."""

    def _git(self, *args):
        import subprocess

        subprocess.run(
            ["git", "-C", self.repo, *args],
            check=True,
            capture_output=True,
            text=True,
        )

    def setUp(self):
        import subprocess
        import tempfile

        self.tmp = tempfile.TemporaryDirectory()
        self.repo = self.tmp.name
        subprocess.run(["git", "init", "-q", self.repo], check=True, capture_output=True)
        self._git("config", "user.email", "t@t")
        self._git("config", "user.name", "t")
        self._git("commit", "--allow-empty", "-q", "-m", "base")
        # Rename the default branch away from master/main/dev so no *base* branch
        # exists at HEAD — otherwise HEAD would be base-equivalent for free.
        self._git("branch", "-m", "trunk")
        # Create a non-master "dependency" branch at the same commit, then a
        # feature branch off it — mirrors a stacked worktree.
        self._git("branch", "feat/issue-100-dep")
        self._git("checkout", "-q", "-b", "feat/issue-101-stacked")

    def tearDown(self):
        self.tmp.cleanup()

    def test_stacked_base_not_equivalent_without_extra_bases(self):
        state = issue_lock_worktree.read_worktree_git_state(self.repo)
        # HEAD does not match master/main/dev, so base-equivalence is False.
        self.assertFalse(state["base_equivalent"])

    def test_stacked_base_equivalent_with_extra_bases(self):
        state = issue_lock_worktree.read_worktree_git_state(
            self.repo, extra_bases=("feat/issue-100-dep",)
        )
        self.assertTrue(state["base_equivalent"])
        self.assertEqual(state["base_branch"], "feat/issue-100-dep")

    def test_unrelated_extra_base_does_not_anchor(self):
        state = issue_lock_worktree.read_worktree_git_state(
            self.repo, extra_bases=("feat/does-not-exist",)
        )
        self.assertFalse(state["base_equivalent"])


class TestIssueLockWorktreeResolution(unittest.TestCase):
    def test_explicit_path_wins(self):
        resolved = issue_lock_worktree.resolve_author_worktree_path(
            "/tmp/scratch/wt", "/shared/dev"
        )
        self.assertEqual(resolved, os.path.realpath("/tmp/scratch/wt"))

    def test_env_var_when_explicit_missing(self):
        with patch.dict(os.environ, {"GITEA_AUTHOR_WORKTREE": "/tmp/from-env"}):
            resolved = issue_lock_worktree.resolve_author_worktree_path(
                None, "/shared/dev"
            )
        self.assertEqual(resolved, os.path.realpath("/tmp/from-env"))

    def test_project_root_fallback(self):
        resolved = issue_lock_worktree.resolve_author_worktree_path(
            None, "/shared/dev"
        )
        self.assertEqual(resolved, os.path.realpath("/shared/dev"))


class TestPrWorktreeMatch(unittest.TestCase):
    def test_matching_paths_pass(self):
        result = issue_lock_worktree.verify_pr_worktree_matches_lock(
            "/tmp/scratch/wt",
            "/tmp/scratch/wt",
            "/shared/dev",
        )
        self.assertTrue(result["proven"])

    def test_mismatch_fails(self):
        result = issue_lock_worktree.verify_pr_worktree_matches_lock(
            "/tmp/scratch/wt",
            "/shared/dev",
            "/shared/dev",
        )
        self.assertFalse(result["proven"])
        self.assertIn("does not match locked worktree", result["reasons"][0])

    def test_missing_locked_path_skips_check(self):
        result = issue_lock_worktree.verify_pr_worktree_matches_lock(
            None,
            "/any/path",
            "/shared/dev",
        )
        self.assertTrue(result["proven"])


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