"""QuizTrainer 边界条件和异常场景测试."""

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 (
    BaseQuestion,
    Difficulty,
    EssayQuestion,
    FillQuestion,
    QuestionType,
    TrueFalseQuestion,
)


class TestBaseQuestionFromDict:
    """测试 BaseQuestion.from_dict 的各种情况."""

    def test_from_dict_with_fill_type(self) -> None:
        """测试从字典创建填空题."""
        data = {
            "id": "fill1",
            "type": "fill",
            "category": "填空题",
            "difficulty": "medium",
            "question": "Python 的输出函数是___",
            "answer": "print",
            "explanation": "print 是输出函数",
        }

        q = FillQuestion.from_dict(data)
        assert q.id == "fill1"
        assert q.type == QuestionType.FILL
        assert q.answer == "print"

    def test_from_dict_with_truefalse_type(self) -> None:
        """测试从字典创建判断题."""
        data = {
            "id": "tf1",
            "type": "truefalse",
            "category": "判断题",
            "difficulty": "easy",
            "question": "Python 是编译型语言",
            "answer": False,
            "explanation": "Python 是解释型语言",
        }

        q = TrueFalseQuestion.from_dict(data)
        assert q.id == "tf1"
        assert q.type == QuestionType.TRUEFALSE
        assert q.answer is False

    def test_from_dict_with_unknown_type(self) -> None:
        """测试从字典创建未知类型的题目抛出异常."""
        # 使用无效的 type 值会抛出 ValueError
        data = {
            "id": "unknown1",
            "type": "unknown",  # 未知类型
            "category": "测试",
            "difficulty": "medium",
            "question": "未知类型题目",
            "answer": "答案",
            "explanation": "解析",
        }

        # 应该抛出 ValueError
        with pytest.raises(ValueError, match="is not a valid QuestionType"):
            BaseQuestion.from_dict(data)


class TestQuizSessionEdgeCases:
    """测试 QuizSession 的边界条件."""

    def test_session_with_zero_questions(self) -> None:
        """测试空题目列表的会话."""
        session = QuizSession(questions=[], mode="practice")

        assert session.current_question is None
        assert session.is_complete is True  # 0 >= 0
        assert session.progress["total"] == 0

        # 获取结果
        results = session.get_results()
        assert results["total"] == 0
        assert results["correct"] == 0
        assert results["accuracy"] == 0

    def test_session_progress_with_all_answered(self) -> None:
        """测试全部答完后的进度."""
        from pytola_office.quiztrainer.models import ChoiceQuestion

        questions = [
            ChoiceQuestion(
                id="q1",
                type=QuestionType.CHOICE,
                category="测试",
                difficulty=Difficulty.EASY,
                question="测试 1",
                answer="A",
                options=["A", "B"],
            ),
            ChoiceQuestion(
                id="q2",
                type=QuestionType.CHOICE,
                category="测试",
                difficulty=Difficulty.EASY,
                question="测试 2",
                answer="A",
                options=["A", "B"],
            ),
        ]

        session = QuizSession(questions=questions, mode="practice")

        # 回答所有题目
        session.submit_answer("A")
        session.next_question()
        session.submit_answer("A")

        progress = session.progress
        assert progress["current"] == 2
        assert progress["total"] == 2
        assert progress["answered"] == 2

    def test_get_results_with_wrong_questions(self) -> None:
        """测试结果中包含错题."""
        from pytola_office.quiztrainer.models import ChoiceQuestion

        questions = [
            ChoiceQuestion(
                id="q1",
                type=QuestionType.CHOICE,
                category="测试",
                difficulty=Difficulty.EASY,
                question="测试 1",
                answer="A",
                options=["A", "B"],
            ),
            ChoiceQuestion(
                id="q2",
                type=QuestionType.CHOICE,
                category="测试",
                difficulty=Difficulty.EASY,
                question="测试 2",
                answer="B",
                options=["A", "B"],
            ),
        ]

        session = QuizSession(questions=questions, mode="practice")

        # 第一题答对，第二题答错
        session.submit_answer("A")  # 正确
        session.next_question()
        session.submit_answer("A")  # 错误

        results = session.get_results()

        assert len(results["wrong_questions"]) == 1
        wrong = results["wrong_questions"][0]
        assert wrong["question"].id == "q2"
        assert wrong["user_answer"] == "A"

    def test_category_stats_in_results(self) -> None:
        """测试结果中的分类统计."""
        from pytola_office.quiztrainer.models import ChoiceQuestion

        questions = [
            ChoiceQuestion(
                id="q1",
                type=QuestionType.CHOICE,
                category="Python",
                difficulty=Difficulty.EASY,
                question="Python 题",
                answer="A",
                options=["A", "B"],
            ),
            ChoiceQuestion(
                id="q2",
                type=QuestionType.CHOICE,
                category="Math",
                difficulty=Difficulty.EASY,
                question="数学题",
                answer="A",
                options=["A", "B"],
            ),
            ChoiceQuestion(
                id="q3",
                type=QuestionType.CHOICE,
                category="Python",
                difficulty=Difficulty.EASY,
                question="Python 题 2",
                answer="A",
                options=["A", "B"],
            ),
        ]

        session = QuizSession(questions=questions, mode="practice")

        # 回答所有题目
        session.submit_answer("A")
        session.next_question()
        session.submit_answer("A")
        session.next_question()
        session.submit_answer("A")

        results = session.get_results()

        assert "Python" in results["category_stats"]
        assert "Math" in results["category_stats"]
        assert results["category_stats"]["Python"]["total"] == 2
        assert results["category_stats"]["Math"]["total"] == 1


class TestDataManagerSpecialCases:
    """测试 DataManager 的特殊情况."""

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

    @pytest.fixture
    def data_manager(self, temp_dir: str) -> 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_load_question_bank_with_none_name(self, data_manager: DataManager) -> None:
        """测试加载所有题库（bank_name 为 None)."""
        # 先添加一些题库
        from pytola_office.quiztrainer.models import ChoiceQuestion

        q1 = ChoiceQuestion(
            id="q1",
            type=QuestionType.CHOICE,
            category="测试",
            difficulty=Difficulty.EASY,
            question="测试 1",
            answer="A",
            options=["A", "B"],
        )

        q2 = ChoiceQuestion(
            id="q2",
            type=QuestionType.CHOICE,
            category="测试",
            difficulty=Difficulty.EASY,
            question="测试 2",
            answer="A",
            options=["A", "B"],
        )

        data_manager.add_question(q1, "bank1")
        data_manager.add_question(q2, "bank2")

        # 加载所有题库
        all_questions = data_manager.load_question_bank(None)
        assert len(all_questions) == 2

    def test_get_categories_empty(self, data_manager: DataManager) -> None:
        """测试空数据时的分类列表."""
        categories = data_manager.get_categories()
        assert categories == []

    def test_delete_question_from_empty_bank(self, data_manager: DataManager) -> None:
        """测试从空题库删除题目."""
        result = data_manager.delete_question("nonexistent", "empty_bank")
        assert result is False

    def test_save_question_bank_overwrites(self, data_manager: DataManager) -> None:
        """测试保存题库会覆盖原有内容."""
        from pytola_office.quiztrainer.models import ChoiceQuestion

        # 第一次保存
        q1 = ChoiceQuestion(
            id="q1",
            type=QuestionType.CHOICE,
            category="测试",
            difficulty=Difficulty.EASY,
            question="测试 1",
            answer="A",
            options=["A", "B"],
        )
        data_manager.save_question_bank("overwrite_test", [q1])

        # 第二次保存（覆盖）
        q2 = ChoiceQuestion(
            id="q2",
            type=QuestionType.CHOICE,
            category="测试",
            difficulty=Difficulty.EASY,
            question="测试 2",
            answer="A",
            options=["A", "B"],
        )
        data_manager.save_question_bank("overwrite_test", [q2])

        # 验证只有第二个题目
        loaded = data_manager.load_question_bank("overwrite_test")
        assert len(loaded) == 1
        assert loaded[0].id == "q2"


class TestQuizManagerEdgeCases:
    """测试 QuizManager 的边界情况."""

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

    @pytest.fixture
    def quiz_manager(self, temp_dir: str) -> 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_filter_questions_no_matches(self, quiz_manager: QuizManager) -> None:
        """测试筛选没有匹配的题目."""
        # 添加一个题目
        quiz_manager.data_manager.add_question(
            quiz_manager.create_question(
                q_type="choice",
                question="测试题",
                answer="A",
                category="Python",
                difficulty="easy",
                options=["A", "B"],
            ),
            "filter_test",
        )

        # 筛选不存在的分类
        result = quiz_manager.filter_questions(
            bank_name="filter_test",
            category="NonExistent",
        )
        assert len(result) == 0

    def test_start_quiz_with_no_matching_questions(self, quiz_manager: QuizManager) -> None:
        """测试开始答题但没有符合条件的题目."""
        # 添加一个简单题
        quiz_manager.data_manager.add_question(
            quiz_manager.create_question(
                q_type="choice",
                question="简单题",
                answer="A",
                difficulty="easy",
                options=["A", "B"],
            ),
            "quiz_test",
        )

        # 尝试选择困难题
        session = quiz_manager.start_quiz(
            bank_name="quiz_test",
            difficulty="hard",
            count=5,
        )

        assert len(session.questions) == 0

    def test_create_question_with_invalid_type(self, quiz_manager: QuizManager) -> None:
        """测试创建无效类型的题目."""
        # 应该抛出异常
        with pytest.raises(ValueError):
            quiz_manager.create_question(
                q_type="invalid_type",
                question="测试",
                answer="A",
            )

    def test_create_question_with_invalid_difficulty(self, quiz_manager: QuizManager) -> None:
        """测试创建无效难度的题目."""
        with pytest.raises(ValueError):
            quiz_manager.create_question(
                q_type="choice",
                question="测试",
                answer="A",
                difficulty="invalid",
                options=["A", "B"],
            )


class TestAnswerRecordEdgeCases:
    """测试 AnswerRecord 的边界情况."""

    def test_answer_record_with_boolean_answer(self) -> None:
        """测试判断题的答案记录."""
        from pytola_office.quiztrainer.models import AnswerRecord

        record = AnswerRecord(
            question_id="tf1",
            user_answer=True,
            is_correct=True,
        )

        data = record.to_dict()
        assert data["user_answer"] is True
        assert data["is_correct"] is True

    def test_answer_record_default_timestamp(self) -> None:
        """测试答题记录默认时间戳."""
        from pytola_office.quiztrainer.models import AnswerRecord

        record = AnswerRecord(
            question_id="q1",
            user_answer="A",
            is_correct=True,
        )

        # 应该有默认的时间戳
        assert record.timestamp is not None
        assert len(record.timestamp) > 0


class TestQuestionTypeDisplay:
    """测试题目类型显示."""

    def test_essay_type_display(self) -> None:
        """测试问答题类型显示."""
        q = EssayQuestion(
            id="essay1",
            type=QuestionType.ESSAY,
            category="论述题",
            difficulty=Difficulty.HARD,
            question="请论述...",
            answer="答案",
        )
        assert q.type_display == "问答题"

    def test_fill_type_display(self) -> None:
        """测试填空题类型显示."""
        q = FillQuestion(
            id="fill1",
            type=QuestionType.FILL,
            category="填空题",
            difficulty=Difficulty.MEDIUM,
            question="填空___",
            answer="答案",
        )
        assert q.type_display == "填空题"


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