"""QuizTrainer Flask Web 应用."""

from __future__ import annotations

import contextlib
import json
import pathlib
import uuid
from datetime import datetime, timezone

from flask import Flask, Response, jsonify, redirect, render_template, request, session, url_for

from .core import DataManager, QuizManager, QuizSession
from .models import BaseQuestion, QuestionType

# 创建 Flask 应用
app = Flask(__name__)
app.secret_key = str(uuid.uuid4())


# 注册全局模板函数
@app.template_global()
def now() -> str:
    """获取当前时间字符串."""
    return datetime.now(tz=timezone.utc).isoformat()


# 初始化管理器
data_manager = DataManager()
quiz_manager = QuizManager(data_manager)

# 服务器端会话存储
_quiz_sessions: dict[str, QuizSession] = {}


def _get_current_quiz_session() -> QuizSession | None:
    """获取当前答题会话."""
    session_id = session.get("quiz_session_id")
    if not session_id:
        return None
    return _quiz_sessions.get(session_id)


def _create_question(data: dict) -> BaseQuestion:
    """创建题目对象."""
    return BaseQuestion.from_dict(data)


def _create_sample_bank() -> None:
    """创建示例题库."""
    questions = [
        {
            "id": "q001",
            "type": "choice",
            "category": "Python基础",
            "difficulty": "easy",
            "question": "以下哪个是 Python 的列表数据类型?",
            "options": ["{1, 2, 3}", "[1, 2, 3]", "(1, 2, 3)", "<1, 2, 3>"],
            "answer": "B",
            "explanation": "列表使用方括号 [] 表示,集合使用大括号 {},元组使用圆括号 ().",
        },
        {
            "id": "q002",
            "type": "choice",
            "category": "Python基础",
            "difficulty": "easy",
            "question": "Python 中用于输出内容的函数是?",
            "options": ["echo()", "print()", "console.log()", "printf()"],
            "answer": "B",
            "explanation": "print() 是 Python 的标准输出函数,用于打印内容到控制台.",
        },
        {
            "id": "q003",
            "type": "truefalse",
            "category": "Python基础",
            "difficulty": "easy",
            "question": "Python 是一种编译型语言. ",
            "answer": False,
            "explanation": "Python 是一种解释型语言,代码在运行时逐行解释执行.",
        },
        {
            "id": "q004",
            "type": "truefalse",
            "category": "Python基础",
            "difficulty": "medium",
            "question": "Python 变量使用前必须先声明. ",
            "answer": False,
            "explanation": "Python 是动态类型语言,变量不需要预先声明,首次赋值即创建变量.",
        },
        {
            "id": "q005",
            "type": "fill",
            "category": "Python基础",
            "difficulty": "medium",
            "question": "Python 中用于获取用户输入的函数是 ___()",
            "answer": "input",
            "explanation": "input() 函数用于从用户获取输入,返回字符串类型.",
        },
        {
            "id": "q006",
            "type": "fill",
            "category": "Python基础",
            "difficulty": "medium",
            "question": "Python 字典使用 ___ 符号来创建",
            "answer": "{}",
            "explanation": "字典使用大括号 {},例如 {'name': 'Tom', 'age': 20}.",
        },
        {
            "id": "q007",
            "type": "essay",
            "category": "Python基础",
            "difficulty": "hard",
            "question": "请简述 Python 列表和元组的主要区别以及各自的使用场景. ",
            "answer": "列表是可变的数据类型,可以添加、删除、修改元素;元组是不可变的,创建后不能修改. 列表适合需要频繁修改的数据集合,元组适合存储不变的数据,如配置信息、函数返回值等. ",
            "explanation": "列表和元组的主要区别在于:1. 可变性 2. 性能(元组更快) 3. 用途(元组可用于字典键) 4. 语法(列表用[],元组用()).",
        },
        {
            "id": "q008",
            "type": "choice",
            "category": "数据结构",
            "difficulty": "easy",
            "question": "以下哪种数据结构适合存储键值对?",
            "options": ["列表", "元组", "字典", "集合"],
            "answer": "C",
            "explanation": "字典(dict)是 Python 中用于存储键值对的数据结构.",
        },
        {
            "id": "q009",
            "type": "choice",
            "category": "数据结构",
            "difficulty": "medium",
            "question": "如何访问列表 lst 的第一个元素?",
            "options": ["lst[0]", "lst[1]", "lst.first()", "lst.get(0)"],
            "answer": "A",
            "explanation": "Python 列表索引从 0 开始,第一个元素是 lst[0].",
        },
        {
            "id": "q010",
            "type": "truefalse",
            "category": "数据结构",
            "difficulty": "easy",
            "question": "集合中的元素不允许重复. ",
            "answer": True,
            "explanation": "集合(set)是一个无序的不重复元素序列,会自动去除重复项.",
        },
        {
            "id": "q011",
            "type": "fill",
            "category": "数据结构",
            "difficulty": "medium",
            "question": "向列表末尾添加元素使用 ___ 方法",
            "answer": "append",
            "explanation": "list.append(x) 用于在列表末尾添加元素 x.",
        },
        {
            "id": "q012",
            "type": "choice",
            "category": "函数",
            "difficulty": "easy",
            "question": "定义函数使用的关键字是?",
            "options": ["function", "func", "def", "define"],
            "answer": "C",
            "explanation": "Python 使用 def 关键字来定义函数.",
        },
        {
            "id": "q013",
            "type": "truefalse",
            "category": "函数",
            "difficulty": "medium",
            "question": "Python 函数可以返回多个值. ",
            "answer": True,
            "explanation": "Python 函数可以返回多个值,实际返回的是一个元组.",
        },
        {
            "id": "q014",
            "type": "fill",
            "category": "函数",
            "difficulty": "medium",
            "question": "函数参数默认值使用 ___ 符号指定",
            "answer": "=",
            "explanation": "例如 def func(x, y=10): 表示 y 的默认值是 10.",
        },
        {
            "id": "q015",
            "type": "essay",
            "category": "函数",
            "difficulty": "hard",
            "question": "解释 Python 中 *args 和 **kwargs 的作用和使用场景. ",
            "answer": "*args 用于传递不定数量的位置参数,**kwargs 用于传递不定数量的关键字参数. 它们允许函数接受任意数量的参数. ",
            "explanation": "*args 将参数打包为元组,**kwargs 将参数打包为字典. 常用于装饰器、偏函数等高级用法.",
        },
    ]
    data_manager.save_question_bank("python_basics", [_create_question(q) for q in questions])


def _ensure_sample_data() -> None:
    """确保示例题库存在."""
    banks = data_manager.get_question_banks()
    if not banks:
        _create_sample_bank()


# 创建示例题库
_ensure_sample_data()


# 页面路由
@app.route("/")
def index() -> str:
    """首页."""
    categories = data_manager.get_categories()
    banks = data_manager.get_question_banks()
    stats = quiz_manager.get_statistics()

    # 统计各类型题目数量
    type_counts = {t.value: 0 for t in QuestionType}
    for _cat in categories:
        questions = data_manager.load_question_bank()
        for q in questions:
            type_counts[q.type.value] = type_counts.get(q.type.value, 0) + 1

    return render_template("index.html", categories=categories, banks=banks, stats=stats, type_counts=type_counts)


@app.route("/quiz")
def quiz() -> str:
    """答题页面."""
    bank_name = request.args.get("bank")
    q_type = request.args.get("type")
    category = request.args.get("category")
    difficulty = request.args.get("difficulty")
    count = request.args.get("count", 10, type=int)
    mode = request.args.get("mode", "practice")
    shuffle = request.args.get("shuffle", "true").lower() == "true"

    # 创建答题会话
    quiz_session = quiz_manager.start_quiz(
        bank_name=bank_name,
        q_type=q_type,
        category=category,
        difficulty=difficulty,
        count=count,
        mode=mode,
        shuffle=shuffle,
    )

    # 存储会话到服务器端字典
    session_id = str(uuid.uuid4())
    _quiz_sessions[session_id] = quiz_session
    session["quiz_session_id"] = session_id

    if not quiz_session.questions:
        return redirect(url_for("index"))

    question = quiz_session.current_question
    return render_template("quiz.html", question=question, session=quiz_session, mode=mode)


@app.route("/quiz/submit", methods=["POST"])
def submit_answer() -> str:
    """提交答案."""
    quiz_session = _get_current_quiz_session()
    if not quiz_session:
        return redirect(url_for("index"))

    user_answer = request.form.get("answer")

    # 处理判断题
    question = quiz_session.current_question
    if question.type == QuestionType.TRUEFALSE:
        user_answer = request.form.get("answer") == "true"

    # 提交答案
    record = quiz_session.submit_answer(user_answer=user_answer)

    return render_template("result.html", question=question, record=record, session=quiz_session)


@app.route("/quiz/next", methods=["POST"])
def next_question() -> str:
    """下一题."""
    quiz_session = _get_current_quiz_session()
    if not quiz_session:
        return redirect(url_for("index"))

    next_q = quiz_session.next_question()
    if not next_q:
        return redirect(url_for("quiz_complete"))

    return render_template("quiz.html", question=next_q, session=quiz_session, mode=quiz_session.mode)


@app.route("/quiz/prev", methods=["POST"])
def prev_question() -> str:
    """上一题."""
    quiz_session = _get_current_quiz_session()
    if not quiz_session:
        return redirect(url_for("index"))

    prev_q = quiz_session.prev_question()
    return render_template("quiz.html", question=prev_q, session=quiz_session, mode=quiz_session.mode)


@app.route("/quiz/complete")
def quiz_complete() -> str:
    """答题完成."""
    quiz_session = _get_current_quiz_session()
    if not quiz_session:
        return redirect(url_for("index"))

    results = quiz_session.get_results()
    return render_template("complete.html", results=results, session=quiz_session)


@app.route("/history")
def history() -> str:
    """答题历史."""
    user_data = data_manager.load_user_data()
    stats = quiz_manager.get_statistics()

    # 获取历史记录详情
    history_with_questions = []
    all_questions = data_manager.load_question_bank()
    question_map = {q.id: q for q in all_questions}

    for record in reversed(user_data.history[-50:]):
        question = question_map.get(record.question_id)
        if question:
            history_with_questions.append({"record": record, "question": question})

    return render_template("history.html", history=history_with_questions, stats=stats, user_data=user_data)


@app.route("/stats")
def stats() -> str:
    """统计页面."""
    stats_data = quiz_manager.get_statistics()
    categories = data_manager.get_categories()

    return render_template("stats.html", stats=stats_data, categories=categories)


@app.route("/admin")
def admin() -> str:
    """管理页面."""
    banks = data_manager.get_question_banks()
    categories = data_manager.get_categories()

    all_questions = data_manager.load_question_bank()

    return render_template("admin.html", banks=banks, categories=categories, questions=all_questions[:20])


@app.route("/admin/add_question", methods=["POST"])
def add_question() -> str:
    """添加题目."""
    q_type = request.form.get("type", "choice")
    question_text = request.form.get("question", "")
    answer = request.form.get("answer", "")
    category = request.form.get("category", "默认分类")
    difficulty = request.form.get("difficulty", "medium")
    explanation = request.form.get("explanation", "")
    options_str = request.form.get("options", "")

    # 处理答案类型
    if q_type == "truefalse":
        answer = answer.lower() in {"true", "1", "yes"}
    elif q_type == "choice":
        answer = answer.upper()
    elif q_type == "fill":
        pass

    # 处理选项
    options = None
    if options_str:
        options = [opt.strip() for opt in options_str.split("\n") if opt.strip()]

    # 创建题目
    question = quiz_manager.create_question(
        q_type=q_type,
        question=question_text,
        answer=answer,
        category=category,
        difficulty=difficulty,
        explanation=explanation,
        options=options,
    )

    # 保存到题库
    data_manager.add_question(question, "python_basics")

    return redirect(url_for("admin"))


@app.route("/api/questions")
def api_questions() -> Response:
    """获取题目列表."""
    bank_name = request.args.get("bank")
    q_type = request.args.get("type")
    category = request.args.get("category")

    questions = quiz_manager.filter_questions(bank_name=bank_name, q_type=q_type, category=category)

    return jsonify([q.to_dict() for q in questions])


@app.route("/api/stats")
def api_stats() -> Response:
    """获取统计数据."""
    return jsonify(quiz_manager.get_statistics())


@app.template_filter("question_type_display")
def question_type_display_filter(q_type: str) -> str:
    """题目类型显示过滤器."""
    type_map = {"choice": "选择题", "truefalse": "判断题", "fill": "填空题", "essay": "问答题"}
    return type_map.get(q_type, q_type)


@app.template_filter("difficulty_display")
def difficulty_display_filter(difficulty: str) -> str:
    """难度显示过滤器."""
    difficulty_map = {"easy": "简单", "medium": "中等", "hard": "困难"}
    return difficulty_map.get(difficulty, difficulty)


@app.template_filter("todatetime")
def todatetime_filter(value: str) -> datetime:
    """将 ISO 格式时间字符串转换为 datetime 对象."""
    if isinstance(value, datetime):
        return value
    try:
        # 尝试解析 ISO 格式
        return datetime.fromisoformat(value.replace("Z", "+00:00"))
    except (ValueError, AttributeError):
        # 如果解析失败，返回当前时间
        return datetime.now(tz=timezone.utc)


@app.route("/import")
def import_page() -> str:
    """导入题库页面."""
    return render_template("import.html")


@app.route("/api/import", methods=["POST"])
def api_import() -> Response:
    """导入题库."""
    import os
    import tempfile

    from pytola_office.quiztrainer.tools.word_importer import WordImporter

    # 检查是否有文件上传
    if "file" not in request.files:
        return jsonify({"success": False, "error": "没有上传文件"})

    file = request.files["file"]
    if not file.filename:
        return jsonify({"success": False, "error": "请选择文件"})

    import_type = request.form.get("import_type", "word")
    category = request.form.get("category", "默认分类")
    difficulty = request.form.get("difficulty", "medium")
    preview_only = request.form.get("preview_only", "false") == "true"
    max_preview = int(request.form.get("max_preview", "10"))
    confirm = request.form.get("confirm", "false") == "true"

    # 保存上传的文件到临时目录
    temp_dir = tempfile.gettempdir()
    file_path = os.path.join(temp_dir, file.filename)

    try:
        file.save(file_path)

        if import_type == "json":
            # 处理 JSON 文件
            from pytola_office.quiztrainer.models import BaseQuestion

            try:
                with pathlib.Path(file_path).open("r", encoding="utf-8") as f:
                    data = json.load(f)

                if confirm:
                    bank_name = os.path.splitext(file.filename)[0]
                    questions = [BaseQuestion.from_dict(q) for q in data]
                    data_manager.save_question_bank(bank_name, questions)

                return jsonify({
                    "success": True,
                    "total": len(data) if isinstance(data, list) else 0,
                    "success_count": len(data) if isinstance(data, list) else 0,
                    "failed_count": 0,
                    "preview_only": preview_only,
                    "imported_count": len(data) if confirm else 0,
                    "data": [
                        {
                            "type": q.get("type", "choice"),
                            "question": q.get("question", ""),
                            "difficulty": q.get("difficulty", "medium"),
                            "answer": q.get("answer", ""),
                            "options": q.get("options", []),
                        }
                        for q in (data if isinstance(data, list) else [])
                    ],
                    "failed": [],
                })
            except json.JSONDecodeError as e:
                return jsonify({"success": False, "error": f"JSON 解析错误: {e!s}"})
        else:
            # 处理 Word 文件
            try:
                importer = WordImporter(file_path)

                result = importer.parse_preview(max_preview) if preview_only else importer.parse()

                # 设置默认分类和难度
                for q in result.success:
                    if not q.category or q.category == "默认分类":
                        q.category = category
                    if q.difficulty == "medium":
                        q.difficulty = difficulty

                # 如果是确认导入
                imported_count = 0
                if confirm and result.success:
                    bank_name = os.path.splitext(file.filename)[0]
                    imported_count = importer.save_to_bank(result, bank_name)["saved"]

                return jsonify({
                    "success": True,
                    "total": result.total,
                    "success_count": result.success_count,
                    "failed_count": result.failed_count,
                    "preview_only": preview_only,
                    "imported_count": imported_count,
                    "data": [
                        {
                            "type": q.q_type,
                            "question": q.question,
                            "difficulty": q.difficulty,
                            "answer": q.answer,
                            "options": q.options,
                            "explanation": q.explanation,
                        }
                        for q in result.success
                    ],
                    "failed": [
                        {"raw_text": q.raw_text, "line_number": q.line_number, "error": q.error} for q in result.failed
                    ],
                })
            except ImportError as e:
                return jsonify({"success": False, "error": str(e)})

    except (OSError, ValueError, TypeError, json.JSONDecodeError) as e:
        return jsonify({"success": False, "error": str(e)})
    finally:
        # 清理临时文件
        if pathlib.Path(file_path).exists():
            with contextlib.suppress(BaseException):
                pathlib.Path(file_path).unlink()


def create_app() -> Flask:
    """创建 Flask 应用."""
    return app


def main() -> None:
    """运行应用."""
    import os

    # 检查是否为生产模式
    debug_mode = os.environ.get("QUIZTRAINER_DEBUG", "false").lower() == "true"
    host = os.environ.get("QUIZTRAINER_HOST", "127.0.0.1")
    port = int(os.environ.get("QUIZTRAINER_PORT", "5000"))

    app.run(debug=debug_mode, host=host, port=port)


if __name__ == "__main__":
    main()
