"""QuizTrainer 核心功能测试。"""

from __future__ import annotations

import shutil
import sys
import tempfile
from pathlib import Path
from typing import Generator
from unittest.mock import patch

import pytest

# 添加项目路径
sys.path.insert(0, str(Path(__file__).parent.parent.parent.parent / "pytola-office"))

from pytola_office.quiztrainer.core import DataManager, QuizManager, QuizSession
from pytola_office.quiztrainer.models import (
    AnswerRecord,
    ChoiceQuestion,
    Difficulty,
    QuestionType,
)


class TestDataManager:
    """数据管理器测试."""

    @pytest.fixture
    def temp_dir(self) -> Generator[Path, None, None]:
        """创建临时目录用于测试。"""
        temp_path = tempfile.mkdtemp()
        yield temp_path
        shutil.rmtree(temp_path, ignore_errors=True)

    @pytest.fixture
    def data_manager(self, temp_dir: Path) -> DataManager:
        """创建测试用的数据管理器。"""
        with patch.object(DataManager, "DATA_DIR", Path(temp_dir)):
            dm = DataManager()
            dm.DATA_DIR = Path(temp_dir)
            dm.QUESTIONS_DIR = dm.DATA_DIR / "questions"
            dm.USER_DATA_DIR = dm.DATA_DIR / "user_data"
            dm._ensure_directories()
            return dm

    def test_ensure_directories(self, data_manager: DataManager) -> None:
        """测试目录创建."""
        assert data_manager.DATA_DIR.exists()
        assert data_manager.QUESTIONS_DIR.exists()
        assert data_manager.USER_DATA_DIR.exists()

    def test_save_and_load_question_bank(self, data_manager: DataManager) -> None:
        """测试保存和加载题库."""
        questions = [
            ChoiceQuestion(
                id="q001",
                type=QuestionType.CHOICE,
                category="测试",
                difficulty=Difficulty.EASY,
                question="测试题目",
                answer="A",
                options=["A", "B", "C", "D"],
            ),
        ]

        # 保存题库
        data_manager.save_question_bank("test_bank", questions)

        # 验证文件存在
        bank_file = data_manager.QUESTIONS_DIR / "test_bank.json"
        assert bank_file.exists()

        # 加载题库
        loaded = data_manager.load_question_bank("test_bank")
        assert len(loaded) == 1
        assert loaded[0].id == "q001"
        assert loaded[0].question == "测试题目"

    def test_load_nonexistent_bank(self, data_manager: DataManager) -> None:
        """测试加载不存在的题库."""
        questions = data_manager.load_question_bank("nonexistent")
        assert questions == []

    def test_get_question_banks(self, data_manager: DataManager) -> None:
        """测试获取题库列表."""
        # 创建一些题库，使用真实题目对象
        questions = [
            ChoiceQuestion(
                id="q1",
                type=QuestionType.CHOICE,
                category="测试",
                difficulty=Difficulty.EASY,
                question="测试",
                answer="A",
            ),
        ]
        data_manager.save_question_bank("bank1", questions)
        data_manager.save_question_bank("bank2", questions)

        banks = data_manager.get_question_banks()
        assert "bank1" in banks
        assert "bank2" in banks

    def test_save_and_load_user_data(self, data_manager: DataManager) -> None:
        """测试保存和加载用户数据."""
        from pytola_office.quiztrainer.models import UserData

        # 创建用户数据
        user_data = UserData(user_id="test_user")
        user_data.history = [
            AnswerRecord(question_id="q1", user_answer="A", is_correct=True),
        ]

        # 保存用户数据
        data_manager.save_user_data(user_data)

        # 加载用户数据
        loaded = data_manager.load_user_data("test_user")
        assert loaded.user_id == "test_user"
        assert loaded.total_questions == 1

    def test_add_question(self, data_manager: DataManager) -> None:
        """测试添加题目."""
        question = ChoiceQuestion(
            id="new_q",
            type=QuestionType.CHOICE,
            category="测试",
            difficulty=Difficulty.MEDIUM,
            question="新题目",
            answer="B",
            options=["A", "B", "C", "D"],
        )

        data_manager.add_question(question, "new_bank")

        # 验证题目已添加
        questions = data_manager.load_question_bank("new_bank")
        assert len(questions) == 1
        assert questions[0].id == "new_q"

    def test_delete_question(self, data_manager: DataManager) -> None:
        """测试删除题目."""
        question = ChoiceQuestion(
            id="to_delete",
            type=QuestionType.CHOICE,
            category="测试",
            difficulty=Difficulty.EASY,
            question="要删除的题目",
            answer="A",
            options=["A", "B", "C", "D"],
        )

        data_manager.add_question(question, "delete_test")

        # 删除题目
        result = data_manager.delete_question("to_delete", "delete_test")
        assert result is True

        # 验证题目已删除
        questions = data_manager.load_question_bank("delete_test")
        assert len(questions) == 0

    def test_delete_nonexistent_question(self, data_manager: DataManager) -> None:
        """测试删除不存在的题目."""
        result = data_manager.delete_question("nonexistent", "any_bank")
        assert result is False


class TestQuizManager:
    """题库管理器测试."""

    @pytest.fixture
    def temp_dir(self) -> Generator[Path, None, None]:
        """创建临时目录。"""
        temp_path = tempfile.mkdtemp()
        yield temp_path
        shutil.rmtree(temp_path, ignore_errors=True)

    @pytest.fixture
    def quiz_manager(self, temp_dir: Path) -> QuizManager:
        """创建测试用的题库管理器。"""
        dm = DataManager()
        dm.DATA_DIR = Path(temp_dir)
        dm.QUESTIONS_DIR = dm.DATA_DIR / "questions"
        dm.USER_DATA_DIR = dm.DATA_DIR / "user_data"
        dm._ensure_directories()
        return QuizManager(dm)

    def test_create_question_choice(self, quiz_manager: QuizManager) -> None:
        """测试创建选择题."""
        q = quiz_manager.create_question(
            q_type="choice",
            question="测试题目",
            answer="A",
            options=["A", "B", "C", "D"],
        )
        assert q.type.value == "choice"
        assert q.answer == "A"

    def test_create_question_truefalse(self, quiz_manager: QuizManager) -> None:
        """测试创建判断题."""
        q = quiz_manager.create_question(
            q_type="truefalse",
            question="判断题",
            answer=True,
        )
        assert q.type.value == "truefalse"
        assert q.answer is True

    def test_create_question_fill(self, quiz_manager: QuizManager) -> None:
        """测试创建填空题."""
        q = quiz_manager.create_question(
            q_type="fill",
            question="填空题___",
            answer="答案",
        )
        assert q.type.value == "fill"
        assert q.answer == "答案"

    def test_filter_questions_by_type(self, quiz_manager: QuizManager) -> None:
        """测试按类型筛选题目."""
        # 添加不同类型的题目
        quiz_manager.data_manager.add_question(
            quiz_manager.create_question(
                q_type="choice",
                question="选择题",
                answer="A",
                options=["A", "B", "C", "D"],
            ),
            "filter_test",
        )
        quiz_manager.data_manager.add_question(
            quiz_manager.create_question(
                q_type="truefalse",
                question="判断题",
                answer=True,
            ),
            "filter_test",
        )

        # 筛选选择题
        choice_questions = quiz_manager.filter_questions(
            bank_name="filter_test",
            q_type="choice",
        )
        assert len(choice_questions) == 1
        assert choice_questions[0].type.value == "choice"

    def test_filter_questions_by_difficulty(self, quiz_manager: QuizManager) -> None:
        """测试按难度筛选题目."""
        quiz_manager.data_manager.add_question(
            quiz_manager.create_question(
                q_type="choice",
                question="简单题",
                answer="A",
                difficulty="easy",
            ),
            "difficulty_test",
        )
        quiz_manager.data_manager.add_question(
            quiz_manager.create_question(
                q_type="choice",
                question="困难题",
                answer="A",
                difficulty="hard",
            ),
            "difficulty_test",
        )

        # 筛选简单题
        easy_questions = quiz_manager.filter_questions(
            bank_name="difficulty_test",
            difficulty="easy",
        )
        assert len(easy_questions) == 1
        assert easy_questions[0].difficulty.value == "easy"

    def test_get_statistics_empty(self, quiz_manager: QuizManager) -> None:
        """测试空数据的统计."""
        stats = quiz_manager.get_statistics()
        assert stats["total_questions"] == 0
        assert stats["correct_count"] == 0
        assert stats["accuracy"] == pytest.approx(0.0, rel=0.1)


class TestQuizSession:
    """答题会话测试."""

    @pytest.fixture
    def session_questions(self) -> list[ChoiceQuestion]:
        """创建测试用题目列表。"""
        return [
            ChoiceQuestion(
                id="q1",
                type=QuestionType.CHOICE,
                category="测试",
                difficulty=Difficulty.EASY,
                question="第一题",
                answer="A",
                options=["A", "B", "C", "D"],
            ),
            ChoiceQuestion(
                id="q2",
                type=QuestionType.CHOICE,
                category="测试",
                difficulty=Difficulty.MEDIUM,
                question="第二题",
                answer="B",
                options=["A", "B", "C", "D"],
            ),
            ChoiceQuestion(
                id="q3",
                type=QuestionType.CHOICE,
                category="测试",
                difficulty=Difficulty.HARD,
                question="第三题",
                answer="C",
                options=["A", "B", "C", "D"],
            ),
        ]

    def test_session_initialization(self, session_questions: list) -> None:
        """测试会话初始化."""
        session = QuizSession(
            questions=session_questions,
            mode="practice",
        )
        assert len(session.questions) == 3
        assert session.current_index == 0
        assert session.current_question.id == "q1"

    def test_submit_answer_correct(self, session_questions: list) -> None:
        """测试提交正确答案."""
        session = QuizSession(questions=session_questions)
        record = session.submit_answer("A")
        assert record.is_correct is True
        assert len(session.answers) == 1

    def test_submit_answer_wrong(self, session_questions: list) -> None:
        """测试提交错误答案."""
        session = QuizSession(questions=session_questions)
        record = session.submit_answer("B")
        assert record.is_correct is False

    def test_next_question(self, session_questions: list) -> None:
        """测试下一题."""
        session = QuizSession(questions=session_questions)
        next_q = session.next_question()
        assert session.current_index == 1
        assert next_q.id == "q2"

    def test_prev_question(self, session_questions: list) -> None:
        """测试上一题."""
        session = QuizSession(questions=session_questions)
        session.next_question()  # 移动到第二题
        prev_q = session.prev_question()
        assert session.current_index == 0
        assert prev_q.id == "q1"

    def test_jump_to_question(self, session_questions: list) -> None:
        """测试跳转到指定题目."""
        session = QuizSession(questions=session_questions)
        session.jump_to(2)
        assert session.current_index == 2
        assert session.current_question.id == "q3"

    def test_progress(self, session_questions: list) -> None:
        """测试进度属性."""
        session = QuizSession(questions=session_questions)
        progress = session.progress
        assert progress["current"] == 1
        assert progress["total"] == 3
        assert progress["answered"] == 0

        session.submit_answer("A")
        progress = session.progress
        assert progress["answered"] == 1

    def test_is_complete(self, session_questions: list) -> None:
        """测试是否完成."""
        # 测试：1个题目时，初始为未完成
        session = QuizSession(questions=session_questions[:1])
        assert session.is_complete is False

        # 提交答案后仍未完成（答题后不会自动标记完成）
        session.submit_answer("A")
        assert session.is_complete is False

        # 直接设置索引模拟完成所有题目
        session.current_index = 1  # 1 >= 1
        assert session.is_complete is True

    def test_get_results(self, session_questions: list) -> None:
        """测试获取结果."""
        session = QuizSession(questions=session_questions)
        session.submit_answer("A")  # 正确
        session.next_question()
        session.submit_answer("A")  # 错误
        session.next_question()
        session.submit_answer("C")  # 正确

        results = session.get_results()
        assert results["total"] == 3
        assert results["correct"] == 2
        assert results["accuracy"] == pytest.approx(66.7, rel=0.1)

    def test_shuffle(self, session_questions: list) -> None:
        """测试打乱题目顺序."""
        session = QuizSession(questions=session_questions.copy())
        original_ids = [q.id for q in session.questions]
        session.shuffle()
        shuffled_ids = [q.id for q in session.questions]
        # 注意：随机打乱后有可能顺序相同（小概率），但测试主要验证shuffle方法可用
        assert len(shuffled_ids) == 3
        assert set(shuffled_ids) == set(original_ids)


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