import pytest
from unittest.mock import MagicMock, patch
from app.services.tag_service import TagService, TAG_EXTRACTION_PROMPT, TAG_RELATION_PROMPT


class TestTagService:
    """TagService 单元测试"""

    def test_parse_tag_path_single_level(self):
        """测试单层标签路径解析"""
        service = TagService(db=MagicMock(), llm_client=MagicMock())
        short_name, level, parent_path = service.parse_tag_path("Python")

        assert short_name == "Python"
        assert level == 1
        assert parent_path is None

    def test_parse_tag_path_nested(self):
        """测试多层标签路径解析"""
        service = TagService(db=MagicMock(), llm_client=MagicMock())
        short_name, level, parent_path = service.parse_tag_path("编程语言/Python/异步")

        assert short_name == "异步"
        assert level == 3
        assert parent_path == "编程语言/Python"

    def test_parse_tag_path_strips_slashes(self):
        """测试标签路径斜杠处理"""
        service = TagService(db=MagicMock(), llm_client=MagicMock())
        short_name, level, parent_path = service.parse_tag_path("/后端/框架/")

        assert short_name == "框架"
        assert level == 2
        assert parent_path == "后端"

    def test_parse_tag_path_empty_parts(self):
        """测试空路径部分处理"""
        service = TagService(db=MagicMock(), llm_client=MagicMock())
        short_name, level, parent_path = service.parse_tag_path("a/b/c/d")

        assert short_name == "d"
        assert level == 4
        assert parent_path == "a/b/c"

    @patch('app.services.tag_service.KGNode')
    @patch('app.services.tag_service.KGEdge')
    def test_get_or_create_tag_node_creates_new(self, mock_edge, mock_node):
        """测试创建新标签节点"""
        mock_db = MagicMock()
        mock_db.query.return_value.filter.return_value.first.return_value = None

        service = TagService(db=mock_db, llm_client=MagicMock())
        tag_info = {"path": "Python", "description": "Python语言"}

        result = service.get_or_create_tag_node(tag_info, "user_123")

        assert result is not None
        mock_db.add.assert_called_once()
        mock_db.flush.assert_called_once()

    @patch('app.services.tag_service.KGNode')
    def test_get_or_create_tag_node_returns_existing(self, mock_node):
        """测试返回已存在的标签节点"""
        mock_db = MagicMock()
        mock_existing = MagicMock()
        mock_db.query.return_value.filter.return_value.first.return_value = mock_existing

        service = TagService(db=mock_db, llm_client=MagicMock())
        tag_info = {"path": "Python", "description": "Python语言"}

        result = service.get_or_create_tag_node(tag_info, "user_123")

        assert result == mock_existing
        mock_db.add.assert_not_called()

    def test_ensure_parent_tags_creates_parents(self):
        """测试自动创建父标签"""
        mock_db = MagicMock()
        mock_db.query.return_value.filter.return_value.first.return_value = None

        service = TagService(db=mock_db, llm_client=MagicMock())

        with patch.object(service, 'get_or_create_tag_node') as mock_create:
            mock_create.return_value = MagicMock()
            result = service.ensure_parent_tags("a/b/c", "user_123")
            assert mock_create.call_count == 2

    def test_ensure_parent_tags_single_level(self):
        """测试单层标签不创建父标签"""
        mock_db = MagicMock()
        service = TagService(db=mock_db, llm_client=MagicMock())

        with patch.object(service, 'get_or_create_tag_node') as mock_create:
            mock_create.return_value = MagicMock()
            result = service.ensure_parent_tags("Python", "user_123")
            assert mock_create.call_count == 0

    @patch('app.services.tag_service.KGNode')
    def test_get_related_content_empty_tags(self, mock_kg_node):
        """测试空标签列表返回空结果"""
        mock_db = MagicMock()
        mock_db.query.return_value.filter.return_value.all.return_value = []

        service = TagService(db=mock_db, llm_client=MagicMock())
        result = service.get_related_content([], "user_123")

        assert result == []

    def test_tag_extraction_prompt_format(self):
        """测试标签提取提示词格式"""
        assert "层级路径格式" in TAG_EXTRACTION_PROMPT
        assert "3-8 个标签" in TAG_EXTRACTION_PROMPT
        assert "{content}" in TAG_EXTRACTION_PROMPT

    def test_tag_relation_prompt_format(self):
        """测试标签关系提示词格式"""
        assert "parent_of" in TAG_RELATION_PROMPT
        assert "related_to" in TAG_RELATION_PROMPT
        assert "synonym_of" in TAG_RELATION_PROMPT
        assert "{tag_paths}" in TAG_RELATION_PROMPT
