import "server-only"

import { and, eq, inArray, notInArray, sql } from "drizzle-orm"

import { db } from "@/shared/db"
import {
  knowledgePointMastery,
  practiceAnswers,
  questions,
  questionsToKnowledgePoints,
} from "@/shared/db/schema"

import { isWeakChapterSourceMeta, isAiRecommendedSourceMeta, isKnowledgePointSourceMeta, isErrorVariantSourceMeta } from "./lib/source-meta"
import type { PracticeSourceMeta, QuestionSelectionResult } from "./types"

// ---------------------------------------------------------------------------
// 常量
// ---------------------------------------------------------------------------

/** 默认出题数量 */
const DEFAULT_QUESTION_COUNT = 10

/** 薄弱知识点掌握度阈值（低于此值视为薄弱） */
const WEAK_MASTERY_THRESHOLD = 60

/** 自动识别薄弱知识点默认返回数量 */
const DEFAULT_WEAK_KP_LIMIT = 5

// ---------------------------------------------------------------------------
// 出题策略
// ---------------------------------------------------------------------------

/**
 * 错题重做出题策略（原"错题变式"）。
 *
 * 从错题本中选取错题，直接使用原题进行练习。
 * 该策略不依赖 AI 生成变式题，确保即使 AI 不可用也能练习。
 *
 * 策略：
 * 1. 从 sourceQuestionIds 中查询题库中存在的题目
 * 2. 排除已在该练习会话中的题目（由调用方保证）
 * 3. 按难度升序排列（先易后难，建立信心）
 */
export async function selectForErrorVariant(
  sourceMeta: PracticeSourceMeta,
  questionCount: number,
): Promise<QuestionSelectionResult> {
  if (!isErrorVariantSourceMeta(sourceMeta)) {
    return { questionIds: [], variants: new Map() }
  }

  const sourceQuestionIds = sourceMeta.sourceQuestionIds
  if (sourceQuestionIds.length === 0) {
    return { questionIds: [], variants: new Map() }
  }

  const rows = await db
    .select({
      id: questions.id,
      difficulty: questions.difficulty,
    })
    .from(questions)
    .where(and(
      inArray(questions.id, sourceQuestionIds),
      sql`${questions.parentId} IS NULL`,
    ))
    .orderBy(questions.difficulty)
    .limit(questionCount)

  return {
    questionIds: rows.map((r) => r.id),
    variants: new Map(),
  }
}

/**
 * 知识点专项练习出题策略。
 *
 * 从题库中按知识点筛选题目，支持难度过滤。
 *
 * 策略：
 * 1. 按知识点 ID 查询关联题目
 * 2. 按难度筛选（如指定）
 * 3. 随机抽取指定数量
 */
export async function selectForKnowledgePoint(
  sourceMeta: PracticeSourceMeta,
  questionCount: number,
): Promise<QuestionSelectionResult> {
  if (!isKnowledgePointSourceMeta(sourceMeta)) {
    return { questionIds: [], variants: new Map() }
  }

  const { knowledgePointIds, difficulty } = sourceMeta
  if (knowledgePointIds.length === 0) {
    return { questionIds: [], variants: new Map() }
  }

  const conditions = [
    inArray(questionsToKnowledgePoints.knowledgePointId, knowledgePointIds),
    sql`${questions.parentId} IS NULL`,
  ]

  if (difficulty !== undefined) {
    conditions.push(eq(questions.difficulty, difficulty))
  }

  // 使用子查询获取题目 ID，然后随机排序
  const rows = await db
    .select({
      id: questions.id,
    })
    .from(questions)
    .innerJoin(
      questionsToKnowledgePoints,
      eq(questionsToKnowledgePoints.questionId, questions.id),
    )
    .where(and(...conditions))
    .groupBy(questions.id)
    .orderBy(sql`RAND()`)
    .limit(questionCount)

  return {
    questionIds: rows.map((r) => r.id),
    variants: new Map(),
  }
}

/**
 * 薄弱章节练习出题策略。
 *
 * 行为：
 * - 若 sourceMeta.weakKnowledgePointIds 已传入，直接使用
 * - 若未传入，调用 identifyWeakKnowledgePoints(studentId, chapterId) 自动识别
 *
 * 然后从（自动识别或显式传入的）薄弱知识点中抽题，
 * 排除学生已做过的题目避免重复。
 */
export async function selectForWeakChapter(
  studentId: string,
  sourceMeta: PracticeSourceMeta,
  questionCount: number,
): Promise<QuestionSelectionResult> {
  if (!isWeakChapterSourceMeta(sourceMeta)) {
    return { questionIds: [], variants: new Map() }
  }

  const { chapterId, weakKnowledgePointIds } = sourceMeta

  // 自动识别薄弱知识点（若未传入）
  let kpIds = weakKnowledgePointIds
  if (!kpIds || kpIds.length === 0) {
    kpIds = await identifyWeakKnowledgePoints(studentId, chapterId, DEFAULT_WEAK_KP_LIMIT)
  }

  if (kpIds.length === 0) {
    return { questionIds: [], variants: new Map() }
  }

  // 查询学生已做过的题目（避免重复）
  const answeredQuestionIds = await getStudentAnsweredQuestionIds(studentId)

  // 从薄弱知识点中抽题
  const conditions = [
    inArray(questionsToKnowledgePoints.knowledgePointId, kpIds),
    sql`${questions.parentId} IS NULL`,
  ]

  if (answeredQuestionIds.length > 0) {
    conditions.push(notInArray(questions.id, answeredQuestionIds))
  }

  const rows = await db
    .select({
      id: questions.id,
    })
    .from(questions)
    .innerJoin(
      questionsToKnowledgePoints,
      eq(questionsToKnowledgePoints.questionId, questions.id),
    )
    .where(and(...conditions))
    .groupBy(questions.id)
    .orderBy(sql`RAND()`)
    .limit(questionCount)

  return {
    questionIds: rows.map((r) => r.id),
    variants: new Map(),
  }
}

/**
 * AI 推荐练习出题策略。
 *
 * AI 推荐的知识点列表由上层（AI 分析）提供，
 * 此函数仅负责从推荐知识点中抽题。
 */
export async function selectForAiRecommended(
  studentId: string,
  sourceMeta: PracticeSourceMeta,
  questionCount: number,
): Promise<QuestionSelectionResult> {
  if (!isAiRecommendedSourceMeta(sourceMeta)) {
    return { questionIds: [], variants: new Map() }
  }

  const { recommendedKnowledgePointIds } = sourceMeta
  if (recommendedKnowledgePointIds.length === 0) {
    return { questionIds: [], variants: new Map() }
  }

  // 查询学生已做过的题目
  const answeredQuestionIds = await getStudentAnsweredQuestionIds(studentId)

  const conditions = [
    inArray(questionsToKnowledgePoints.knowledgePointId, recommendedKnowledgePointIds),
    sql`${questions.parentId} IS NULL`,
  ]

  if (answeredQuestionIds.length > 0) {
    conditions.push(notInArray(questions.id, answeredQuestionIds))
  }

  const rows = await db
    .select({
      id: questions.id,
    })
    .from(questions)
    .innerJoin(
      questionsToKnowledgePoints,
      eq(questionsToKnowledgePoints.questionId, questions.id),
    )
    .where(and(...conditions))
    .groupBy(questions.id)
    .orderBy(sql`RAND()`)
    .limit(questionCount)

  return {
    questionIds: rows.map((r) => r.id),
    variants: new Map(),
  }
}

// ---------------------------------------------------------------------------
// 主入口
// ---------------------------------------------------------------------------

/**
 * 根据练习类型选择题目。
 *
 * @param studentId 学生 ID
 * @param practiceType 练习类型
 * @param sourceMeta 来源元数据
 * @param questionCount 题目数量
 * @returns 选中的题目 ID 列表和变式题映射
 */
export async function selectQuestionsForPractice(
  studentId: string,
  practiceType: string,
  sourceMeta: PracticeSourceMeta,
  questionCount: number = DEFAULT_QUESTION_COUNT,
): Promise<QuestionSelectionResult> {
  switch (practiceType) {
    case "error_variant":
      return selectForErrorVariant(sourceMeta, questionCount)
    case "knowledge_point":
      return selectForKnowledgePoint(sourceMeta, questionCount)
    case "weak_chapter":
      return selectForWeakChapter(studentId, sourceMeta, questionCount)
    case "ai_recommended":
      return selectForAiRecommended(studentId, sourceMeta, questionCount)
    default:
      return { questionIds: [], variants: new Map() }
  }
}

// ---------------------------------------------------------------------------
// 辅助函数
// ---------------------------------------------------------------------------

/**
 * 获取学生已答过的题目 ID 列表（避免重复练习）。
 *
 * 从专项练习中汇总已答题目。
 * 为控制查询量，仅查询最近 1000 条记录。
 */
export async function getStudentAnsweredQuestionIds(studentId: string): Promise<string[]> {
  const rows = await db
    .select({ questionId: practiceAnswers.questionId })
    .from(practiceAnswers)
    .where(eq(practiceAnswers.studentId, studentId))
    .limit(1000)

  return Array.from(new Set(rows.map((r) => r.questionId)))
}

/**
 * 自动识别学生的薄弱知识点。
 *
 * 查询掌握度低于阈值的知识点，按掌握度升序排列。
 * 用于"薄弱章节练习"的自动推荐。
 *
 * @param studentId 学生 ID
 * @param chapterId 章节 ID（可选，不传则查询所有章节）
 * @param limit 返回数量限制
 * @returns 薄弱知识点 ID 列表
 */
export async function identifyWeakKnowledgePoints(
  studentId: string,
  chapterId?: string,
  limit: number = DEFAULT_WEAK_KP_LIMIT,
): Promise<string[]> {
  const conditions = [
    eq(knowledgePointMastery.studentId, studentId),
    sql`${knowledgePointMastery.masteryLevel} < ${WEAK_MASTERY_THRESHOLD}`,
  ]

  const rows = await db
    .select({
      knowledgePointId: knowledgePointMastery.knowledgePointId,
      masteryLevel: knowledgePointMastery.masteryLevel,
    })
    .from(knowledgePointMastery)
    .where(and(...conditions))
    .orderBy(knowledgePointMastery.masteryLevel)
    .limit(limit)

  return rows.map((r) => r.knowledgePointId)
}
