"""
PM-Agent v1.2.0 Ultra Coverage Tests

用于将v1.2.0新服务代码覆盖率提升至80%以上
"""
import os
import sys
import pytest
import tempfile
import shutil
from pathlib import Path
from unittest.mock import Mock, patch, MagicMock
from datetime import datetime, timedelta

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


class TestGitSyncUltraCoverage:
    """GitSyncService 超高覆盖率测试"""

    @pytest.fixture
    def temp_dir(self):
        temp = tempfile.mkdtemp()
        yield temp
        shutil.rmtree(temp, ignore_errors=True)

    @patch('backend.services.git_sync_service.Repo')
    def test_clone_general_exception(self, mock_repo, temp_dir):
        """测试克隆通用异常"""
        from backend.services.git_sync_service import GitSyncService
        
        mock_repo.clone_from.side_effect = Exception("Unknown error")
        
        service = GitSyncService(base_path=temp_dir)
        
        result = service._clone_repo(
            "https://github.com/test/repo.git",
            os.path.join(temp_dir, "test"),
            "test",
            datetime.now()
        )
        
        assert result.success is False

    @patch('backend.services.git_sync_service.Repo')
    def test_pull_exception(self, mock_repo, temp_dir):
        """测试拉取异常"""
        from backend.services.git_sync_service import GitSyncService
        
        project_dir = os.path.join(temp_dir, "test")
        os.makedirs(os.path.join(project_dir, ".git"))
        
        mock_repo.side_effect = Exception("Error")
        
        service = GitSyncService(base_path=temp_dir)
        
        result = service._pull_changes(project_dir, "test", datetime.now())
        
        assert result.success is False

    @patch('backend.services.git_sync_service.Repo')
    def test_pull_with_fetch_info_new_head(self, mock_repo, temp_dir):
        """测试新HEAD拉取"""
        from backend.services.git_sync_service import GitSyncService
        from git import FetchInfo
        
        project_dir = os.path.join(temp_dir, "test")
        os.makedirs(os.path.join(project_dir, ".git"))
        
        mock_fetch_info = Mock()
        mock_fetch_info.flags = FetchInfo.NEW_HEAD
        mock_fetch_info.ref = "refs/heads/main"
        
        mock_git_repo = Mock()
        mock_git_repo.head.is_detached = False
        mock_git_repo.remotes.origin.fetch.return_value = [mock_fetch_info]
        
        mock_active_branch = Mock()
        mock_active_branch.tracking_branch.return_value = None
        mock_git_repo.active_branch = mock_active_branch
        
        mock_git_repo.index.diff.return_value = []
        mock_git_repo.untracked_files = []
        
        mock_repo.return_value = mock_git_repo
        
        service = GitSyncService(base_path=temp_dir)
        
        result = service._pull_changes(project_dir, "test", datetime.now())
        
        assert result.success is True

    def test_sync_project_exception(self, temp_dir):
        """测试同步项目异常"""
        from backend.services.git_sync_service import GitSyncService
        
        service = GitSyncService(base_path=temp_dir)
        
        with patch.object(service, '_clone_repo', side_effect=Exception("Error")):
            result = service.sync_project("test", "https://github.com/test.git")
        
        assert result is not None

    def test_count_files_permission_error(self, temp_dir):
        """测试文件计数权限错误"""
        from backend.services.git_sync_service import GitSyncService
        
        service = GitSyncService()
        
        result = service._count_files("/root")  # Permission denied
        
        assert result == 0


class TestProgressUltraCoverage:
    """ProgressService 超高覆盖率测试"""

    def test_calculate_progress_zero_weights_normalized(self):
        """测试归一化零权重"""
        from backend.services.progress_service import ProgressService, ProjectProgress, RequirementsProgress, BugsProgress, TodosProgress
        
        service = ProgressService()
        
        service.set_weights(requirements=0, bugs=0, todos=0)
        
        progress = ProjectProgress(
            project_name="test",
            requirements=RequirementsProgress(total=10, completed=5),
            bugs=BugsProgress(total=10, resolved=5),
            todos=TodosProgress(total=10, completed=5)
        )
        
        result = service.calculate_overall_progress(progress)
        
        assert result >= 0

    def test_get_project_progress_error(self):
        """测试获取进度错误"""
        from backend.services.progress_service import ProgressService
        
        mock_client = Mock()
        mock_client.get_project_progress.side_effect = Exception("Error")
        
        service = ProgressService(client=mock_client)
        
        progress = service.get_project_progress("test-project")
        
        assert progress is not None

    def test_get_progress_history_empty(self):
        """测试空进度历史"""
        from backend.services.progress_service import ProgressService
        
        service = ProgressService()
        
        history = service.get_progress_history("test-project", days=0)
        
        assert isinstance(history, list)

    def test_projects_summary_with_data(self):
        """测试有数据的项目汇总"""
        from backend.services.progress_service import ProgressService, ProjectProgress, RequirementsProgress, BugsProgress, TodosProgress
        
        service = ProgressService()
        
        progress = ProjectProgress(
            project_name="test",
            requirements=RequirementsProgress(total=10, completed=5),
            bugs=BugsProgress(total=10, resolved=5),
            todos=TodosProgress(total=10, completed=5)
        )
        
        summary = service.get_all_projects_summary(project_names=["test"])
        
        assert summary.total_projects >= 0


class TestIssueSyncUltraCoverage:
    """IssueSyncService 超高覆盖率测试"""

    def test_sync_bugs_exception(self):
        """测试同步BUG异常"""
        from backend.services.issue_sync_service import IssueSyncService
        
        mock_client = Mock()
        mock_client.get_project_bugs.side_effect = Exception("Error")
        
        service = IssueSyncService(client=mock_client)
        
        result = service.sync_bugs("test-project")
        
        assert result == []

    def test_sync_requirements_exception(self):
        """测试同步需求异常"""
        from backend.services.issue_sync_service import IssueSyncService
        
        mock_client = Mock()
        mock_client.get_project_requirements.side_effect = Exception("Error")
        
        service = IssueSyncService(client=mock_client)
        
        result = service.sync_requirements("test-project")
        
        assert result == []

    def test_save_bugs_exception(self):
        """测试保存BUG异常"""
        from backend.services.issue_sync_service import IssueSyncService
        
        mock_db = Mock()
        mock_db.commit.side_effect = Exception("Error")
        
        service = IssueSyncService()
        
        service.save_bugs_to_db("test-project", [], mock_db)

    def test_get_bugs_summary_with_data(self):
        """测试有数据的BUG统计"""
        from backend.services.issue_sync_service import IssueSyncService
        
        mock_client = Mock()
        mock_client.get_project_bugs.return_value = [
            Mock(id="1", title="Bug", status="open", severity="high")
        ]
        
        service = IssueSyncService(client=mock_client)
        
        summary = service.get_bugs_summary("test-project")
        
        assert 'total' in summary

    def test_get_requirements_summary_with_data(self):
        """测试有数据的需求统计"""
        from backend.services.issue_sync_service import IssueSyncService
        
        mock_client = Mock()
        mock_client.get_project_requirements.return_value = [
            Mock(id="1", title="Req", status="draft", priority="high")
        ]
        
        service = IssueSyncService(client=mock_client)
        
        summary = service.get_requirements_summary("test-project")
        
        assert 'total' in summary


class TestStatusFeedbackUltraCoverage:
    """StatusFeedbackService 超高覆盖率测试"""

    def test_poll_changes_empty(self):
        """测试空轮询"""
        from backend.services.status_feedback_service import StatusFeedbackService
        
        mock_client = Mock()
        mock_client.get_changes.return_value = []
        
        service = StatusFeedbackService(client=mock_client)
        
        changes = service.poll_changes("test-project")
        
        assert changes == []

    def test_parse_change_with_values(self):
        """测试解析带值的变更"""
        from backend.services.status_feedback_service import StatusFeedbackService, ChangeType
        
        service = StatusFeedbackService()
        
        change = {
            'id': '1',
            'title': 'Test',
            'change_type': 'todo',
            'old_status': 'pending',
            'new_status': 'done',
            'old_value': '10',
            'new_value': '20',
            'description': 'Updated'
        }
        
        event = service._parse_change("test-project", change)
        
        assert event.old_value == '10'
        assert event.new_value == '20'

    def test_update_local_status_multiple(self):
        """测试批量更新"""
        from backend.services.status_feedback_service import StatusFeedbackService, ChangeEvent, ChangeType
        
        mock_storage = Mock()
        
        service = StatusFeedbackService(storage=mock_storage)
        
        events = [
            ChangeEvent(
                project_name="test",
                change_type=ChangeType.BUG,
                change_id=f"BUG-{i}",
                title="Test",
                old_status="open",
                new_status="closed"
            )
            for i in range(3)
        ]
        
        count = service.update_local_status(events)
        
        assert count == 3


class TestSyncPermissionUltraCoverage:
    """SyncPermissionService 超高覆盖率测试"""

    def test_get_sync_recommendation_with_project(self):
        """测试带项目的建议"""
        from backend.services.sync_permission_service import SyncPermissionService
        
        service = SyncPermissionService()
        
        result = service.get_sync_recommendation("test-project")
        
        assert result is not None

    def test_get_confidential_with_storage(self):
        """测试有存储的保密项目"""
        from backend.services.sync_permission_service import SyncPermissionService
        
        service = SyncPermissionService()
        
        summary = service.get_confidential_projects_summary()
        
        assert 'total_confidential_projects' in summary

    def test_check_sync_safety_detailed(self):
        """测试详细安全检查"""
        from backend.services.sync_permission_service import SyncPermissionService
        
        service = SyncPermissionService()
        
        result = service.check_sync_safety("test-project")
        
        assert 'is_safe' in result
        assert 'checks' in result


class TestDocumentFetcherUltraCoverage:
    """DocumentFetcher 超高覆盖率测试"""

    @pytest.fixture
    def temp_project(self):
        temp = tempfile.mkdtemp()
        yield temp
        shutil.rmtree(temp, ignore_errors=True)

    def test_search_path_match(self, temp_project):
        """测试路径匹配搜索"""
        from backend.services.document_fetcher import DocumentFetcher
        
        with open(os.path.join(temp_project, "readme.md"), "w") as f:
            f.write("content")
        
        fetcher = DocumentFetcher(base_path=temp_project)
        
        results = fetcher.search("readme", project_name=os.path.basename(temp_project))
        
        assert isinstance(results, list)

    def test_fetch_docs_with_ignore(self, temp_project):
        """测试忽略文件拉取"""
        from backend.services.document_fetcher import DocumentFetcher
        
        os.makedirs(os.path.join(temp_project, ".git"))
        with open(os.path.join(temp_project, ".git", "config"), "w") as f:
            f.write("test")
        
        os.makedirs(os.path.join(temp_project, "node_modules"))
        with open(os.path.join(temp_project, "node_modules", "test.js"), "w") as f:
            f.write("test")
        
        fetcher = DocumentFetcher()
        docs = fetcher.fetch_docs(temp_project)
        
        assert isinstance(docs, list)

    def test_category_other(self, temp_project):
        """测试其他分类"""
        from backend.services.document_fetcher import DocumentFetcher
        
        fetcher = DocumentFetcher()
        
        category = fetcher._get_category("data/file.bin")
        
        assert category == "其他"


class TestConfidentialCheckerUltraCoverage:
    """ConfidentialChecker 超高覆盖率测试"""

    def test_check_files_large_dir(self):
        """测试大目录检查"""
        from backend.services.confidential_checker import SensitiveContentChecker
        
        temp = tempfile.mkdtemp()
        try:
            for i in range(10):
                with open(os.path.join(temp, f"file{i}.txt"), "w") as f:
                    f.write(f"content {i}")
            
            checker = SensitiveContentChecker()
            result = checker.check_files(temp, recursive=True)
            
            assert result is not None
        finally:
            shutil.rmtree(temp, ignore_errors=True)

    def test_keyword_edge_cases(self):
        """测试关键词边界情况"""
        from backend.services.confidential_checker import SensitiveContentChecker
        
        checker = SensitiveContentChecker()
        
        result = checker.check_content("")
        assert result is False
        
        result = checker.check_content("NORMAL")
        assert result is False

    def test_add_remove_keyword(self):
        """测试添加移除关键词"""
        from backend.services.confidential_checker import SensitiveContentChecker
        
        checker = SensitiveContentChecker()
        
        original = len(checker.keywords)
        checker.add_keyword("testkw")
        assert len(checker.keywords) == original + 1
        
        checker.remove_keyword("testkw")
        assert len(checker.keywords) == original


class TestOcCollabClientUltraCoverage:
    """OcCollabClient 超高覆盖率测试"""

    @patch('backend.services.oc_collab_client.shutil.which')
    @patch('backend.services.oc_collab_client.subprocess.run')
    def test_status_parse_variations(self, mock_run, mock_which):
        """测试状态解析变体"""
        from backend.services.oc_collab_client import OcCollabClient
        
        mock_which.return_value = "/usr/bin/oc-collab"
        
        mock_result = Mock()
        mock_result.returncode = 0
        mock_result.stdout = '{"status": "completed", "progress": 100}'
        mock_result.stderr = ''
        mock_run.return_value = mock_result
        
        client = OcCollabClient()
        status = client.get_project_status("test-project")
        
        assert status is not None

    @patch('backend.services.oc_collab_client.shutil.which')
    @patch('backend.services.oc_collab_client.subprocess.run')
    def test_progress_parse(self, mock_run, mock_which):
        """测试进度解析"""
        from backend.services.oc_collab_client import OcCollabClient
        
        mock_which.return_value = "/usr/bin/oc-collab"
        
        mock_result = Mock()
        mock_result.returncode = 0
        mock_result.stdout = '{"requirements_total": 0, "requirements_completed": 0}'
        mock_result.stderr = ''
        mock_run.return_value = mock_result
        
        client = OcCollabClient()
        progress = client.get_project_progress("test-project")
        
        assert progress is not None


if __name__ == "__main__":
    pytest.main([__file__, "-v", "--tb=short"])
