# tests/test_todo_system.py
"""
Todo系统全面测试
覆盖：todo_manager.py、todo_tools.py 的核心功能
"""

import asyncio
import pytest
import tempfile
import shutil
from pathlib import Path

# 确保可以导入项目模块
import sys
sys.path.insert(0, str(Path(__file__).parent.parent))

from aacode.utils.todo_manager import TodoManager, get_todo_manager, _todo_managers
from aacode.tools.todo_tools import TodoTools


@pytest.fixture
def tmp_project(tmp_path):
    """创建临时项目目录"""
    project_dir = tmp_path / "test_project"
    project_dir.mkdir()
    return project_dir


@pytest.fixture
def todo_manager(tmp_project):
    """创建 TodoManager 实例"""
    # 清除全局缓存，避免测试间干扰
    _todo_managers.clear()
    return TodoManager(tmp_project)


@pytest.fixture
def todo_tools(tmp_project):
    """创建 TodoTools 实例"""
    _todo_managers.clear()
    return TodoTools(tmp_project)


# ==================== TodoManager 基础测试 ====================

class TestTodoManagerCreate:
    """测试创建待办清单"""

    @pytest.mark.asyncio
    async def test_create_todo_list_basic(self, todo_manager):
        """基本创建"""
        path = await todo_manager.create_todo_list("测试任务")
        assert path != ""
        assert todo_manager.current_todo_file is not None
        assert todo_manager.current_todo_file.exists()

    @pytest.mark.asyncio
    async def test_create_todo_list_no_record_section(self, todo_manager):
        """创建的文件不应包含 ## 记录 section"""
        await todo_manager.create_todo_list("测试任务")
        content = todo_manager.current_todo_file.read_text(encoding="utf-8")
        assert "## 记录" not in content
        assert "## 待办" in content
        assert "## 已完成" in content

    @pytest.mark.asyncio
    async def test_create_todo_list_resets_counter(self, todo_manager):
        """创建新清单应重置计数器"""
        await todo_manager.create_todo_list("任务1")
        await todo_manager.add_todo_item("事项1")
        assert todo_manager.todo_counter == 1

        await todo_manager.create_todo_list("任务2")
        assert todo_manager.todo_counter == 0

    @pytest.mark.asyncio
    async def test_create_todo_list_custom_project_name(self, todo_manager):
        """自定义项目名称"""
        await todo_manager.create_todo_list("测试", project_name="my_project")
        content = todo_manager.current_todo_file.read_text(encoding="utf-8")
        assert "my_project" in content


# ==================== TodoManager add_todo_item 测试 ====================

class TestTodoManagerAdd:
    """测试添加待办事项"""

    @pytest.mark.asyncio
    async def test_add_returns_todo_id(self, todo_manager):
        """add_todo_item 应返回 todo_id"""
        await todo_manager.create_todo_list("测试")
        todo_id = await todo_manager.add_todo_item("写代码")
        assert todo_id == "t1"

    @pytest.mark.asyncio
    async def test_add_increments_id(self, todo_manager):
        """连续添加应递增 ID"""
        await todo_manager.create_todo_list("测试")
        id1 = await todo_manager.add_todo_item("任务1")
        id2 = await todo_manager.add_todo_item("任务2")
        id3 = await todo_manager.add_todo_item("任务3")
        assert id1 == "t1"
        assert id2 == "t2"
        assert id3 == "t3"

    @pytest.mark.asyncio
    async def test_add_writes_id_tag_to_file(self, todo_manager):
        """文件中应包含 [#tN] 标记"""
        await todo_manager.create_todo_list("测试")
        await todo_manager.add_todo_item("写代码")
        content = todo_manager.current_todo_file.read_text(encoding="utf-8")
        assert "[#t1]" in content
        assert "写代码" in content

    @pytest.mark.asyncio
    async def test_add_with_priority(self, todo_manager):
        """不同优先级的标记"""
        await todo_manager.create_todo_list("测试")
        await todo_manager.add_todo_item("高优先", priority="high")
        await todo_manager.add_todo_item("低优先", priority="low")
        content = todo_manager.current_todo_file.read_text(encoding="utf-8")
        assert "🔴" in content
        assert "🟢" in content

    @pytest.mark.asyncio
    async def test_add_without_active_file_returns_none(self, todo_manager):
        """没有活动文件时返回 None"""
        result = await todo_manager.add_todo_item("测试")
        assert result is None

    @pytest.mark.asyncio
    async def test_add_format_includes_category(self, todo_manager):
        """文件格式应包含分类"""
        await todo_manager.create_todo_list("测试")
        await todo_manager.add_todo_item("修bug", category="修复")
        content = todo_manager.current_todo_file.read_text(encoding="utf-8")
        assert "**修复**" in content


# ==================== TodoManager mark_todo_completed 测试 ====================

class TestTodoManagerComplete:
    """测试标记完成"""

    @pytest.mark.asyncio
    async def test_complete_by_todo_id(self, todo_manager):
        """通过 todo_id 精确标记完成"""
        await todo_manager.create_todo_list("测试")
        todo_id = await todo_manager.add_todo_item("写代码")
        result = await todo_manager.mark_todo_completed(todo_id=todo_id)
        assert result is True

        content = todo_manager.current_todo_file.read_text(encoding="utf-8")
        assert "- [x]" in content
        assert "✅" in content

    @pytest.mark.asyncio
    async def test_complete_by_todo_id_specific(self, todo_manager):
        """多个事项时，todo_id 只标记对应的那个"""
        await todo_manager.create_todo_list("测试")
        id1 = await todo_manager.add_todo_item("任务A")
        id2 = await todo_manager.add_todo_item("任务B")

        await todo_manager.mark_todo_completed(todo_id=id2)

        content = todo_manager.current_todo_file.read_text(encoding="utf-8")
        lines = content.split("\n")

        # 任务A 应该还是未完成
        task_a_lines = [l for l in lines if "任务A" in l and l.strip().startswith("- [")]
        assert any("- [ ]" in l for l in task_a_lines)

        # 任务B 应该已完成
        task_b_lines = [l for l in lines if "任务B" in l and l.strip().startswith("- [")]
        assert any("- [x]" in l for l in task_b_lines)

    @pytest.mark.asyncio
    async def test_complete_by_text_pattern(self, todo_manager):
        """通过文本模式匹配标记完成（fallback）"""
        await todo_manager.create_todo_list("测试")
        await todo_manager.add_todo_item("编写helloworld程序")
        result = await todo_manager.mark_todo_completed(item_pattern="helloworld")
        assert result is True

    @pytest.mark.asyncio
    async def test_complete_by_fuzzy_keywords(self, todo_manager):
        """关键词模糊匹配（第三级 fallback）"""
        await todo_manager.create_todo_list("测试")
        await todo_manager.add_todo_item("编写一个简单的Python程序，打印Hello World")
        # 用一个不完全匹配的模式
        result = await todo_manager.mark_todo_completed(item_pattern="打印Hello")
        assert result is True

    @pytest.mark.asyncio
    async def test_complete_nonexistent_returns_false(self, todo_manager):
        """标记不存在的事项返回 False"""
        await todo_manager.create_todo_list("测试")
        await todo_manager.add_todo_item("任务A")
        result = await todo_manager.mark_todo_completed(todo_id="t999")
        assert result is False

    @pytest.mark.asyncio
    async def test_complete_adds_to_completed_section(self, todo_manager):
        """完成的事项应出现在已完成 section"""
        await todo_manager.create_todo_list("测试")
        todo_id = await todo_manager.add_todo_item("写代码")
        await todo_manager.mark_todo_completed(todo_id=todo_id)

        content = todo_manager.current_todo_file.read_text(encoding="utf-8")
        # 已完成 section 应包含事项描述和 todo_id
        completed_idx = content.index("## 已完成")
        completed_section = content[completed_idx:]
        assert "写代码" in completed_section
        assert "[#t1]" in completed_section

    @pytest.mark.asyncio
    async def test_complete_without_any_params_returns_false(self, todo_manager):
        """不传任何参数返回 False"""
        await todo_manager.create_todo_list("测试")
        await todo_manager.add_todo_item("任务")
        result = await todo_manager.mark_todo_completed()
        assert result is False


# ==================== TodoManager _load_counter 测试 ====================

class TestTodoManagerCounter:
    """测试 ID 计数器恢复"""

    @pytest.mark.asyncio
    async def test_load_counter_from_existing_file(self, todo_manager):
        """从已有文件恢复计数器"""
        await todo_manager.create_todo_list("测试")
        await todo_manager.add_todo_item("任务1")
        await todo_manager.add_todo_item("任务2")
        await todo_manager.add_todo_item("任务3")
        assert todo_manager.todo_counter == 3

        # 模拟重新加载（重置计数器，下次 add 时会从文件恢复）
        todo_manager.todo_counter = 0
        id4 = await todo_manager.add_todo_item("任务4")
        assert id4 == "t4"  # 应该从 t3 继续
        assert todo_manager.todo_counter == 4

    @pytest.mark.asyncio
    async def test_load_counter_empty_file(self, todo_manager):
        """空文件计数器从 0 开始"""
        await todo_manager.create_todo_list("测试")
        content = todo_manager.current_todo_file.read_text(encoding="utf-8")
        todo_manager._load_counter(content)
        assert todo_manager.todo_counter == 0


# ==================== TodoManager add_execution_record 测试 ====================

class TestTodoManagerExecutionRecord:
    """测试执行记录（已废弃）"""

    @pytest.mark.asyncio
    async def test_execution_record_is_noop(self, todo_manager):
        """add_execution_record 应该是 no-op"""
        await todo_manager.create_todo_list("测试")
        result = await todo_manager.add_execution_record("一些记录")
        assert result is True

        # 文件内容不应包含这条记录
        content = todo_manager.current_todo_file.read_text(encoding="utf-8")
        assert "一些记录" not in content


# ==================== TodoManager get_todo_summary 测试 ====================

class TestTodoManagerSummary:
    """测试摘要功能"""

    @pytest.mark.asyncio
    async def test_summary_counts(self, todo_manager):
        """摘要应正确统计"""
        await todo_manager.create_todo_list("测试")
        await todo_manager.add_todo_item("任务1")
        id2 = await todo_manager.add_todo_item("任务2")
        await todo_manager.add_todo_item("任务3")
        await todo_manager.mark_todo_completed(todo_id=id2)

        summary = await todo_manager.get_todo_summary()
        assert summary["total_todos"] == 3
        assert summary["completed_todos"] == 1
        assert summary["pending_todos"] == 2


# ==================== TodoTools 集成测试 ====================

class TestTodoToolsAdd:
    """测试 TodoTools.add_todo_item"""

    @pytest.mark.asyncio
    async def test_add_returns_todo_id_in_result(self, todo_tools, tmp_project):
        """返回结果应包含 todo_id 和使用提示"""
        # 先创建待办清单
        _todo_managers.clear()
        manager = get_todo_manager(tmp_project)
        await manager.create_todo_list("测试")

        result = await todo_tools.add_todo_item(description="写代码")
        assert result["success"] is True
        assert "todo_id" in result
        assert result["todo_id"] == "t1"
        assert "[#t1]" in result["message"]
        assert "mark_todo_completed" in result["message"]

    @pytest.mark.asyncio
    async def test_add_with_title_alias(self, todo_tools, tmp_project):
        """使用 title 别名"""
        _todo_managers.clear()
        manager = get_todo_manager(tmp_project)
        await manager.create_todo_list("测试")

        result = await todo_tools.add_todo_item(title="写代码")
        assert result["success"] is True
        assert result["todo_id"] == "t1"


class TestTodoToolsComplete:
    """测试 TodoTools.mark_todo_completed"""

    @pytest.mark.asyncio
    async def test_complete_with_todo_id(self, todo_tools, tmp_project):
        """通过 todo_id 标记完成"""
        _todo_managers.clear()
        manager = get_todo_manager(tmp_project)
        await manager.create_todo_list("测试")

        add_result = await todo_tools.add_todo_item(description="写代码")
        todo_id = add_result["todo_id"]

        complete_result = await todo_tools.mark_todo_completed(todo_id=todo_id)
        assert complete_result["success"] is True
        assert complete_result["todo_id"] == todo_id

    @pytest.mark.asyncio
    async def test_complete_with_text_fallback(self, todo_tools, tmp_project):
        """文本 fallback 匹配"""
        _todo_managers.clear()
        manager = get_todo_manager(tmp_project)
        await manager.create_todo_list("测试")

        await todo_tools.add_todo_item(description="编写helloworld程序")
        result = await todo_tools.mark_todo_completed(item_pattern="helloworld")
        assert result["success"] is True

    @pytest.mark.asyncio
    async def test_complete_without_params_fails(self, todo_tools, tmp_project):
        """不传参数应失败"""
        _todo_managers.clear()
        manager = get_todo_manager(tmp_project)
        await manager.create_todo_list("测试")

        result = await todo_tools.mark_todo_completed()
        assert result["success"] is False

    @pytest.mark.asyncio
    async def test_complete_nonexistent_fails(self, todo_tools, tmp_project):
        """标记不存在的事项应失败"""
        _todo_managers.clear()
        manager = get_todo_manager(tmp_project)
        await manager.create_todo_list("测试")
        await todo_tools.add_todo_item(description="任务A")

        result = await todo_tools.mark_todo_completed(todo_id="t999")
        assert result["success"] is False


class TestTodoToolsExecutionRecord:
    """测试 TodoTools.add_execution_record（已废弃）"""

    @pytest.mark.asyncio
    async def test_execution_record_returns_success(self, todo_tools):
        """废弃的方法应静默成功"""
        result = await todo_tools.add_execution_record(record="一些记录")
        assert result["success"] is True


# ==================== 端到端场景测试 ====================

class TestEndToEnd:
    """模拟真实使用场景"""

    @pytest.mark.asyncio
    async def test_helloworld_scenario(self, todo_tools, tmp_project):
        """模拟 helloworld 任务的完整流程"""
        _todo_managers.clear()
        manager = get_todo_manager(tmp_project)
        await manager.create_todo_list("请写一个python的helloworld程序")

        # 模型添加待办
        add_result = await todo_tools.add_todo_item(
            description="编写一个简单的Python程序，打印'Hello, World!'",
            priority="high",
        )
        assert add_result["success"] is True
        todo_id = add_result["todo_id"]
        assert todo_id == "t1"

        # 模型完成任务后，用 todo_id 标记完成
        complete_result = await todo_tools.mark_todo_completed(todo_id=todo_id)
        assert complete_result["success"] is True

        # 验证文件状态
        content = manager.current_todo_file.read_text(encoding="utf-8")
        assert "- [x]" in content
        assert "## 记录" not in content

        # 验证摘要
        summary = await manager.get_todo_summary()
        assert summary["total_todos"] == 1
        assert summary["completed_todos"] == 1
        assert summary["completion_rate"] == 100.0

    @pytest.mark.asyncio
    async def test_multi_task_scenario(self, todo_tools, tmp_project):
        """模拟多任务场景"""
        _todo_managers.clear()
        manager = get_todo_manager(tmp_project)
        await manager.create_todo_list("构建用户系统")

        # 添加多个任务
        r1 = await todo_tools.add_todo_item(description="设计数据库schema", priority="high")
        r2 = await todo_tools.add_todo_item(description="实现用户注册API", priority="high")
        r3 = await todo_tools.add_todo_item(description="编写单元测试", priority="medium")

        # 按顺序完成
        await todo_tools.mark_todo_completed(todo_id=r1["todo_id"])
        await todo_tools.mark_todo_completed(todo_id=r2["todo_id"])

        summary = await manager.get_todo_summary()
        assert summary["total_todos"] == 3
        assert summary["completed_todos"] == 2
        assert summary["pending_todos"] == 1

    @pytest.mark.asyncio
    async def test_backward_compat_old_format(self, tmp_project):
        """向后兼容：老格式文件（无 [#tN] 标记）仍可用文本匹配"""
        _todo_managers.clear()
        manager = TodoManager(tmp_project)

        # 手动创建老格式的 todo 文件
        todo_dir = tmp_project / ".aacode" / "todos"
        todo_dir.mkdir(parents=True, exist_ok=True)
        old_file = todo_dir / "old_todo.md"
        old_file.write_text(
            "# old - 待办清单\n\n## 待办\n- [ ] 🔴 **任务**: 修复登录bug\n\n## 已完成\n",
            encoding="utf-8",
        )
        manager.current_todo_file = old_file

        # 用文本匹配标记完成
        result = await manager.mark_todo_completed(item_pattern="登录bug")
        assert result is True

        content = old_file.read_text(encoding="utf-8")
        assert "- [x]" in content

    @pytest.mark.asyncio
    async def test_counter_recovery_across_sessions(self, tmp_project):
        """模拟跨会话的 ID 恢复"""
        _todo_managers.clear()

        # 第一个会话
        manager1 = TodoManager(tmp_project)
        await manager1.create_todo_list("任务")
        await manager1.add_todo_item("事项1")
        await manager1.add_todo_item("事项2")

        # 第二个会话（新的 manager 实例）
        _todo_managers.clear()
        manager2 = TodoManager(tmp_project)
        manager2.current_todo_file = manager1.current_todo_file

        # 添加新事项，ID 应该从 t3 开始
        id3 = await manager2.add_todo_item("事项3")
        assert id3 == "t3"
