import "server-only"

import { count, desc, eq, inArray, sql } from "drizzle-orm"

import { db } from "@/shared/db"
import {
  errorBookItems,
  questions,
  knowledgePoints,
  chapters,
  subjects,
  users,
  classEnrollments,
  classes,
} from "@/shared/db/schema"
import { getStudentIdsByClassIds } from "@/modules/classes/data-access"
import { ROLE_NAMES } from "@/shared/types/permissions"

import type {
  ChapterWeakness,
  ClassErrorOverview,
  KnowledgePointWeakness,
  StudentErrorBookSummary,
  SubjectErrorOverview,
} from "./types"
import { buildStudentErrorWhereClause, toStatus, toStringArray } from "./data-access"

// ---------------------------------------------------------------------------
// 教师/管理员分析查询（SQL 聚合优化）
// ---------------------------------------------------------------------------

/** 查询多个学生的错题统计（教师视图，支持按学科过滤） */
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)

  // 使用 SQL 条件聚合（GROUP BY studentId）
  const rows = await db
    .select({
      studentId: errorBookItems.studentId,
      totalCount: count(),
      newCount: sql<number>`sum(case when ${errorBookItems.status} = 'new' then 1 else 0 end)`,
      learningCount: sql<number>`sum(case when ${errorBookItems.status} = 'learning' then 1 else 0 end)`,
      masteredCount: sql<number>`sum(case when ${errorBookItems.status} = 'mastered' then 1 else 0 end)`,
      dueReviewCount: sql<number>`sum(case when ${errorBookItems.status} not in ('mastered', 'archived') and (${errorBookItems.nextReviewAt} is null or ${errorBookItems.nextReviewAt} <= ${now}) then 1 else 0 end)`,
      lastActivityAt: sql<Date | null>`max(${errorBookItems.updatedAt})`,
    })
    .from(errorBookItems)
    .where(whereClause)
    .groupBy(errorBookItems.studentId)

  // 查询学生所属班级（用于按班级分组展示）
  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 })
    }
  }

  return rows.map((row) => {
    const totalCount = Number(row.totalCount)
    const masteredCount = Number(row.masteredCount)
    const classInfo = studentClassMap.get(row.studentId)
    return {
      studentId: row.studentId,
      totalCount,
      newCount: Number(row.newCount),
      learningCount: Number(row.learningCount),
      masteredCount,
      dueReviewCount: Number(row.dueReviewCount),
      masteredRate: totalCount > 0 ? masteredCount / totalCount : 0,
      lastActivityAt: row.lastActivityAt,
      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)

  // 使用子查询聚合，避免 GROUP BY JSON 列的问题
  // 注意：子查询中的 raw SQL 字段（count()、sql``）必须声明 alias，
  // 否则外层引用时会报 "doesn't have an alias declared" 错误
  const aggregatedSubquery = db
    .select({
      questionId: errorBookItems.questionId,
      errorCount: count().as("errorCount"),
      masteredCount: sql<number>`sum(case when ${errorBookItems.status} = 'mastered' then 1 else 0 end)`.as("masteredCount"),
    })
    .from(errorBookItems)
    .where(whereClause)
    .groupBy(errorBookItems.questionId)
    .as("aggregated")

  const rows = await db
    .select({
      questionId: aggregatedSubquery.questionId,
      errorCount: aggregatedSubquery.errorCount,
      masteredCount: aggregatedSubquery.masteredCount,
      content: questions.content,
      type: questions.type,
    })
    .from(aggregatedSubquery)
    .innerJoin(questions, eq(questions.id, aggregatedSubquery.questionId))
    .orderBy(desc(aggregatedSubquery.errorCount))
    .limit(limit)

  return rows.map((row) => ({
    questionId: row.questionId,
    questionContent: row.content,
    questionType: row.type,
    errorCount: Number(row.errorCount),
    masteredCount: Number(row.masteredCount),
  }))
}

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

  const whereClause = buildStudentErrorWhereClause(studentIds, subjectId)

  // knowledgePointIds 是 JSON 数组字段，无法直接用 SQL GROUP BY，保留 JS 聚合
  // 仅查询必要字段（不查询全行）
  const rows = await db
    .select({
      status: errorBookItems.status,
      knowledgePointIds: errorBookItems.knowledgePointIds,
    })
    .from(errorBookItems)
    .where(whereClause)

  // 展开知识点并统计（使用类型守卫替代 as 断言）
  const kpMap = new Map<string, { errorCount: number; masteredCount: number }>()

  for (const row of rows) {
    const kps = toStringArray(row.knowledgePointIds) ?? []
    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 []

  // 使用 SQL GROUP BY 聚合
  const rows = await db
    .select({
      subjectId: errorBookItems.subjectId,
      errorCount: count(),
      masteredCount: sql<number>`sum(case when ${errorBookItems.status} = 'mastered' then 1 else 0 end)`,
    })
    .from(errorBookItems)
    .where(inArray(errorBookItems.studentId, studentIds))
    .groupBy(errorBookItems.subjectId)

  // 查询学科名称
  const subjectIds = rows
    .map((r) => r.subjectId)
    .filter((s): s is string => s !== 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 rows.map((row) => {
    const errorCount = Number(row.errorCount)
    const masteredCount = Number(row.masteredCount)
    const sid = row.subjectId
    return {
      subjectId: sid,
      subjectName: sid ? (subjectNameMap.get(sid) ?? "未知学科") : "未分类",
      errorCount,
      masteredCount,
      masteryRate: errorCount > 0 ? masteredCount / 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)

  // knowledgePointIds 是 JSON 数组字段，无法直接用 SQL GROUP BY，保留 JS 聚合
  // 仅查询必要字段（不查询全行）
  const rows = await db
    .select({
      status: errorBookItems.status,
      knowledgePointIds: errorBookItems.knowledgePointIds,
    })
    .from(errorBookItems)
    .where(whereClause)

  // 展开知识点，建立 kpId → 错题统计（使用类型守卫替代 as 断言）
  const kpErrorMap = new Map<string, { errorCount: number; masteredCount: number }>()
  for (const row of rows) {
    const kps = toStringArray(row.knowledgePointIds) ?? []
    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)
  }

  // 查询所有相关学生的错题（使用 SQL 聚合按 studentId 分组）
  const allStudentIds = Array.from(new Set(enrollmentRows.map((r) => r.studentId)))
  if (allStudentIds.length === 0) return []

  const now = new Date()
  const whereClause = buildStudentErrorWhereClause(allStudentIds, subjectId)
  const errorRows = await db
    .select({
      studentId: errorBookItems.studentId,
      total: count(),
      mastered: sql<number>`sum(case when ${errorBookItems.status} = 'mastered' then 1 else 0 end)`,
      due: sql<number>`sum(case when ${errorBookItems.status} not in ('mastered', 'archived') and (${errorBookItems.nextReviewAt} is null or ${errorBookItems.nextReviewAt} <= ${now}) then 1 else 0 end)`,
    })
    .from(errorBookItems)
    .where(whereClause)
    .groupBy(errorBookItems.studentId)

  // 按学生聚合结果映射
  const studentStatMap = new Map<string, { total: number; mastered: number; due: number }>()
  for (const row of errorRows) {
    studentStatMap.set(row.studentId, {
      total: Number(row.total),
      mastered: Number(row.mastered),
      due: Number(row.due),
    })
  }

  // 按班级聚合
  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 now = new Date()
  // 使用 SQL GROUP BY 聚合（含 count distinct studentId）
  const rows = await db
    .select({
      subjectId: errorBookItems.subjectId,
      totalErrorItems: count(),
      studentCount: sql<number>`count(distinct ${errorBookItems.studentId})`,
      masteredCount: sql<number>`sum(case when ${errorBookItems.status} = 'mastered' then 1 else 0 end)`,
      dueReviewCount: sql<number>`sum(case when ${errorBookItems.status} not in ('mastered', 'archived') and (${errorBookItems.nextReviewAt} is null or ${errorBookItems.nextReviewAt} <= ${now}) then 1 else 0 end)`,
    })
    .from(errorBookItems)
    .where(inArray(errorBookItems.studentId, studentIds))
    .groupBy(errorBookItems.subjectId)

  // 过滤无学科的错题
  const filtered = rows.filter((r) => r.subjectId !== null)
  if (filtered.length === 0) return []

  // 查询学科名称
  const subjectIds = filtered.map((r) => r.subjectId as string)
  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 filtered
    .map((row) => {
      const totalErrorItems = Number(row.totalErrorItems)
      const masteredCount = Number(row.masteredCount)
      const sid = row.subjectId as string
      return {
        subjectId: sid,
        subjectName: subjectNameMap.get(sid) ?? "未知学科",
        totalErrorItems,
        studentCount: Number(row.studentCount),
        averageMasteryRate: totalErrorItems > 0 ? masteredCount / totalErrorItems : 0,
        dueReviewCount: Number(row.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 ?? "未知"]))
}

/** 按班级 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, ROLE_NAMES.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)
}
