"""
PM-Agent v1.2.0 Additional Coverage Tests - Part 2

用于进一步提升v1.2.0新服务代码覆盖率
"""
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 TestDocumentFetcherCoverage2:
    """DocumentFetcher 更多覆盖率测试"""

    @pytest.fixture
    def temp_project(self):
        temp = tempfile.mkdtemp()
        
        os.makedirs(os.path.join(temp, "docs", "api"), exist_ok=True)
        os.makedirs(os.path.join(temp, "src", "backend"), exist_ok=True)
        
        with open(os.path.join(temp, "README.md"), "w") as f:
            f.write("# Test Project")
        
        with open(os.path.join(temp, "docs", "api", "rest.md"), "w") as f:
            f.write("# API Documentation")
        
        with open(os.path.join(temp, "src", "backend", "main.py"), "w") as f:
            f.write("print('hello')")
        
        yield temp
        shutil.rmtree(temp, ignore_errors=True)

    def test_search_with_doc_type(self, temp_project):
        """测试按文档类型搜索"""
        from backend.services.document_fetcher import DocumentFetcher
        
        fetcher = DocumentFetcher(base_path=temp_project)
        results = fetcher.search("test", project_name=os.path.basename(temp_project), doc_type="文档")
        
        assert isinstance(results, list)

    def test_search_with_content_match(self, temp_project):
        """测试内容匹配搜索"""
        from backend.services.document_fetcher import DocumentFetcher
        
        fetcher = DocumentFetcher(base_path=temp_project)
        results = fetcher.search("Test", project_name=os.path.basename(temp_project))
        
        assert isinstance(results, list)

    def test_fetch_docs_non_recursive(self, temp_project):
        """测试非递归拉取"""
        from backend.services.document_fetcher import DocumentFetcher
        
        fetcher = DocumentFetcher()
        docs = fetcher.fetch_docs(temp_project, recursive=False)
        
        assert isinstance(docs, list)

    def test_get_category_src(self, temp_project):
        """测试源代码分类"""
        from backend.services.document_fetcher import DocumentFetcher
        
        fetcher = DocumentFetcher()
        
        category = fetcher._get_category("src/main.py")
        assert category == "源代码"

    def test_get_category_docs(self, temp_project):
        """测试文档分类"""
        from backend.services.document_fetcher import DocumentFetcher
        
        fetcher = DocumentFetcher()
        
        category = fetcher._get_category("docs/guide.md")
        assert category == "文档"

    def test_get_category_config(self, temp_project):
        """测试配置分类"""
        from backend.services.document_fetcher import DocumentFetcher
        
        fetcher = DocumentFetcher()
        
        category = fetcher._get_category("config/app.json")
        assert category == "配置"

    def test_get_category_frontend(self, temp_project):
        """测试前端分类"""
        from backend.services.document_fetcher import DocumentFetcher
        
        fetcher = DocumentFetcher()
        
        category = fetcher._get_category("frontend/App.vue")
        assert category == "前端"

    def test_should_ignore_pattern(self):
        """测试忽略模式"""
        from backend.services.document_fetcher import DocumentFetcher
        
        fetcher = DocumentFetcher()
        
        assert fetcher._should_ignore("test.pyc") is True
        assert fetcher._should_ignore("test.pyo") is True

    def test_create_document_readable_file(self, temp_project):
        """测试创建可读文档对象"""
        from backend.services.document_fetcher import DocumentFetcher
        
        fetcher = DocumentFetcher()
        
        file_path = os.path.join(temp_project, "README.md")
        doc = fetcher._create_document(file_path, temp_project)
        
        assert doc is not None
        assert doc.content is not None

    def test_create_document_binary_file(self, temp_project):
        """测试创建二进制文档对象"""
        from backend.services.document_fetcher import DocumentFetcher
        
        fetcher = DocumentFetcher()
        
        file_path = os.path.join(temp_project, "test.bin")
        with open(file_path, "wb") as f:
            f.write(b"\x00\x01\x02")
        
        doc = fetcher._create_document(file_path, temp_project)
        
        assert doc is not None


class TestStatusFeedbackServiceCoverage2:
    """StatusFeedbackService 更多覆盖率测试"""

    def test_get_active_project_names_with_storage(self):
        """测试有存储获取活跃项目名"""
        from backend.services.status_feedback_service import StatusFeedbackService
        
        mock_storage = Mock()
        mock_storage.get_active_project_names.return_value = ["proj1", "proj2"]
        
        service = StatusFeedbackService(storage=mock_storage)
        
        names = service._get_active_project_names()
        
        assert names == ["proj1", "proj2"]

    def test_get_active_project_names_exception(self):
        """测试获取活跃项目名异常"""
        from backend.services.status_feedback_service import StatusFeedbackService
        
        mock_storage = Mock()
        mock_storage.get_active_project_names.side_effect = Exception("Error")
        
        service = StatusFeedbackService(storage=mock_storage)
        
        names = service._get_active_project_names()
        
        assert names == []

    def test_update_local_status_with_storage(self):
        """测试有存储更新本地状态"""
        from backend.services.status_feedback_service import StatusFeedbackService, ChangeEvent, ChangeType
        
        mock_storage = Mock()
        mock_storage.save_change.return_value = True
        
        service = StatusFeedbackService(storage=mock_storage)
        
        event = ChangeEvent(
            project_name="test",
            change_type=ChangeType.BUG,
            change_id="BUG-001",
            title="Test",
            old_status="open",
            new_status="closed"
        )
        
        count = service.update_local_status([event])
        
        assert count == 1

    def test_update_local_status_exception(self):
        """测试更新本地状态异常"""
        from backend.services.status_feedback_service import StatusFeedbackService, ChangeEvent, ChangeType
        
        mock_storage = Mock()
        mock_storage.save_change.side_effect = Exception("Error")
        
        service = StatusFeedbackService(storage=mock_storage)
        
        event = ChangeEvent(
            project_name="test",
            change_type=ChangeType.BUG,
            change_id="BUG-001",
            title="Test",
            old_status="open",
            new_status="closed"
        )
        
        count = service.update_local_status([event])
        
        assert count == 0

    def test_unregister_callback(self):
        """测试注销回调"""
        from backend.services.status_feedback_service import StatusFeedbackService
        
        service = StatusFeedbackService()
        
        def callback(event):
            pass
        
        service.register_callback(callback)
        service.unregister_callback(callback)
        
        assert len(service._callbacks) == 0

    def test_get_change_history_exception(self):
        """测试获取变更历史异常"""
        from backend.services.status_feedback_service import StatusFeedbackService
        
        mock_storage = Mock()
        mock_storage.get_change_history.side_effect = Exception("Error")
        
        service = StatusFeedbackService(storage=mock_storage)
        
        history = service.get_change_history()
        
        assert history == []

    def test_poll_changes_exception(self):
        """测试轮询异常"""
        from backend.services.status_feedback_service import StatusFeedbackService
        
        mock_client = Mock()
        mock_client.get_changes.side_effect = Exception("Error")
        
        service = StatusFeedbackService(client=mock_client)
        
        changes = service.poll_changes("test-project")
        
        assert changes == []


class TestProgressServiceCoverage2:
    """ProgressService 更多覆盖率测试"""

    def test_get_project_progress_basic(self):
        """测试获取项目进度基本功能"""
        from backend.services.progress_service import ProgressService
        
        service = ProgressService()
        
        progress = service.get_project_progress("test-project")
        
        assert progress is not None

    def test_get_all_projects_summary_with_names(self):
        """测试有项目名的汇总"""
        from backend.services.progress_service import ProgressService
        
        service = ProgressService()
        
        summary = service.get_all_projects_summary(project_names=["proj1", "proj2"])
        
        assert summary.total_projects == 0

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

    def test_set_weights_invalid_sum(self):
        """测试无效权重和"""
        from backend.services.progress_service import ProgressService
        
        service = ProgressService()
        service.set_weights(requirements=0.5, bugs=0.3, todos=0.3)
        
        assert service._requirements_weight > 0

    def test_calculate_progress_edge_cases(self):
        """测试进度计算边界情况"""
        from backend.services.progress_service import ProgressService, ProjectProgress, RequirementsProgress
        
        service = ProgressService()
        
        progress = ProjectProgress(
            project_name="test",
            requirements=RequirementsProgress(total=0, completed=0)
        )
        
        result = service.calculate_overall_progress(progress)
        
        assert result == 0


class TestIssueSyncServiceCoverage2:
    """IssueSyncService 更多覆盖率测试"""

    def test_sync_bugs_with_client(self):
        """测试有客户端同步BUG"""
        from backend.services.issue_sync_service import IssueSyncService
        
        mock_client = Mock()
        mock_client.get_project_bugs.return_value = []
        
        service = IssueSyncService(client=mock_client)
        
        result = service.sync_bugs("test-project")
        
        assert result == []

    def test_sync_requirements_with_client(self):
        """测试有客户端同步需求"""
        from backend.services.issue_sync_service import IssueSyncService
        
        mock_client = Mock()
        mock_client.get_project_requirements.return_value = []
        
        service = IssueSyncService(client=mock_client)
        
        result = service.sync_requirements("test-project")
        
        assert result == []

    def test_save_bugs_to_db(self):
        """测试保存BUG到数据库"""
        from backend.services.issue_sync_service import IssueSyncService, SyncBug
        from unittest.mock import MagicMock
        
        mock_db = MagicMock()
        
        service = IssueSyncService()
        
        bugs = [
            SyncBug(
                id="BUG-001",
                title="Test",
                status="open",
                severity="high"
            )
        ]
        
        service.save_bugs_to_db("test-project", bugs, db_session=mock_db)


class TestConfidentialCheckerCoverage2:
    """ConfidentialChecker 更多覆盖率测试"""

    def test_check_files_recursive(self):
        """测试递归检查文件"""
        from backend.services.confidential_checker import SensitiveContentChecker
        
        checker = SensitiveContentChecker()
        
        temp = tempfile.mkdtemp()
        try:
            os.makedirs(os.path.join(temp, "subdir"))
            with open(os.path.join(temp, "file1.txt"), "w") as f:
                f.write("normal content")
            with open(os.path.join(temp, "subdir", "file2.txt"), "w") as f:
                f.write("content")
            
            result = checker.check_files(temp, recursive=True)
            
            assert result is not None
        finally:
            shutil.rmtree(temp, ignore_errors=True)

    def test_check_files_non_recursive(self):
        """测试非递归检查文件"""
        from backend.services.confidential_checker import SensitiveContentChecker
        
        checker = SensitiveContentChecker()
        
        temp = tempfile.mkdtemp()
        try:
            os.makedirs(os.path.join(temp, "subdir"))
            with open(os.path.join(temp, "file1.txt"), "w") as f:
                f.write("content")
            
            result = checker.check_files(temp, recursive=False)
            
            assert result is not None
        finally:
            shutil.rmtree(temp, ignore_errors=True)

    def test_add_keyword(self):
        """测试添加关键词"""
        from backend.services.confidential_checker import SensitiveContentChecker
        
        checker = SensitiveContentChecker()
        
        original_count = len(checker.keywords)
        checker.add_keyword("test_keyword")
        
        assert len(checker.keywords) > original_count

    def test_remove_keyword(self):
        """测试移除关键词"""
        from backend.services.confidential_checker import SensitiveContentChecker
        
        checker = SensitiveContentChecker()
        keyword = "test_remove"
        checker.add_keyword(keyword)
        
        checker.remove_keyword(keyword)
        
        assert keyword.lower() not in checker.keywords

    def test_check_content_case_insensitive(self):
        """测试大小写不敏感检查"""
        from backend.services.confidential_checker import SensitiveContentChecker
        
        checker = SensitiveContentChecker()
        
        result = checker.check_content("This is CONFIDENTIAL information")
        
        assert result is True


class TestSyncPermissionServiceCoverage2:
    """SyncPermissionService 更多覆盖率测试"""

    def test_get_sync_recommendation(self):
        """测试获取同步建议"""
        from backend.services.sync_permission_service import SyncPermissionService
        
        service = SyncPermissionService()
        
        result = service.get_sync_recommendation("test-project")
        
        assert 'can_sync' in result

    def test_sync_permission_service_init(self):
        """测试服务初始化"""
        from backend.services.sync_permission_service import SyncPermissionService
        
        service = SyncPermissionService()
        
        assert service is not None


class TestGitSyncServiceCoverage2:
    """GitSyncService 更多覆盖率测试"""

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

    def test_get_last_sync_time(self, temp_dir):
        """测试获取最后同步时间"""
        from backend.services.git_sync_service import GitSyncService
        
        service = GitSyncService(base_path=temp_dir)
        
        last_time = service.get_last_sync_time("test-project")
        
        assert last_time is None

    def test_get_sync_status(self, temp_dir):
        """测试获取同步状态"""
        from backend.services.git_sync_service import GitSyncService
        
        service = GitSyncService(base_path=temp_dir)
        
        status = service.get_sync_status("nonexistent-project")
        
        assert 'error' in status or 'project_name' in status

    def test_get_repo_info_not_exists(self, temp_dir):
        """测试获取不存在的仓库信息"""
        from backend.services.git_sync_service import GitSyncService
        
        service = GitSyncService(base_path=temp_dir)
        
        info = service.get_repo_info(os.path.join(temp_dir, "nonexistent"))
        
        assert info is None

    def test_sync_project_not_exists(self, temp_dir):
        """测试同步不存在的项目"""
        from backend.services.git_sync_service import GitSyncService
        
        service = GitSyncService(base_path=temp_dir)
        
        result = service.sync_project("nonexistent")
        
        assert result.success is False


class TestOcCollabClientCoverage2:
    """OcCollabClient 更多覆盖率测试"""

    @patch('backend.services.oc_collab_client.shutil.which')
    @patch('backend.services.oc_collab_client.subprocess.run')
    def test_get_project_todos(self, mock_run, mock_which):
        """测试获取项目TODO"""
        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 = '{"todos": []}'
        mock_result.stderr = ''
        mock_run.return_value = mock_result
        
        client = OcCollabClient()
        todos = client.get_project_todos("test-project")
        
        assert isinstance(todos, list)

    @patch('backend.services.oc_collab_client.shutil.which')
    @patch('backend.services.oc_collab_client.subprocess.run')
    def test_get_project_progress(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": 10, "requirements_completed": 5}'
        mock_result.stderr = ''
        mock_run.return_value = mock_result
        
        client = OcCollabClient()
        progress = client.get_project_progress("test-project")
        
        assert progress is not None

    @patch('backend.services.oc_collab_client.shutil.which')
    @patch('backend.services.oc_collab_client.subprocess.run')
    def test_get_project_bugs(self, mock_run, mock_which):
        """测试获取项目BUG"""
        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 = '{"bugs": []}'
        mock_result.stderr = ''
        mock_run.return_value = mock_result
        
        client = OcCollabClient()
        bugs = client.get_project_bugs("test-project")
        
        assert isinstance(bugs, list)

    @patch('backend.services.oc_collab_client.shutil.which')
    @patch('backend.services.oc_collab_client.subprocess.run')
    def test_get_project_requirements(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": []}'
        mock_result.stderr = ''
        mock_run.return_value = mock_result
        
        client = OcCollabClient()
        requirements = client.get_project_requirements("test-project")
        
        assert isinstance(requirements, list)


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