import "server-only"

import { cache } from "react"
import { and, asc, eq, inArray, sql, count } from "drizzle-orm"

import { db } from "@/shared/db"
import {
  chapters,
  knowledgePoints,
  knowledgePointPrerequisites,
  questionsToKnowledgePoints,
  knowledgePointMastery,
} from "@/shared/db/schema"
import type { KpWithRelations, MasteryInfo } from "./types"

/**
 * 获取教材下全书知识点（含前置依赖 + 关联题目数 + 章节标题）。
 *
 * 一次查询聚合，避免 N+1。
 */
export const getKnowledgePointsWithRelations = cache(async (
  textbookId: string,
): Promise<KpWithRelations[]> => {
  // 1. 查询全书知识点 + 章节标题
  const kpRows = await db
    .select({
      id: knowledgePoints.id,
      name: knowledgePoints.name,
      description: knowledgePoints.description,
      parentId: knowledgePoints.parentId,
      chapterId: knowledgePoints.chapterId,
      level: knowledgePoints.level,
      order: knowledgePoints.order,
      chapterTitle: chapters.title,
    })
    .from(knowledgePoints)
    .innerJoin(chapters, eq(chapters.id, knowledgePoints.chapterId))
    .where(eq(chapters.textbookId, textbookId))
    .orderBy(asc(chapters.order), asc(knowledgePoints.order), asc(knowledgePoints.name))

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

  const kpIds = kpRows.map((r) => r.id)

  // 2. 查询关联题目数（批量聚合）
  const questionCountRows = await db
    .select({
      knowledgePointId: questionsToKnowledgePoints.knowledgePointId,
      count: count(),
    })
    .from(questionsToKnowledgePoints)
    .where(inArray(questionsToKnowledgePoints.knowledgePointId, kpIds))
    .groupBy(questionsToKnowledgePoints.knowledgePointId)

  const questionCountMap = new Map<string, number>()
  for (const r of questionCountRows) {
    questionCountMap.set(r.knowledgePointId, Number(r.count))
  }

  // 3. 查询前置依赖（批量，仅查询属于当前教材知识点的依赖）
  // 双向过滤：knowledgePointId 和 prerequisiteKpId 都必须在当前教材的知识点集合内
  const prereqRows = await db
    .select({
      knowledgePointId: knowledgePointPrerequisites.knowledgePointId,
      prerequisiteKpId: knowledgePointPrerequisites.prerequisiteKpId,
    })
    .from(knowledgePointPrerequisites)
    .where(and(
      inArray(knowledgePointPrerequisites.knowledgePointId, kpIds),
      inArray(knowledgePointPrerequisites.prerequisiteKpId, kpIds),
    ))

  const prereqMap = new Map<string, string[]>()
  for (const r of prereqRows) {
    const arr = prereqMap.get(r.knowledgePointId) ?? []
    arr.push(r.prerequisiteKpId)
    prereqMap.set(r.knowledgePointId, arr)
  }

  // 4. 组装结果
  return kpRows.map((r) => ({
    id: r.id,
    name: r.name,
    description: r.description,
    parentId: r.parentId,
    chapterId: r.chapterId,
    level: r.level ?? 0,
    order: r.order ?? 0,
    chapterTitle: r.chapterTitle,
    questionCount: questionCountMap.get(r.id) ?? 0,
    prerequisiteIds: prereqMap.get(r.id) ?? [],
  }))
})

/**
 * 获取学生在某教材下所有知识点的掌握度。
 */
export const getStudentKpMastery = cache(async (
  studentId: string,
  textbookId: string,
): Promise<Map<string, MasteryInfo>> => {
  const rows = await db
    .select({
      knowledgePointId: knowledgePointMastery.knowledgePointId,
      masteryLevel: knowledgePointMastery.masteryLevel,
      totalQuestions: knowledgePointMastery.totalQuestions,
      correctQuestions: knowledgePointMastery.correctQuestions,
      lastAssessedAt: knowledgePointMastery.lastAssessedAt,
    })
    .from(knowledgePointMastery)
    .innerJoin(knowledgePoints, eq(knowledgePoints.id, knowledgePointMastery.knowledgePointId))
    .innerJoin(chapters, eq(chapters.id, knowledgePoints.chapterId))
    .where(and(
      eq(knowledgePointMastery.studentId, studentId),
      eq(chapters.textbookId, textbookId),
    ))

  const map = new Map<string, MasteryInfo>()
  for (const r of rows) {
    map.set(r.knowledgePointId, {
      masteryLevel: Number(r.masteryLevel),
      totalQuestions: r.totalQuestions,
      correctQuestions: r.correctQuestions,
      lastAssessedAt: r.lastAssessedAt,
    })
  }
  return map
})

/**
 * 获取班级（教师所带班级的所有学生）在某教材下知识点的平均掌握度。
 *
 * @param studentIds 班级学生 ID 列表
 * @param textbookId 教材 ID
 */
export const getClassKpMastery = cache(async (
  studentIds: string[],
  textbookId: string,
): Promise<Map<string, MasteryInfo>> => {
  if (studentIds.length === 0) return new Map()

  const rows = await db
    .select({
      knowledgePointId: knowledgePointMastery.knowledgePointId,
      avgMastery: sql<number>`AVG(${knowledgePointMastery.masteryLevel})`,
      totalQuestions: sql<number>`SUM(${knowledgePointMastery.totalQuestions})`,
      correctQuestions: sql<number>`SUM(${knowledgePointMastery.correctQuestions})`,
      lastAssessedAt: sql<Date>`MAX(${knowledgePointMastery.lastAssessedAt})`,
    })
    .from(knowledgePointMastery)
    .innerJoin(knowledgePoints, eq(knowledgePoints.id, knowledgePointMastery.knowledgePointId))
    .innerJoin(chapters, eq(chapters.id, knowledgePoints.chapterId))
    .where(and(
      inArray(knowledgePointMastery.studentId, studentIds),
      eq(chapters.textbookId, textbookId),
    ))
    .groupBy(knowledgePointMastery.knowledgePointId)

  const map = new Map<string, MasteryInfo>()
  for (const r of rows) {
    map.set(r.knowledgePointId, {
      masteryLevel: Number(r.avgMastery),
      totalQuestions: Number(r.totalQuestions),
      correctQuestions: Number(r.correctQuestions),
      lastAssessedAt: r.lastAssessedAt,
    })
  }
  return map
})

/**
 * 获取某个知识点的前置依赖列表（含知识点详情）。
 */
export const getPrerequisitesForKp = cache(async (
  kpId: string,
): Promise<{ id: string; name: string; description: string | null }[]> => {
  const rows = await db
    .select({
      id: knowledgePoints.id,
      name: knowledgePoints.name,
      description: knowledgePoints.description,
    })
    .from(knowledgePointPrerequisites)
    .innerJoin(knowledgePoints, eq(knowledgePoints.id, knowledgePointPrerequisites.prerequisiteKpId))
    .where(eq(knowledgePointPrerequisites.knowledgePointId, kpId))

  return rows
})

/**
 * 获取某个知识点的后置知识点列表（即哪些知识点以此 KP 为前置）。
 */
export const getSuccessorsForKp = cache(async (
  kpId: string,
): Promise<{ id: string; name: string; description: string | null }[]> => {
  const rows = await db
    .select({
      id: knowledgePoints.id,
      name: knowledgePoints.name,
      description: knowledgePoints.description,
    })
    .from(knowledgePointPrerequisites)
    .innerJoin(knowledgePoints, eq(knowledgePoints.id, knowledgePointPrerequisites.knowledgePointId))
    .where(eq(knowledgePointPrerequisites.prerequisiteKpId, kpId))

  return rows
})
