"""Unit tests for own-branch lock adoption decision (#442 / #443)."""
import sys
import unittest
from pathlib import Path

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

from issue_lock_adoption import (  # noqa: E402
    ADOPT,
    BLOCK_COMPETING,
    NO_MATCH,
    assess_own_branch_adoption,
    build_adoption_proof,
    build_non_adoption_lock_proof,
)

REQ = "feat/issue-420-server-code-parity"


class TestAssessOwnBranchAdoption(unittest.TestCase):
    def test_exact_own_branch_is_adopted(self):
        result = assess_own_branch_adoption(
            issue_number=420,
            requested_branch=REQ,
            existing_branches=[{"name": REQ, "commit_sha": "934688a"}],
        )
        self.assertEqual(result["outcome"], ADOPT)
        self.assertTrue(result["adopt"])
        self.assertFalse(result["block"])
        self.assertEqual(result["matched_branch"], REQ)
        self.assertEqual(result["matched_head_sha"], "934688a")

    def test_exact_own_branch_adopted_when_sha_missing(self):
        result = assess_own_branch_adoption(
            issue_number=420, requested_branch=REQ, existing_branches=[REQ]
        )
        self.assertEqual(result["outcome"], ADOPT)
        self.assertIsNone(result["matched_head_sha"])

    def test_different_branch_same_issue_blocks(self):
        result = assess_own_branch_adoption(
            issue_number=420,
            requested_branch=REQ,
            existing_branches=[{"name": "feat/issue-420-other-work"}],
        )
        self.assertEqual(result["outcome"], BLOCK_COMPETING)
        self.assertTrue(result["block"])
        self.assertFalse(result["adopt"])
        self.assertIn("feat/issue-420-other-work", result["competing_branches"])
        self.assertIn("fail closed", result["reason"])

    def test_own_branch_plus_competing_branch_blocks(self):
        # Ambiguous ownership: fail closed even though the exact branch exists.
        result = assess_own_branch_adoption(
            issue_number=420,
            requested_branch=REQ,
            existing_branches=[{"name": REQ}, {"name": "feat/issue-420-rogue"}],
        )
        self.assertEqual(result["outcome"], BLOCK_COMPETING)
        self.assertTrue(result["block"])
        self.assertEqual(result["competing_branches"], ["feat/issue-420-rogue"])

    def test_no_matching_branch_is_normal_path(self):
        result = assess_own_branch_adoption(
            issue_number=420,
            requested_branch=REQ,
            existing_branches=[{"name": "feat/issue-999-unrelated"}],
        )
        self.assertEqual(result["outcome"], NO_MATCH)
        self.assertFalse(result["block"])
        self.assertFalse(result["adopt"])

    def test_empty_branch_list_is_normal_path(self):
        result = assess_own_branch_adoption(
            issue_number=420, requested_branch=REQ, existing_branches=[]
        )
        self.assertEqual(result["outcome"], NO_MATCH)

    def test_higher_issue_number_branch_does_not_block_lower_issue_adoption(self):
        # issue-420 must not be treated as competing work for issue #42.
        own_branch = "feat/issue-42-widget"
        result = assess_own_branch_adoption(
            issue_number=42,
            requested_branch=own_branch,
            existing_branches=[
                {"name": own_branch, "commit_sha": "abc1234"},
                {"name": "feat/issue-420-server-code-parity"},
            ],
        )
        self.assertEqual(result["outcome"], ADOPT)
        self.assertTrue(result["adopt"])
        self.assertFalse(result["block"])
        self.assertEqual(result["matched_branch"], own_branch)

    def test_unrelated_higher_number_branch_is_ignored_without_own_branch(self):
        result = assess_own_branch_adoption(
            issue_number=42,
            requested_branch="feat/issue-42-thing",
            existing_branches=[{"name": "feat/issue-420-server-code-parity"}],
        )
        self.assertEqual(result["outcome"], NO_MATCH)
        self.assertFalse(result["block"])
        self.assertFalse(result["adopt"])


class TestBuildAdoptionProof(unittest.TestCase):
    def test_proof_has_all_required_fields(self):
        assessment = assess_own_branch_adoption(
            issue_number=420,
            requested_branch=REQ,
            existing_branches=[{"name": REQ, "commit_sha": "934688a"}],
        )
        proof = build_adoption_proof(
            issue_number=420,
            branch_name=REQ,
            assessment=assessment,
            open_pr_checked=True,
            competing_lock_checked=True,
            lock_file_path="/tmp/example-lock.json",
            lock_file_status="written",
        )
        for key in (
            "issue_number",
            "branch_name",
            "branch_head_commit",
            "adoption_reason",
            "no_existing_pr_proof",
            "no_competing_live_lock_proof",
            "lock_file_path",
            "lock_file_status",
        ):
            self.assertIn(key, proof)
        self.assertEqual(proof["branch_head_commit"], "934688a")
        self.assertTrue(proof["no_existing_pr_proof"])
        self.assertTrue(proof["no_competing_live_lock_proof"])


class TestExplicitAdoptionProofFields(unittest.TestCase):
    """#477: explicit, citable adoption-proof fields for all outcomes."""

    def _proof(self, assessment, branch):
        return build_adoption_proof(
            issue_number=420,
            branch_name=branch,
            assessment=assessment,
            open_pr_checked=True,
            competing_lock_checked=True,
            lock_file_path="/tmp/example-lock.json",
            lock_file_status="written",
        )

    def test_adopt_proof_exposes_explicit_fields(self):
        assessment = assess_own_branch_adoption(
            issue_number=420,
            requested_branch=REQ,
            existing_branches=[{"name": REQ, "commit_sha": "934688a"}],
        )
        proof = self._proof(assessment, REQ)
        self.assertEqual(proof["adoption_decision"], "ADOPT")
        self.assertTrue(proof["adopted"])
        self.assertEqual(proof["adopted_branch"], REQ)
        self.assertEqual(proof["adopted_branch_head"], "934688a")
        self.assertEqual(proof["competing_branch_check"]["result"], "clear")
        self.assertEqual(proof["competing_branch_check"]["competing_branches"], [])
        self.assertIn("gitea_create_pr", proof["safe_next_action"])
        self.assertIn("exactly matches", proof["matcher_summary"])

    def test_block_proof_reports_competing_and_does_not_claim_adoption(self):
        assessment = assess_own_branch_adoption(
            issue_number=420,
            requested_branch=REQ,
            existing_branches=[{"name": "feat/issue-420-rogue"}],
        )
        proof = self._proof(assessment, REQ)
        self.assertEqual(proof["adoption_decision"], "BLOCK_COMPETING")
        self.assertFalse(proof["adopted"])
        self.assertIsNone(proof["adopted_branch"])
        self.assertIsNone(proof["adopted_branch_head"])
        self.assertEqual(proof["competing_branch_check"]["result"], "blocked")
        self.assertIn(
            "feat/issue-420-rogue",
            proof["competing_branch_check"]["competing_branches"],
        )
        self.assertIn("fail closed", proof["safe_next_action"])

    def test_no_match_proof_does_not_claim_adoption(self):
        assessment = assess_own_branch_adoption(
            issue_number=420,
            requested_branch=REQ,
            existing_branches=[{"name": "feat/issue-999-unrelated"}],
        )
        proof = self._proof(assessment, REQ)
        self.assertEqual(proof["adoption_decision"], "NO_MATCH")
        self.assertFalse(proof["adopted"])
        self.assertIsNone(proof["adopted_branch"])
        self.assertEqual(proof["competing_branch_check"]["result"], "clear")

    def test_substring_collision_stays_boundary_safe(self):
        # issue-42 must not adopt/claim against an issue-420 branch (#440/#477).
        own = "feat/issue-42-widget"
        assessment = assess_own_branch_adoption(
            issue_number=42,
            requested_branch=own,
            existing_branches=[
                {"name": own, "commit_sha": "abc1234"},
                {"name": "feat/issue-420-server-code-parity"},
            ],
        )
        proof = self._proof(assessment, own)
        self.assertEqual(proof["adoption_decision"], "ADOPT")
        self.assertEqual(proof["adopted_branch"], own)
        self.assertEqual(proof["competing_branch_check"]["competing_branches"], [])

    def test_non_adoption_lock_proof_is_adoption_free(self):
        proof = build_non_adoption_lock_proof(
            issue_number=196, branch_name="feat/issue-196-mutations"
        )
        self.assertEqual(proof["adoption_decision"], "NO_MATCH")
        self.assertFalse(proof["adopted"])
        self.assertIsNone(proof["adopted_branch"])
        self.assertIsNone(proof["adopted_branch_head"])
        self.assertEqual(proof["competing_branch_check"]["result"], "clear")
        self.assertIn("no adoption", proof["safe_next_action"].lower())


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