import "server-only"

import { cache } from "react"
import { desc, eq, inArray } from "drizzle-orm"

import { db } from "@/shared/db"
import { knowledgePointMastery, knowledgePoints } from "@/shared/db/schema"

import { getClassNameById, getActiveStudentIdsByClassId, getClassExists } from "@/modules/classes/data-access"
import { getExamSubmissionWithAnswers } from "@/modules/exams/data-access"
import { getKnowledgePointsForQuestions } from "@/modules/questions/data-access"
import { getUserIdsByGradeId, getUserNamesByIds } from "@/modules/users/data-access"

import type {
  ClassMasterySummary,
  KnowledgePointMastery,
  KnowledgePointStat,
  MasteryWithKnowledgePoint,
  StudentMasterySummary,
} from "./types"

const toNumber = (v: unknown): number => {
  const n = typeof v === "number" ? v : Number(v)
  return Number.isFinite(n) ? n : 0
}

const round2 = (n: number): number => Math.round(n * 100) / 100

const serializeMastery = (r: typeof knowledgePointMastery.$inferSelect): KnowledgePointMastery => ({
  id: r.id,
  studentId: r.studentId,
  knowledgePointId: r.knowledgePointId,
  masteryLevel: toNumber(r.masteryLevel),
  totalQuestions: r.totalQuestions,
  correctQuestions: r.correctQuestions,
  lastAssessedAt: r.lastAssessedAt.toISOString(),
  createdAt: r.createdAt.toISOString(),
  updatedAt: r.updatedAt.toISOString(),
})

/** 获取学生在所有知识点的掌握度（含知识点名称） */
export const getStudentMastery = cache(async (studentId: string): Promise<MasteryWithKnowledgePoint[]> => {
  const rows = await db
    .select({
      mastery: knowledgePointMastery,
      kpName: knowledgePoints.name,
      kpDescription: knowledgePoints.description,
    })
    .from(knowledgePointMastery)
    .leftJoin(knowledgePoints, eq(knowledgePoints.id, knowledgePointMastery.knowledgePointId))
    .where(eq(knowledgePointMastery.studentId, studentId))
    .orderBy(desc(knowledgePointMastery.masteryLevel))

  return rows.map((r) => ({
    ...serializeMastery(r.mastery),
    knowledgePointName: r.kpName ?? "Unknown",
    knowledgePointDescription: r.kpDescription,
  }))
})

/** 获取学生掌握度摘要（含强项/弱项分析） */
export const getStudentMasterySummary = cache(async (studentId: string): Promise<StudentMasterySummary | null> => {
  const userMap = await getUserNamesByIds([studentId])
  const student = userMap.get(studentId)
  if (!student) return null

  const allMastery = await getStudentMastery(studentId)
  const averageMastery =
    allMastery.length > 0
      ? round2(allMastery.reduce((acc, m) => acc + m.masteryLevel, 0) / allMastery.length)
      : 0

  // Single-pass classification: strengths (>=80) and weaknesses (<60)
  const strengths: MasteryWithKnowledgePoint[] = []
  const weaknesses: MasteryWithKnowledgePoint[] = []
  for (const m of allMastery) {
    if (m.masteryLevel >= 80) strengths.push(m)
    if (m.masteryLevel < 60) weaknesses.push(m)
  }

  return {
    studentId,
    studentName: student.name ?? "Unknown",
    averageMastery,
    totalKnowledgePoints: allMastery.length,
    strengths,
    weaknesses,
    allMastery,
  }
})

/** 从提交答案更新掌握度（正确率作为掌握度） */
export async function updateMasteryFromSubmission(submissionId: string): Promise<void> {
  const submission = await getExamSubmissionWithAnswers(submissionId)
  if (!submission) return

  const answers = submission.answers
  if (answers.length === 0) return

  const questionIds = Array.from(new Set(answers.map((a) => a.questionId)))
  const kpMap = await getKnowledgePointsForQuestions(questionIds)

  // Build a Map for O(1) answer lookup instead of find() in loop
  const answerByQuestionId = new Map(answers.map((a) => [a.questionId, a]))

  const kpStats = new Map<string, { total: number; correct: number }>()
  for (const [questionId, kpLinks] of kpMap.entries()) {
    const answer = answerByQuestionId.get(questionId)
    if (!answer) continue
    for (const link of kpLinks) {
      const stat = kpStats.get(link.knowledgePointId) ?? { total: 0, correct: 0 }
      stat.total += 1
      if ((answer.score ?? 0) > 0) stat.correct += 1
      kpStats.set(link.knowledgePointId, stat)
    }
  }

  const now = new Date()
  await Promise.all(
    Array.from(kpStats.entries()).map(async ([kpId, stat]) => {
      const masteryLevel = stat.total > 0 ? round2((stat.correct / stat.total) * 100) : 0
      await db
        .insert(knowledgePointMastery)
        .values({
          studentId: submission.studentId,
          knowledgePointId: kpId,
          masteryLevel: String(masteryLevel),
          totalQuestions: stat.total,
          correctQuestions: stat.correct,
          lastAssessedAt: now,
        })
        .onDuplicateKeyUpdate({
          set: {
            masteryLevel: String(masteryLevel),
            totalQuestions: stat.total,
            correctQuestions: stat.correct,
            lastAssessedAt: now,
            updatedAt: now,
          },
        })
    }),
  )
}

/** 获取班级掌握度摘要 */
export const getClassMasterySummary = cache(async (classId: string): Promise<ClassMasterySummary | null> => {
  const classExists = await getClassExists(classId)
  if (!classExists) return null

  // 班级名称 与 学生列表 相互独立，并行拉取
  const [classNameResult, studentIds] = await Promise.all([
    getClassNameById(classId),
    getActiveStudentIdsByClassId(classId),
  ])
  const className = classNameResult ?? "Unknown"

  if (studentIds.length === 0) {
    return { classId, className, studentCount: 0, averageMastery: 0, knowledgePointStats: [], studentsNeedingAttention: [] }
  }

  // 学生姓名 与 掌握度记录 相互独立，并行拉取
  const [userMap, masteryRows] = await Promise.all([
    getUserNamesByIds(studentIds),
    db
      .select({ mastery: knowledgePointMastery, kpName: knowledgePoints.name })
      .from(knowledgePointMastery)
      .leftJoin(knowledgePoints, eq(knowledgePoints.id, knowledgePointMastery.knowledgePointId))
      .where(inArray(knowledgePointMastery.studentId, studentIds)),
  ])

  const students = studentIds
    .map((id) => ({ id, name: userMap.get(id)?.name ?? null }))
    .sort((a, b) => (a.name ?? "").localeCompare(b.name ?? ""))

  const byKp = new Map<string, { name: string; levels: number[]; mastered: number; notMastered: number }>()
  const byStudent = new Map<string, { levels: number[]; weakCount: number }>()
  for (const s of students) byStudent.set(s.id, { levels: [], weakCount: 0 })

  for (const r of masteryRows) {
    const level = toNumber(r.mastery.masteryLevel)
    const kpId = r.mastery.knowledgePointId
    const kpEntry = byKp.get(kpId) ?? { name: r.kpName ?? "Unknown", levels: [], mastered: 0, notMastered: 0 }
    kpEntry.levels.push(level)
    if (level >= 80) kpEntry.mastered += 1
    if (level < 60) kpEntry.notMastered += 1
    byKp.set(kpId, kpEntry)

    const stuEntry = byStudent.get(r.mastery.studentId)
    if (stuEntry) {
      stuEntry.levels.push(level)
      if (level < 60) stuEntry.weakCount += 1
    }
  }

  const knowledgePointStats: KnowledgePointStat[] = Array.from(byKp.entries()).map(([kpId, e]) => ({
    knowledgePointId: kpId,
    knowledgePointName: e.name,
    averageMastery: e.levels.length > 0 ? round2(e.levels.reduce((a, b) => a + b, 0) / e.levels.length) : 0,
    masteredCount: e.mastered,
    notMasteredCount: e.notMastered,
    totalStudents: students.length,
  }))

  const allLevels = masteryRows.map((r) => toNumber(r.mastery.masteryLevel))
  const averageMastery = allLevels.length > 0 ? round2(allLevels.reduce((a, b) => a + b, 0) / allLevels.length) : 0

  const studentsNeedingAttention = students
    .map((s) => {
      const e = byStudent.get(s.id)
      if (!e) return null
      const avg = e.levels.length > 0 ? round2(e.levels.reduce((a, b) => a + b, 0) / e.levels.length) : 0
      return { studentId: s.id, studentName: s.name ?? "Unknown", averageMastery: avg, weakCount: e.weakCount }
    })
    .filter((s): s is { studentId: string; studentName: string; averageMastery: number; weakCount: number } => s !== null)
    .filter((s) => s.averageMastery < 60)
    .sort((a, b) => a.averageMastery - b.averageMastery)

  return { classId, className, studentCount: students.length, averageMastery, knowledgePointStats, studentsNeedingAttention }
})

/** 获取知识点统计（按班级或年级聚合） */
export const getKnowledgePointStats = cache(async (classId?: string, gradeId?: string): Promise<KnowledgePointStat[]> => {
  let studentIds: string[] = []
  if (classId) {
    studentIds = await getActiveStudentIdsByClassId(classId)
  } else if (gradeId) {
    studentIds = await getUserIdsByGradeId(gradeId)
  }

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

  const masteryRows = await db
    .select({ mastery: knowledgePointMastery, kpName: knowledgePoints.name })
    .from(knowledgePointMastery)
    .leftJoin(knowledgePoints, eq(knowledgePoints.id, knowledgePointMastery.knowledgePointId))
    .where(inArray(knowledgePointMastery.studentId, studentIds))

  const byKp = new Map<string, { name: string; levels: number[]; mastered: number; notMastered: number }>()
  for (const r of masteryRows) {
    const level = toNumber(r.mastery.masteryLevel)
    const kpId = r.mastery.knowledgePointId
    const e = byKp.get(kpId) ?? { name: r.kpName ?? "Unknown", levels: [], mastered: 0, notMastered: 0 }
    e.levels.push(level)
    if (level >= 80) e.mastered += 1
    if (level < 60) e.notMastered += 1
    byKp.set(kpId, e)
  }

  return Array.from(byKp.entries()).map(([kpId, e]) => ({
    knowledgePointId: kpId,
    knowledgePointName: e.name,
    averageMastery: e.levels.length > 0 ? round2(e.levels.reduce((a, b) => a + b, 0) / e.levels.length) : 0,
    masteredCount: e.mastered,
    notMasteredCount: e.notMastered,
    totalStudents: studentIds.length,
  }))
})
