"""Tests for preserving and resuming prepared Gitea review drafts (#609)."""

from __future__ import annotations

import os
import sys
import tempfile
import unittest
from pathlib import Path
from unittest.mock import patch, MagicMock

sys_path_root = str(Path(__file__).resolve().parent.parent)
if sys_path_root not in sys.path:
    sys.path.insert(0, sys_path_root)

import mcp_session_state
import gitea_mcp_server
import reviewer_pr_lease


class TestReviewDrafts(unittest.TestCase):
    def setUp(self):
        self._tmpdir = tempfile.TemporaryDirectory()
        self.state_dir = self._tmpdir.name
        self._env = patch.dict(
            os.environ,
            {
                mcp_session_state.STATE_DIR_ENV: self.state_dir,
                mcp_session_state.SESSION_PROFILE_LOCK_ENV: "prgs-reviewer",
                "GITEA_MCP_PROFILE": "prgs-reviewer",
                "GITEA_PROFILE_NAME": "prgs-reviewer",
                "PYTEST_CURRENT_TEST": "1",  # skip runtime stale checks
            },
            clear=False,
        )
        self._env.start()
        
        # Mock workspace binding to pass in test context
        self._nwb_patch = patch(
            "gitea_mcp_server.nwb.assess_namespace_mutation_workspace",
            return_value={"block": False, "mutation_workspace": "/tmp/test-worktree"},
        )
        self._nwb_patch.start()

        # Mock authentication headers
        self._auth_patch = patch(
            "gitea_mcp_server.get_auth_header",
            return_value="Bearer mock-token",
        )
        self._auth_patch.start()

        self._username_patch = patch(
            "gitea_mcp_server._authenticated_username",
            return_value="sysadmin",
        )
        self._username_patch.start()
        
        # Clear/Mock local session lease in-memory state
        reviewer_pr_lease.clear_session_lease()

    def tearDown(self):
        self._username_patch.stop()
        self._auth_patch.stop()
        self._nwb_patch.stop()
        self._env.stop()
        self._tmpdir.cleanup()
        reviewer_pr_lease.clear_session_lease()

    @patch("gitea_mcp_server.api_request")
    def test_save_draft_success(self, mock_api):
        mock_api.return_value = {
            "head": {"sha": "headsha1234567890"},
            "base": {"ref": "master", "sha": "basesha0987654321"},
        }
        res = gitea_mcp_server.gitea_save_review_draft(
            pr_number=587,
            action="approve",
            body="Good changes",
            expected_head_sha="headsha1234567890",
            validation_commands="pytest tests/",
            validation_results="all pass",
            blocker_reason="stale daemon",
            remote="prgs",
            org="Scaled-Tech-Consulting",
            repo="Gitea-Tools",
            worktree_path="/tmp/test-worktree",
        )
        self.assertTrue(res.get("success"))
        self.assertEqual(res.get("head_sha"), "headsha1234567890")
        
        # Load draft and verify fields
        draft = mcp_session_state.load_state(
            kind=mcp_session_state.KIND_REVIEW_DRAFT,
            remote="prgs",
            org="Scaled-Tech-Consulting",
            repo="Gitea-Tools",
            profile_identity="prgs-reviewer",
        )
        self.assertIsNotNone(draft)
        self.assertEqual(draft.get("pr_number"), 587)
        self.assertEqual(draft.get("action"), "approve")
        self.assertEqual(draft.get("body"), "Good changes")
        self.assertEqual(draft.get("validation_commands"), "pytest tests/")
        self.assertEqual(draft.get("validation_results"), "all pass")
        self.assertEqual(draft.get("blocker_reason"), "stale daemon")
        self.assertEqual(os.path.realpath(draft.get("worktree_path")), os.path.realpath("/tmp/test-worktree"))

    @patch("gitea_mcp_server.api_request")
    @patch("gitea_mcp_server._fetch_pr_comments")
    @patch("gitea_mcp_server.gitea_submit_pr_review")
    def test_resume_draft_and_submit_success(self, mock_submit, mock_comments, mock_api):
        # 1. Save draft
        mock_api.return_value = {
            "state": "open",
            "head": {"sha": "headsha1234567890"},
            "base": {"ref": "master", "sha": "basesha0987654321"},
        }
        
        gitea_mcp_server.gitea_save_review_draft(
            pr_number=587,
            action="approve",
            body="Good changes",
            expected_head_sha="headsha1234567890",
            remote="prgs",
            org="Scaled-Tech-Consulting",
            repo="Gitea-Tools",
            worktree_path="/tmp/test-worktree",
        )
        
        # Seed local session lease
        reviewer_pr_lease.record_session_lease({
            "pr_number": 587,
            "session_id": "session-1234",
        })
        
        # Mock Gitea comments to return active reviewer lease owned by us
        mock_comments.return_value = [
            {
                "id": 1,
                "user": {"username": "sysadmin"},
                "body": (
                    "<!-- mcp-review-lease:v1 -->\n"
                    "repo: Scaled-Tech-Consulting/Gitea-Tools\n"
                    "pr: #587\n"
                    "reviewer_identity: sysadmin\n"
                    "profile: prgs-reviewer\n"
                    "session_id: session-1234\n"
                    "phase: claimed\n"
                ),
            }
        ]
        
        mock_submit.return_value = {"performed": True, "success": True}
        
        # 2. Resume draft
        res = gitea_mcp_server.gitea_resume_review_draft(
            pr_number=587,
            submit=True,
            remote="prgs",
            org="Scaled-Tech-Consulting",
            repo="Gitea-Tools",
            worktree_path="/tmp/test-worktree",
        )
        
        self.assertTrue(res.get("success"))
        self.assertTrue(res.get("marked_ready"))
        self.assertTrue(res.get("submitted"))
        mock_submit.assert_called_once()
        
        # Verify draft was deleted after successful submit
        draft = mcp_session_state.load_state(
            kind=mcp_session_state.KIND_REVIEW_DRAFT,
            remote="prgs",
            org="Scaled-Tech-Consulting",
            repo="Gitea-Tools",
            profile_identity="prgs-reviewer",
        )
        self.assertIsNone(draft)

    @patch("gitea_mcp_server.api_request")
    @patch("gitea_mcp_server._fetch_pr_comments")
    def test_resume_draft_fails_checks(self, mock_comments, mock_api):
        # Save a draft
        mock_api.return_value = {
            "state": "open",
            "head": {"sha": "headsha1234567890"},
            "base": {"ref": "master", "sha": "basesha0987654321"},
        }
        gitea_mcp_server.gitea_save_review_draft(
            pr_number=587,
            action="approve",
            body="Good changes",
            expected_head_sha="headsha1234567890",
            remote="prgs",
            org="Scaled-Tech-Consulting",
            repo="Gitea-Tools",
            worktree_path="/tmp/test-worktree",
        )
        
        # Seed local session lease
        reviewer_pr_lease.record_session_lease({
            "pr_number": 587,
            "session_id": "session-1234",
        })
        
        # Mock Gitea comments to return active reviewer lease owned by us
        mock_comments.return_value = [
            {
                "id": 1,
                "user": {"username": "sysadmin"},
                "body": (
                    "<!-- mcp-review-lease:v1 -->\n"
                    "repo: Scaled-Tech-Consulting/Gitea-Tools\n"
                    "pr: #587\n"
                    "reviewer_identity: sysadmin\n"
                    "profile: prgs-reviewer\n"
                    "session_id: session-1234\n"
                    "phase: claimed\n"
                ),
            }
        ]
        
        # Case A: PR closed
        mock_api.return_value = {
            "state": "closed",
            "head": {"sha": "headsha1234567890"},
            "base": {"ref": "master", "sha": "basesha0987654321"},
        }
        res = gitea_mcp_server.gitea_resume_review_draft(
            pr_number=587,
            remote="prgs",
            org="Scaled-Tech-Consulting",
            repo="Gitea-Tools",
            worktree_path="/tmp/test-worktree",
        )
        self.assertFalse(res.get("success"))
        self.assertIn("PR #587 is not open", res.get("reasons")[0])
        
        # Case B: Head changed
        mock_api.return_value = {
            "state": "open",
            "head": {"sha": "headsha_NEW"},
            "base": {"ref": "master", "sha": "basesha0987654321"},
        }
        res = gitea_mcp_server.gitea_resume_review_draft(
            pr_number=587,
            remote="prgs",
            org="Scaled-Tech-Consulting",
            repo="Gitea-Tools",
            worktree_path="/tmp/test-worktree",
        )
        self.assertFalse(res.get("success"))
        self.assertIn("PR head SHA changed", res.get("reasons")[0])
        
        # Case C: Target branch changed
        mock_api.return_value = {
            "state": "open",
            "head": {"sha": "headsha1234567890"},
            "base": {"ref": "master", "sha": "basesha_NEW"},
        }
        res = gitea_mcp_server.gitea_resume_review_draft(
            pr_number=587,
            remote="prgs",
            org="Scaled-Tech-Consulting",
            repo="Gitea-Tools",
            worktree_path="/tmp/test-worktree",
        )
        self.assertFalse(res.get("success"))
        self.assertIn("target branch SHA changed", res.get("reasons")[0])
        
        # Case D: Worktree mismatch
        mock_api.return_value = {
            "state": "open",
            "head": {"sha": "headsha1234567890"},
            "base": {"ref": "master", "sha": "basesha0987654321"},
        }
        res = gitea_mcp_server.gitea_resume_review_draft(
            pr_number=587,
            remote="prgs",
            org="Scaled-Tech-Consulting",
            repo="Gitea-Tools",
            worktree_path="/tmp/different-worktree",
        )
        self.assertFalse(res.get("success"))
        self.assertIn("worktree binding mismatch", res.get("reasons")[0])

    @patch("gitea_mcp_server.api_request")
    @patch("gitea_mcp_server._fetch_pr_comments")
    def test_resume_draft_foreign_lease(self, mock_comments, mock_api):
        mock_api.return_value = {
            "state": "open",
            "head": {"sha": "headsha1234567890"},
            "base": {"ref": "master", "sha": "basesha0987654321"},
        }
        gitea_mcp_server.gitea_save_review_draft(
            pr_number=587,
            action="approve",
            body="Good changes",
            expected_head_sha="headsha1234567890",
            remote="prgs",
            org="Scaled-Tech-Consulting",
            repo="Gitea-Tools",
            worktree_path="/tmp/test-worktree",
        )
        reviewer_pr_lease.record_session_lease({
            "pr_number": 587,
            "session_id": "my-session",
        })
        mock_comments.return_value = [
            {
                "id": 9,
                "user": {"username": "sysadmin"},
                "body": (
                    "<!-- mcp-review-lease:v1 -->\n"
                    "repo: Scaled-Tech-Consulting/Gitea-Tools\n"
                    "pr: #587\n"
                    "reviewer_identity: sysadmin\n"
                    "profile: prgs-reviewer\n"
                    "session_id: foreign-session-999\n"
                    "phase: claimed\n"
                ),
            }
        ]
        res = gitea_mcp_server.gitea_resume_review_draft(
            pr_number=587,
            remote="prgs",
            org="Scaled-Tech-Consulting",
            repo="Gitea-Tools",
            worktree_path="/tmp/test-worktree",
        )
        self.assertFalse(res.get("success"))
        self.assertTrue(
            any("foreign-session-999" in r or "session_id" in r for r in (res.get("reasons") or []))
        )

    @patch("gitea_mcp_server.api_request")
    @patch("gitea_mcp_server._fetch_pr_comments")
    @patch("gitea_mcp_server.terminal_review_hard_stop_reasons")
    def test_resume_draft_terminal_lock_blocks(self, mock_hard_stop, mock_comments, mock_api):
        mock_api.return_value = {
            "state": "open",
            "head": {"sha": "headsha1234567890"},
            "base": {"ref": "master", "sha": "basesha0987654321"},
        }
        gitea_mcp_server.gitea_save_review_draft(
            pr_number=587,
            action="approve",
            body="Good changes",
            expected_head_sha="headsha1234567890",
            remote="prgs",
            org="Scaled-Tech-Consulting",
            repo="Gitea-Tools",
            worktree_path="/tmp/test-worktree",
        )
        reviewer_pr_lease.record_session_lease({
            "pr_number": 587,
            "session_id": "session-1234",
        })
        mock_comments.return_value = [
            {
                "id": 1,
                "user": {"username": "sysadmin"},
                "body": (
                    "<!-- mcp-review-lease:v1 -->\n"
                    "repo: Scaled-Tech-Consulting/Gitea-Tools\n"
                    "pr: #587\n"
                    "reviewer_identity: sysadmin\n"
                    "profile: prgs-reviewer\n"
                    "session_id: session-1234\n"
                    "phase: claimed\n"
                ),
            }
        ]
        mock_hard_stop.return_value = [
            "terminal review mutation already recorded for PR #587 (approve); #332 hard-stop"
        ]
        res = gitea_mcp_server.gitea_resume_review_draft(
            pr_number=587,
            remote="prgs",
            org="Scaled-Tech-Consulting",
            repo="Gitea-Tools",
            worktree_path="/tmp/test-worktree",
        )
        self.assertFalse(res.get("success"))
        self.assertTrue(any("#332" in r or "terminal" in r for r in (res.get("reasons") or [])))

