import "server-only"

import { cache } from "react"
import { and, count, desc, eq, inArray, isNull, lte, or, sql, type SQL } from "drizzle-orm"
import { createId } from "@paralleldrive/cuid2"

import { db } from "@/shared/db"
import {
  errorBookItems,
  errorBookReviews,
  questions,
  questionsToKnowledgePoints,
  knowledgePoints,
  chapters,
  subjects,
  users,
  classEnrollments,
  classes,
} from "@/shared/db/schema"
import { getStudentIdsByClassIds } from "@/modules/classes/data-access"
import {
  calculateNewInterval,
  calculateNewMastery,
  deriveStatus,
  calculateNextReviewAt,
  calculateNewCorrectStreak,
} from "./sm2-algorithm"

import type {
  ErrorBookItem,
  ErrorBookItemDetail,
  ErrorBookListResult,
  ErrorBookReviewRecord,
  ErrorBookStats,
  ErrorBookStatusValue,
  GetErrorBookItemsParams,
  KnowledgePointWeakness,
  ChapterWeakness,
  ClassErrorOverview,
  SubjectErrorOverview,
  StudentErrorBookSummary,
} from "./types"
import type { ErrorBookReviewResult } from "./schema"

// ---------------------------------------------------------------------------
// SM-2 间隔重复算法（简化版）
// ---------------------------------------------------------------------------

// ---------------------------------------------------------------------------
// 类型守卫
// ---------------------------------------------------------------------------

const isReviewResult = (v: unknown): v is ErrorBookReviewResult =>
  v === "again" || v === "hard" || v === "good" || v === "easy"

const toReviewResult = (v: string | null | undefined): ErrorBookReviewResult =>
  isReviewResult(v) ? v : "again"

const isStatus = (v: unknown): v is ErrorBookStatusValue =>
  v === "new" || v === "learning" || v === "mastered" || v === "archived"

const toStatus = (v: string | null | undefined): ErrorBookStatusValue =>
  isStatus(v) ? v : "new"

// ---------------------------------------------------------------------------
// 行映射
// ---------------------------------------------------------------------------

function mapRowToItem(row: typeof errorBookItems.$inferSelect & {
  question?: typeof questions.$inferSelect | null
  subject?: typeof subjects.$inferSelect | null
}): ErrorBookItem {
  return {
    id: row.id,
    studentId: row.studentId,
    questionId: row.questionId,
    sourceType: row.sourceType as ErrorBookItem["sourceType"],
    sourceId: row.sourceId,
    studentAnswer: row.studentAnswer,
    correctAnswer: row.correctAnswer,
    subjectId: row.subjectId,
    knowledgePointIds: row.knowledgePointIds as string[] | null,
    status: toStatus(row.status),
    masteryLevel: row.masteryLevel,
    nextReviewAt: row.nextReviewAt,
    reviewInterval: row.reviewInterval,
    reviewCount: row.reviewCount,
    correctStreak: row.correctStreak,
    note: row.note,
    errorTags: row.errorTags as string[] | null,
    createdAt: row.createdAt,
    updatedAt: row.updatedAt,
    question: row.question
      ? {
          id: row.question.id,
          content: row.question.content,
          type: row.question.type,
          difficulty: row.question.difficulty,
        }
      : null,
    subjectName: row.subject?.name ?? null,
  }
}

// ---------------------------------------------------------------------------
// 查询：错题本列表
// ---------------------------------------------------------------------------

export const getErrorBookItems = cache(async (params: GetErrorBookItemsParams): Promise<ErrorBookListResult> => {
  const { studentId, q, page = 1, pageSize = 20, status, sourceType, subjectId, dueOnly } = params
  const offset = (page - 1) * pageSize

  const conditions: SQL[] = [eq(errorBookItems.studentId, studentId)]

  if (status) {
    conditions.push(eq(errorBookItems.status, status))
  }

  if (sourceType) {
    conditions.push(eq(errorBookItems.sourceType, sourceType))
  }

  if (subjectId) {
    conditions.push(eq(errorBookItems.subjectId, subjectId))
  }

  if (dueOnly) {
    const now = new Date()
    conditions.push(
      or(
        isNull(errorBookItems.nextReviewAt),
        lte(errorBookItems.nextReviewAt, now)
      )!
    )
  }

  if (q && q.trim().length > 0) {
    const needle = `%${q.trim().toLowerCase()}%`
    conditions.push(sql`LOWER(CAST(${errorBookItems.note} AS CHAR)) LIKE ${needle}`)
  }

  const whereClause = and(...conditions)

  const [totalResult] = await db
    .select({ value: count() })
    .from(errorBookItems)
    .where(whereClause)

  const total = Number(totalResult?.value ?? 0)

  const rows = await db.query.errorBookItems.findMany({
    where: whereClause,
    limit: pageSize,
    offset,
    orderBy: [desc(errorBookItems.createdAt)],
    with: {
      question: {
        columns: {
          id: true,
          content: true,
          type: true,
          difficulty: true,
        },
      },
      subject: {
        columns: {
          id: true,
          name: true,
        },
      },
    },
  })

  return {
    data: rows.map((row) => mapRowToItem(row as unknown as Parameters<typeof mapRowToItem>[0])),
    meta: {
      page,
      pageSize,
      total,
      totalPages: Math.ceil(total / pageSize),
    },
  }
})

// ---------------------------------------------------------------------------
// 查询：错题详情
// ---------------------------------------------------------------------------

export const getErrorBookItemById = cache(async (
  itemId: string,
  studentId: string
): Promise<ErrorBookItemDetail | null> => {
  const row = await db.query.errorBookItems.findFirst({
    where: and(
      eq(errorBookItems.id, itemId),
      eq(errorBookItems.studentId, studentId)
    ),
    with: {
      question: {
        columns: {
          id: true,
          content: true,
          type: true,
          difficulty: true,
        },
      },
      subject: {
        columns: {
          id: true,
          name: true,
        },
      },
      reviews: {
        orderBy: [desc(errorBookReviews.reviewedAt)],
        limit: 20,
      },
    },
  })

  if (!row) return null

  const base = mapRowToItem(row as unknown as Parameters<typeof mapRowToItem>[0])
  const reviews: ErrorBookReviewRecord[] = (row.reviews ?? []).map((r) => ({
    id: r.id,
    result: toReviewResult(r.result),
    reviewedAt: r.reviewedAt,
    newInterval: r.newInterval,
    newMasteryLevel: r.newMasteryLevel,
  }))

  return { ...base, reviews }
})

// ---------------------------------------------------------------------------
// 查询：错题本统计
// ---------------------------------------------------------------------------

export const getErrorBookStats = cache(async (studentId: string): Promise<ErrorBookStats> => {
  const now = new Date()

  const rows = await db
    .select({
      status: errorBookItems.status,
      nextReviewAt: errorBookItems.nextReviewAt,
    })
    .from(errorBookItems)
    .where(eq(errorBookItems.studentId, studentId))

  const total = rows.length
  let newCount = 0
  let learningCount = 0
  let masteredCount = 0
  let archivedCount = 0
  let dueReviewCount = 0

  for (const row of rows) {
    const status = toStatus(row.status)
    if (status === "new") newCount++
    else if (status === "learning") learningCount++
    else if (status === "mastered") masteredCount++
    else if (status === "archived") archivedCount++

    if (status !== "mastered" && status !== "archived") {
      if (!row.nextReviewAt || row.nextReviewAt <= now) {
        dueReviewCount++
      }
    }
  }

  return {
    totalCount: total,
    newCount,
    learningCount,
    masteredCount,
    archivedCount,
    dueReviewCount,
    masteredRate: total > 0 ? masteredCount / total : 0,
  }
})

// ---------------------------------------------------------------------------
// 写入：手动添加错题
// ---------------------------------------------------------------------------

export async function createErrorBookItem(
  studentId: string,
  input: {
    questionId: string
    studentAnswer?: unknown
    correctAnswer?: unknown
    subjectId?: string
    knowledgePointIds?: string[]
    note?: string
    errorTags?: string[]
  }
): Promise<string> {
  const newId = createId()
  const now = new Date()

  // 如果未提供知识点，从题目关联中查询
  let knowledgePointIds = input.knowledgePointIds
  if (!knowledgePointIds || knowledgePointIds.length === 0) {
    const kps = await db
      .select({ id: questionsToKnowledgePoints.knowledgePointId })
      .from(questionsToKnowledgePoints)
      .where(eq(questionsToKnowledgePoints.questionId, input.questionId))
    knowledgePointIds = kps.map((k) => k.id)
  }

  // 如果未提供学科，从题目关联中查询（暂留空，学科由调用方提供）

  await db.insert(errorBookItems).values({
    id: newId,
    studentId,
    questionId: input.questionId,
    sourceType: "manual",
    sourceId: null,
    studentAnswer: input.studentAnswer ?? null,
    correctAnswer: input.correctAnswer ?? null,
    subjectId: input.subjectId ?? null,
    knowledgePointIds: knowledgePointIds ?? null,
    status: "new",
    masteryLevel: 0,
    nextReviewAt: now, // 立即可复习
    reviewInterval: 1,
    reviewCount: 0,
    correctStreak: 0,
    note: input.note ?? null,
    errorTags: input.errorTags ?? null,
  })

  return newId
}

// ---------------------------------------------------------------------------
// 写入：更新笔记
// ---------------------------------------------------------------------------

export async function updateErrorBookNote(
  itemId: string,
  studentId: string,
  input: { note?: string; errorTags?: string[] }
): Promise<void> {
  await db
    .update(errorBookItems)
    .set({
      ...(input.note !== undefined ? { note: input.note } : {}),
      ...(input.errorTags !== undefined ? { errorTags: input.errorTags } : {}),
      updatedAt: new Date(),
    })
    .where(
      and(
        eq(errorBookItems.id, itemId),
        eq(errorBookItems.studentId, studentId)
      )
    )
}

// ---------------------------------------------------------------------------
// 写入：记录复习结果（SM-2 算法）
// ---------------------------------------------------------------------------

export async function recordReview(
  itemId: string,
  studentId: string,
  result: ErrorBookReviewResult
): Promise<void> {
  const item = await db.query.errorBookItems.findFirst({
    where: and(
      eq(errorBookItems.id, itemId),
      eq(errorBookItems.studentId, studentId)
    ),
  })

  if (!item) throw new Error("错题条目不存在")

  const newInterval = calculateNewInterval(item.reviewInterval, result, item.reviewCount)
  const newStreak = calculateNewCorrectStreak(item.correctStreak, result)
  const newMastery = calculateNewMastery(item.masteryLevel, result, newStreak)
  const newStatus = deriveStatus(newMastery, newStreak)
  const nextReviewAt = newStatus === "mastered" ? null : calculateNextReviewAt(newInterval)

  await db.transaction(async (tx) => {
    await tx.insert(errorBookReviews).values({
      id: createId(),
      itemId,
      studentId,
      result,
      reviewedAt: new Date(),
      newInterval,
      newMasteryLevel: newMastery,
    })

    await tx
      .update(errorBookItems)
      .set({
        status: newStatus,
        masteryLevel: newMastery,
        reviewInterval: newInterval,
        reviewCount: item.reviewCount + 1,
        correctStreak: newStreak,
        nextReviewAt,
        updatedAt: new Date(),
      })
      .where(eq(errorBookItems.id, itemId))
  })
}

// ---------------------------------------------------------------------------
// 写入：删除错题
// ---------------------------------------------------------------------------

export async function deleteErrorBookItem(itemId: string, studentId: string): Promise<void> {
  await db
    .delete(errorBookItems)
    .where(
      and(
        eq(errorBookItems.id, itemId),
        eq(errorBookItems.studentId, studentId)
      )
    )
}

// ---------------------------------------------------------------------------
// 写入：归档错题
// ---------------------------------------------------------------------------

export async function archiveErrorBookItem(itemId: string, studentId: string): Promise<void> {
  await db
    .update(errorBookItems)
    .set({ status: "archived", updatedAt: new Date() })
    .where(
      and(
        eq(errorBookItems.id, itemId),
        eq(errorBookItems.studentId, studentId)
      )
    )
}

// ---------------------------------------------------------------------------
// 自动采集函数已迁移至 data-access-collection.ts
// 通过跨模块接口（exams/homework/questions data-access）避免直查多表，修复三层架构违规
// ---------------------------------------------------------------------------

export {
  collectFromExamSubmission,
  collectFromHomeworkSubmission,
} from "./data-access-collection"

// ---------------------------------------------------------------------------
// 跨模块查询接口：供教师/家长视图使用
// ---------------------------------------------------------------------------

/** 构建学生错题查询的 where 条件（支持按学科过滤） */
function buildStudentErrorWhereClause(
  studentIds: string[],
  subjectId?: string | null
): SQL | undefined {
  const conditions: SQL[] = [inArray(errorBookItems.studentId, studentIds)]
  if (subjectId) {
    conditions.push(eq(errorBookItems.subjectId, subjectId))
  }
  return and(...conditions)
}

/** 查询多个学生的错题统计（教师视图，支持按学科过滤） */
export async function getStudentErrorBookSummaries(
  studentIds: string[],
  subjectId?: string | null
): Promise<StudentErrorBookSummary[]> {
  if (studentIds.length === 0) return []

  const now = new Date()
  const whereClause = buildStudentErrorWhereClause(studentIds, subjectId)

  const rows = await db
    .select({
      studentId: errorBookItems.studentId,
      status: errorBookItems.status,
      nextReviewAt: errorBookItems.nextReviewAt,
      updatedAt: errorBookItems.updatedAt,
    })
    .from(errorBookItems)
    .where(whereClause)

  // 查询学生所属班级（用于按班级分组展示）
  const enrollmentRows = await db
    .select({
      studentId: classEnrollments.studentId,
      classId: classes.id,
      className: classes.name,
    })
    .from(classEnrollments)
    .innerJoin(classes, eq(classEnrollments.classId, classes.id))
    .where(inArray(classEnrollments.studentId, studentIds))

  const studentClassMap = new Map<string, { classId: string; className: string }>()
  for (const row of enrollmentRows) {
    // 取第一个班级（学生通常只属于一个班）
    if (!studentClassMap.has(row.studentId)) {
      studentClassMap.set(row.studentId, { classId: row.classId, className: row.className })
    }
  }

  const map = new Map<string, {
    totalCount: number
    newCount: number
    learningCount: number
    masteredCount: number
    dueReviewCount: number
    lastActivityAt: Date | null
  }>()

  for (const row of rows) {
    const stat = map.get(row.studentId) ?? {
      totalCount: 0,
      newCount: 0,
      learningCount: 0,
      masteredCount: 0,
      dueReviewCount: 0,
      lastActivityAt: null,
    }
    stat.totalCount++
    const status = toStatus(row.status)
    if (status === "new") stat.newCount++
    else if (status === "learning") stat.learningCount++
    else if (status === "mastered") stat.masteredCount++

    if (status !== "mastered" && status !== "archived") {
      if (!row.nextReviewAt || row.nextReviewAt <= now) {
        stat.dueReviewCount++
      }
    }

    if (!stat.lastActivityAt || row.updatedAt > stat.lastActivityAt) {
      stat.lastActivityAt = row.updatedAt
    }

    map.set(row.studentId, stat)
  }

  return Array.from(map.entries()).map(([studentId, stat]) => {
    const classInfo = studentClassMap.get(studentId)
    return {
      studentId,
      ...stat,
      masteredRate: stat.totalCount > 0 ? stat.masteredCount / stat.totalCount : 0,
      classId: classInfo?.classId ?? null,
      className: classInfo?.className ?? null,
    }
  })
}

/** 查询班级内错题最多的题目（教师视图：高频错题，支持按学科过滤） */
export async function getTopWrongQuestionsByStudentIds(
  studentIds: string[],
  limit = 10,
  subjectId?: string | null
): Promise<Array<{
  questionId: string
  questionContent: unknown
  questionType: string
  errorCount: number
  masteredCount: number
}>> {
  if (studentIds.length === 0) return []

  const whereClause = buildStudentErrorWhereClause(studentIds, subjectId)

  const rows = await db
    .select({
      questionId: errorBookItems.questionId,
      status: errorBookItems.status,
      content: questions.content,
      type: questions.type,
    })
    .from(errorBookItems)
    .innerJoin(questions, eq(questions.id, errorBookItems.questionId))
    .where(whereClause)

  const map = new Map<string, { questionContent: unknown; questionType: string; errorCount: number; masteredCount: number }>()

  for (const row of rows) {
    const stat = map.get(row.questionId) ?? {
      questionContent: row.content,
      questionType: row.type,
      errorCount: 0,
      masteredCount: 0,
    }
    stat.errorCount++
    if (toStatus(row.status) === "mastered") stat.masteredCount++
    map.set(row.questionId, stat)
  }

  return Array.from(map.entries())
    .map(([questionId, stat]) => ({ questionId, ...stat }))
    .sort((a, b) => b.errorCount - a.errorCount)
    .slice(0, limit)
}

/** 按班级 ID 查询学生 ID 列表（委托给 classes 模块） */
export async function getStudentIdsByClassIdList(classIds: string[]): Promise<string[]> {
  return await getStudentIdsByClassIds(classIds)
}

/**
 * 查询所有学生用户 ID（管理员视图）。
 * 通过 usersToRoles + roles 表关联查询 role === "student" 的用户。
 * 此函数封装了 DB 访问，避免 app 层直接查询 DB（遵循三层架构）。
 */
export async function getAllStudentIds(): Promise<string[]> {
  const { usersToRoles, roles } = await import("@/shared/db/schema")
  const studentRole = await db
    .select({ id: roles.id })
    .from(roles)
    .where(eq(roles.name, "student"))
    .limit(1)

  if (studentRole.length === 0) return []

  const userRoleRows = await db
    .select({ userId: usersToRoles.userId })
    .from(usersToRoles)
    .where(eq(usersToRoles.roleId, studentRole[0].id))

  return userRoleRows.map((r) => r.userId)
}

// ---------------------------------------------------------------------------
// 统计：知识点薄弱度 & 学科分布 & 章节维度（教师/管理员视图）
// ---------------------------------------------------------------------------

/** 查询多个学生的知识点薄弱度统计（支持按学科过滤，关联章节信息） */
export async function getKnowledgePointWeakness(
  studentIds: string[],
  limit = 10,
  subjectId?: string | null
): Promise<KnowledgePointWeakness[]> {
  if (studentIds.length === 0) return []

  const whereClause = buildStudentErrorWhereClause(studentIds, subjectId)

  // 查询这些学生的所有错题条目（含知识点）
  const rows = await db
    .select({
      itemId: errorBookItems.id,
      status: errorBookItems.status,
      knowledgePointIds: errorBookItems.knowledgePointIds,
    })
    .from(errorBookItems)
    .where(whereClause)

  // 展开知识点并统计
  const kpMap = new Map<string, { errorCount: number; masteredCount: number }>()

  for (const row of rows) {
    const kps = (row.knowledgePointIds as string[] | null) ?? []
    for (const kpId of kps) {
      const stat = kpMap.get(kpId) ?? { errorCount: 0, masteredCount: 0 }
      stat.errorCount++
      if (toStatus(row.status) === "mastered") stat.masteredCount++
      kpMap.set(kpId, stat)
    }
  }

  if (kpMap.size === 0) return []

  // 查询知识点名称及所属章节
  const kpIds = Array.from(kpMap.keys())
  const kpRows = await db
    .select({
      id: knowledgePoints.id,
      name: knowledgePoints.name,
      chapterId: knowledgePoints.chapterId,
    })
    .from(knowledgePoints)
    .where(inArray(knowledgePoints.id, kpIds))
  const kpInfoMap = new Map(kpRows.map((k) => [k.id, { name: k.name, chapterId: k.chapterId }]))

  // 查询章节标题
  const chapterIds = Array.from(new Set(
    kpRows.map((k) => k.chapterId).filter((c): c is string => c !== null)
  ))
  let chapterTitleMap = new Map<string, string>()
  if (chapterIds.length > 0) {
    const chapterRows = await db
      .select({ id: chapters.id, title: chapters.title })
      .from(chapters)
      .where(inArray(chapters.id, chapterIds))
    chapterTitleMap = new Map(chapterRows.map((c) => [c.id, c.title]))
  }

  return Array.from(kpMap.entries())
    .map(([kpId, stat]) => {
      const info = kpInfoMap.get(kpId)
      return {
        knowledgePointId: kpId,
        knowledgePointName: info?.name ?? "未知知识点",
        errorCount: stat.errorCount,
        masteredCount: stat.masteredCount,
        totalCount: stat.errorCount,
        masteryRate: stat.errorCount > 0 ? stat.masteredCount / stat.errorCount : 0,
        chapterId: info?.chapterId ?? null,
        chapterTitle: info?.chapterId ? (chapterTitleMap.get(info.chapterId) ?? null) : null,
      }
    })
    .sort((a, b) => {
      // 按错误数降序，掌握率升序（最薄弱的在前）
      if (b.errorCount !== a.errorCount) return b.errorCount - a.errorCount
      return a.masteryRate - b.masteryRate
    })
    .slice(0, limit)
}

/** 查询多个学生的学科错题分布 */
export async function getSubjectErrorDistribution(
  studentIds: string[]
): Promise<Array<{
  subjectId: string | null
  subjectName: string
  errorCount: number
  masteredCount: number
  masteryRate: number
}>> {
  if (studentIds.length === 0) return []

  const rows = await db
    .select({
      subjectId: errorBookItems.subjectId,
      status: errorBookItems.status,
    })
    .from(errorBookItems)
    .where(inArray(errorBookItems.studentId, studentIds))

  const subjectMap = new Map<string | null, { errorCount: number; masteredCount: number }>()

  for (const row of rows) {
    const key = row.subjectId
    const stat = subjectMap.get(key) ?? { errorCount: 0, masteredCount: 0 }
    stat.errorCount++
    if (toStatus(row.status) === "mastered") stat.masteredCount++
    subjectMap.set(key, stat)
  }

  // 查询学科名称
  const subjectIds = Array.from(subjectMap.keys()).filter((k): k is string => k !== null)
  let subjectNameMap = new Map<string, string>()
  if (subjectIds.length > 0) {
    const subjectRows = await db
      .select({ id: subjects.id, name: subjects.name })
      .from(subjects)
      .where(inArray(subjects.id, subjectIds))
    subjectNameMap = new Map(subjectRows.map((s) => [s.id, s.name]))
  }

  return Array.from(subjectMap.entries()).map(([sid, stat]) => ({
    subjectId: sid,
    subjectName: sid ? (subjectNameMap.get(sid) ?? "未知学科") : "未分类",
    errorCount: stat.errorCount,
    masteredCount: stat.masteredCount,
    masteryRate: stat.errorCount > 0 ? stat.masteredCount / stat.errorCount : 0,
  }))
}

/**
 * 查询章节薄弱度统计（哪些课在错）。
 * 通过 errorBookItems.knowledgePointIds → knowledgePoints.chapterId → chapters 关联。
 * 支持按学科过滤（通过 errorBookItems.subjectId）。
 */
export async function getChapterWeakness(
  studentIds: string[],
  limit = 10,
  subjectId?: string | null
): Promise<ChapterWeakness[]> {
  if (studentIds.length === 0) return []

  const whereClause = buildStudentErrorWhereClause(studentIds, subjectId)

  // 查询错题条目（含知识点和状态）
  const rows = await db
    .select({
      itemId: errorBookItems.id,
      status: errorBookItems.status,
      knowledgePointIds: errorBookItems.knowledgePointIds,
    })
    .from(errorBookItems)
    .where(whereClause)

  // 展开知识点，建立 kpId → 错题统计
  const kpErrorMap = new Map<string, { errorCount: number; masteredCount: number }>()
  for (const row of rows) {
    const kps = (row.knowledgePointIds as string[] | null) ?? []
    for (const kpId of kps) {
      const stat = kpErrorMap.get(kpId) ?? { errorCount: 0, masteredCount: 0 }
      stat.errorCount++
      if (toStatus(row.status) === "mastered") stat.masteredCount++
      kpErrorMap.set(kpId, stat)
    }
  }

  if (kpErrorMap.size === 0) return []

  // 查询知识点 → 章节映射
  const kpIds = Array.from(kpErrorMap.keys())
  const kpRows = await db
    .select({
      id: knowledgePoints.id,
      name: knowledgePoints.name,
      chapterId: knowledgePoints.chapterId,
    })
    .from(knowledgePoints)
    .where(inArray(knowledgePoints.id, kpIds))

  // 按章节聚合
  const chapterMap = new Map<string, {
    errorCount: number
    masteredCount: number
    knowledgePointCount: number
    topKps: Array<{ knowledgePointId: string; knowledgePointName: string; errorCount: number }>
  }>()

  for (const kp of kpRows) {
    if (!kp.chapterId) continue
    const kpStat = kpErrorMap.get(kp.id)
    if (!kpStat) continue

    const chapterStat = chapterMap.get(kp.chapterId) ?? {
      errorCount: 0,
      masteredCount: 0,
      knowledgePointCount: 0,
      topKps: [],
    }
    chapterStat.errorCount += kpStat.errorCount
    chapterStat.masteredCount += kpStat.masteredCount
    chapterStat.knowledgePointCount++
    chapterStat.topKps.push({
      knowledgePointId: kp.id,
      knowledgePointName: kp.name,
      errorCount: kpStat.errorCount,
    })
    chapterMap.set(kp.chapterId, chapterStat)
  }

  if (chapterMap.size === 0) return []

  // 查询章节标题
  const chapterIds = Array.from(chapterMap.keys())
  const chapterRows = await db
    .select({ id: chapters.id, title: chapters.title })
    .from(chapters)
    .where(inArray(chapters.id, chapterIds))
  const chapterTitleMap = new Map(chapterRows.map((c) => [c.id, c.title]))

  return Array.from(chapterMap.entries())
    .map(([chapterId, stat]) => ({
      chapterId,
      chapterTitle: chapterTitleMap.get(chapterId) ?? "未知章节",
      errorCount: stat.errorCount,
      masteredCount: stat.masteredCount,
      knowledgePointCount: stat.knowledgePointCount,
      masteryRate: stat.errorCount > 0 ? stat.masteredCount / stat.errorCount : 0,
      topKnowledgePoints: stat.topKps
        .sort((a, b) => b.errorCount - a.errorCount)
        .slice(0, 3),
    }))
    .sort((a, b) => b.errorCount - a.errorCount)
    .slice(0, limit)
}

/**
 * 查询按班级分组的错题概览（教师视图：分班显示）。
 * 对每个班级统计：学生数、错题总数、人均错题数、平均掌握率、待复习数。
 * 支持按学科过滤。
 */
export async function getClassErrorOverviews(
  classIds: string[],
  subjectId?: string | null
): Promise<ClassErrorOverview[]> {
  if (classIds.length === 0) return []

  // 查询每个班级的学生
  const enrollmentRows = await db
    .select({
      classId: classEnrollments.classId,
      studentId: classEnrollments.studentId,
      className: classes.name,
    })
    .from(classEnrollments)
    .innerJoin(classes, eq(classEnrollments.classId, classes.id))
    .where(inArray(classEnrollments.classId, classIds))

  const classStudentMap = new Map<string, { className: string; studentIds: Set<string> }>()
  for (const row of enrollmentRows) {
    const entry = classStudentMap.get(row.classId) ?? { className: row.className, studentIds: new Set<string>() }
    entry.studentIds.add(row.studentId)
    classStudentMap.set(row.classId, entry)
  }

  // 查询所有相关学生的错题
  const allStudentIds = Array.from(new Set(enrollmentRows.map((r) => r.studentId)))
  if (allStudentIds.length === 0) return []

  const whereClause = buildStudentErrorWhereClause(allStudentIds, subjectId)
  const errorRows = await db
    .select({
      studentId: errorBookItems.studentId,
      status: errorBookItems.status,
      nextReviewAt: errorBookItems.nextReviewAt,
    })
    .from(errorBookItems)
    .where(whereClause)

  const now = new Date()
  // 按学生聚合
  const studentStatMap = new Map<string, { total: number; mastered: number; due: number }>()
  for (const row of errorRows) {
    const stat = studentStatMap.get(row.studentId) ?? { total: 0, mastered: 0, due: 0 }
    stat.total++
    const status = toStatus(row.status)
    if (status === "mastered") stat.mastered++
    if (status !== "mastered" && status !== "archived") {
      if (!row.nextReviewAt || row.nextReviewAt <= now) stat.due++
    }
    studentStatMap.set(row.studentId, stat)
  }

  // 按班级聚合
  return Array.from(classStudentMap.entries()).map(([classId, entry]) => {
    const studentIds = Array.from(entry.studentIds)
    let totalError = 0
    let totalMastered = 0
    let totalDue = 0
    for (const sid of studentIds) {
      const stat = studentStatMap.get(sid)
      if (stat) {
        totalError += stat.total
        totalMastered += stat.mastered
        totalDue += stat.due
      }
    }
    return {
      classId,
      className: entry.className,
      studentCount: studentIds.length,
      totalErrorItems: totalError,
      averageErrorPerStudent: studentIds.length > 0 ? totalError / studentIds.length : 0,
      averageMasteryRate: totalError > 0 ? totalMastered / totalError : 0,
      dueReviewCount: totalDue,
    }
  }).sort((a, b) => b.totalErrorItems - a.totalErrorItems)
}

/**
 * 查询按学科分组的错题概览（用于学科 Tab 展示）。
 * 返回每个学科的错题总数、涉及学生数、平均掌握率、待复习数。
 */
export async function getSubjectErrorOverviews(
  studentIds: string[]
): Promise<SubjectErrorOverview[]> {
  if (studentIds.length === 0) return []

  const rows = await db
    .select({
      subjectId: errorBookItems.subjectId,
      studentId: errorBookItems.studentId,
      status: errorBookItems.status,
      nextReviewAt: errorBookItems.nextReviewAt,
    })
    .from(errorBookItems)
    .where(inArray(errorBookItems.studentId, studentIds))

  const now = new Date()
  const subjectMap = new Map<string, {
    totalErrorItems: number
    masteredCount: number
    dueReviewCount: number
    studentSet: Set<string>
  }>()

  for (const row of rows) {
    const key = row.subjectId
    if (!key) continue // 跳过无学科的错题
    const stat = subjectMap.get(key) ?? {
      totalErrorItems: 0,
      masteredCount: 0,
      dueReviewCount: 0,
      studentSet: new Set<string>(),
    }
    stat.totalErrorItems++
    stat.studentSet.add(row.studentId)
    const status = toStatus(row.status)
    if (status === "mastered") stat.masteredCount++
    if (status !== "mastered" && status !== "archived") {
      if (!row.nextReviewAt || row.nextReviewAt <= now) stat.dueReviewCount++
    }
    subjectMap.set(key, stat)
  }

  if (subjectMap.size === 0) return []

  // 查询学科名称
  const subjectIds = Array.from(subjectMap.keys())
  const subjectRows = await db
    .select({ id: subjects.id, name: subjects.name })
    .from(subjects)
    .where(inArray(subjects.id, subjectIds))
  const subjectNameMap = new Map(subjectRows.map((s) => [s.id, s.name]))

  return Array.from(subjectMap.entries())
    .map(([sid, stat]) => ({
      subjectId: sid,
      subjectName: subjectNameMap.get(sid) ?? "未知学科",
      totalErrorItems: stat.totalErrorItems,
      studentCount: stat.studentSet.size,
      averageMasteryRate: stat.totalErrorItems > 0 ? stat.masteredCount / stat.totalErrorItems : 0,
      dueReviewCount: stat.dueReviewCount,
    }))
    .sort((a, b) => b.totalErrorItems - a.totalErrorItems)
}

/** 查询学生姓名映射 */
export async function getStudentNameMap(studentIds: string[]): Promise<Map<string, string>> {
  if (studentIds.length === 0) return new Map()
  const rows = await db
    .select({ id: users.id, name: users.name })
    .from(users)
    .where(inArray(users.id, studentIds))
  return new Map(rows.map((r) => [r.id, r.name ?? "未知"]))
}
