"""Tests for reviewer worktree safety proofs (Issue #233)."""
import sys
import unittest

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

from reviewer_worktree import (  # noqa: E402
    assess_author_worktree_continuity,
    assess_reviewer_git_command_log,
    assess_reviewer_worktree_proof,
    files_outside_pr_scope,
    is_forbidden_reviewer_git_command,
    is_readonly_reviewer_git_command,
    parse_dirty_tracked_files,
)


class TestParseDirtyTrackedFiles(unittest.TestCase):
    def test_ignores_untracked_files(self):
        porcelain = "?? untracked.txt\n M tracked.py\n"
        self.assertEqual(parse_dirty_tracked_files(porcelain), ["tracked.py"])

    def test_parses_renamed_paths(self):
        porcelain = "R  old.py -> new.py\n"
        self.assertEqual(parse_dirty_tracked_files(porcelain), ["new.py"])


class TestFilesOutsidePrScope(unittest.TestCase):
    def test_all_dirty_in_scope_is_clean(self):
        self.assertEqual(
            files_outside_pr_scope(
                ["docs/wiki/Repositories.md"],
                ["docs/wiki/Repositories.md"],
            ),
            [],
        )

    def test_unrelated_dirty_files_detected(self):
        self.assertEqual(
            files_outside_pr_scope(
                ["review_proofs.py", "docs/wiki/Repositories.md"],
                ["docs/wiki/Repositories.md"],
            ),
            ["review_proofs.py"],
        )


class TestForbiddenGitCommands(unittest.TestCase):
    def test_blocks_stash_operations(self):
        self.assertTrue(is_forbidden_reviewer_git_command("git stash"))
        self.assertTrue(
            is_forbidden_reviewer_git_command(
                'git stash push -m "reviewer-temp-stash" -- tests/test_mcp_server.py'
            )
        )
        self.assertTrue(is_forbidden_reviewer_git_command("git stash pop"))
        self.assertTrue(is_forbidden_reviewer_git_command("git stash drop"))

    def test_blocks_checkout_reset_and_clean(self):
        self.assertTrue(
            is_forbidden_reviewer_git_command(
                "git checkout -- review_proofs.py tests/test_mcp_server.py"
            )
        )
        self.assertTrue(is_forbidden_reviewer_git_command("git reset --hard"))
        self.assertTrue(is_forbidden_reviewer_git_command("git clean -fd"))

    def test_blocks_checkout_restore_and_switch_bypasses(self):
        """Issue #243: blocklist gaps closed via readonly allowlist model."""
        blocked = (
            "git checkout HEAD -- review_proofs.py",
            "git checkout prgs/master -- review_proofs.py",
            "git checkout .",
            "git switch -",
            "git switch -C feat/other-branch",
            "git switch master",
            "git stash store",
            "git stash branch wip-stash",
        )
        for cmd in blocked:
            with self.subTest(cmd=cmd):
                self.assertTrue(is_forbidden_reviewer_git_command(cmd))
                self.assertFalse(is_readonly_reviewer_git_command(cmd))

    def test_allows_readonly_commands(self):
        for cmd in (
            "git fetch prgs master",
            "git status --porcelain",
            "git diff prgs/master...HEAD",
            "git rev-parse HEAD",
            "git -C /repo log -1",
        ):
            with self.subTest(cmd=cmd):
                self.assertFalse(is_forbidden_reviewer_git_command(cmd))
                self.assertTrue(is_readonly_reviewer_git_command(cmd))


class TestAssessReviewerWorktreeProof(unittest.TestCase):
    def test_clean_worktree_proceeds(self):
        result = assess_reviewer_worktree_proof({
            "worktree_path": "/repo/branches/review-pr-231",
            "porcelain_status": "",
            "pr_scope_files": ["docs/wiki/Repositories.md"],
            "scratch_used": False,
            "git_commands": ["git fetch prgs master", "git diff prgs/master...HEAD"],
        })
        self.assertTrue(result["proven"])
        self.assertFalse(result["block"])

    def test_dirty_unrelated_without_scratch_blocks(self):
        result = assess_reviewer_worktree_proof({
            "worktree_path": "/repo",
            "dirty_files": ["review_proofs.py", "tests/test_review_proofs.py"],
            "pr_scope_files": ["docs/wiki/Repositories.md"],
            "scratch_used": False,
        })
        self.assertFalse(result["proven"])
        self.assertTrue(result["block"])
        self.assertIn("review_proofs.py", result["unrelated_dirty_files"][0])

    def test_scratch_worktree_allows_dirty_main_repo(self):
        result = assess_reviewer_worktree_proof({
            "worktree_path": "/repo",
            "dirty_files": ["review_proofs.py"],
            "pr_scope_files": ["docs/wiki/Repositories.md"],
            "scratch_used": True,
            "scratch_path": "/repo/branches/review-feat-issue-224",
        })
        self.assertTrue(result["proven"])

    def test_forbidden_command_history_blocks(self):
        result = assess_reviewer_worktree_proof({
            "worktree_path": "/repo/branches/review-pr-231",
            "porcelain_status": "",
            "git_commands": ["git stash push -m temp -- review_proofs.py"],
        })
        self.assertFalse(result["proven"])
        self.assertTrue(result["forbidden_commands"])

    def test_unrelated_mutations_claimed_blocks(self):
        result = assess_reviewer_worktree_proof({
            "worktree_path": "/repo",
            "porcelain_status": "",
            "unrelated_mutations_claimed": True,
        })
        self.assertFalse(result["proven"])


class TestAuthorContinuity(unittest.TestCase):
    def test_author_may_keep_dirty_worktree(self):
        result = assess_author_worktree_continuity({
            "task_role": "author",
            "dirty_files": ["feat.py"],
        })
        self.assertTrue(result["allowed"])

    def test_reviewer_dirty_worktree_uses_reviewer_gate(self):
        result = assess_author_worktree_continuity({
            "task_role": "reviewer",
            "worktree_path": "/repo",
            "dirty_files": ["other.py"],
            "pr_scope_files": ["docs/a.md"],
            "scratch_used": False,
        })
        self.assertFalse(result["proven"])


class TestAssessReviewerGitCommandLog(unittest.TestCase):
    def test_empty_log_is_clean(self):
        result = assess_reviewer_git_command_log([])
        self.assertTrue(result["proven"])

    def test_mixed_log_blocks_on_forbidden(self):
        result = assess_reviewer_git_command_log([
            "git fetch prgs",
            "git stash",
        ])
        self.assertFalse(result["proven"])
        self.assertEqual(len(result["forbidden_commands"]), 1)


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