import "server-only"

import { cache } from "react"
import { and, 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, getExamWithQuestionsForHomework } from "@/modules/exams/data-access"
import { getHomeworkSubmissionWithAnswersForMastery } from "@/modules/homework/data-access-error-collection"
import { getKnowledgePointsForQuestions } from "@/modules/questions/data-access"
import { getUserIdsByGradeId, getUserNamesByIds } from "@/modules/users/data-access"
import { getGradeNameById } from "@/modules/school/data-access"

import {
  aggregateClassMastery,
  buildClassMasterySummary,
  buildGradeMasterySummary,
  buildStudentMasterySummary,
  computeKpStats,
  computeMasteryLevel,
  serializeMasteryWithKp,
  type RawClassMasteryRow,
  type RawMasteryWithKpRow,
} from "./stats-service"
import type {
  ClassMasterySummary,
  GradeMasterySummary,
  KnowledgePointStat,
  MasteryWithKnowledgePoint,
  StudentMasterySummary,
} from "./types"

/** 获取学生在所有知识点的掌握度（含知识点名称） */
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) =>
    serializeMasteryWithKp({
      mastery: r.mastery,
      kpName: r.kpName,
      kpDescription: r.kpDescription,
    } satisfies RawMasteryWithKpRow),
  )
})

/** 获取学生掌握度摘要（含强项/弱项分析） */
export const getStudentMasterySummary = cache(async (studentId: string): Promise<StudentMasterySummary | null> => {
  // P3-18 修复：用户名查询与掌握度查询相互独立，并行执行
  const [userMap, allMastery] = await Promise.all([
    getUserNamesByIds([studentId]),
    getStudentMastery(studentId),
  ])
  const student = userMap.get(studentId)
  if (!student) return null

  return buildStudentMasterySummary(studentId, student.name ?? "Unknown", 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 existingRows = await db
    .select()
    .from(knowledgePointMastery)
    .where(
      and(
        eq(knowledgePointMastery.studentId, submission.studentId),
        inArray(knowledgePointMastery.knowledgePointId, Array.from(kpStats.keys())),
      ),
    )

  const existingByKp = new Map<string, { total: number; correct: number }>()
  for (const row of existingRows) {
    existingByKp.set(row.knowledgePointId, {
      total: row.totalQuestions,
      correct: row.correctQuestions,
    })
  }

  const now = new Date()
  // 使用事务保证多个 upsert 的原子性
  await db.transaction(async (tx) => {
    await Promise.all(
      Array.from(kpStats.entries()).map(async ([kpId, stat]) => {
        const existing = existingByKp.get(kpId)
        const totalQuestions = (existing?.total ?? 0) + stat.total
        const correctQuestions = (existing?.correct ?? 0) + stat.correct
        const masteryLevel = computeMasteryLevel(correctQuestions, totalQuestions)
        await tx
          .insert(knowledgePointMastery)
          .values({
            studentId: submission.studentId,
            knowledgePointId: kpId,
            masteryLevel: String(masteryLevel),
            totalQuestions,
            correctQuestions,
            lastAssessedAt: now,
          })
          .onDuplicateKeyUpdate({
            set: {
              masteryLevel: String(masteryLevel),
              totalQuestions,
              correctQuestions,
              lastAssessedAt: now,
              updatedAt: now,
            },
          })
      }),
    )
  })
}

/**
 * 从作业提交更新掌握度（累积模式）。
 *
 * 与 updateMasteryFromSubmission 类似，但数据来源是作业提交而非考试提交。
 * 通过 homework 模块的跨模块接口获取作业提交的答案数据，避免直查 homeworkSubmissions 表。
 *
 * @param submissionId 作业提交 ID
 */
export async function updateMasteryFromHomeworkSubmission(submissionId: string): Promise<void> {
  const submission = await getHomeworkSubmissionWithAnswersForMastery(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 existingRows = await db
    .select()
    .from(knowledgePointMastery)
    .where(
      and(
        eq(knowledgePointMastery.studentId, submission.studentId),
        inArray(knowledgePointMastery.knowledgePointId, Array.from(kpStats.keys())),
      ),
    )

  const existingByKp = new Map<string, { total: number; correct: number }>()
  for (const row of existingRows) {
    existingByKp.set(row.knowledgePointId, {
      total: row.totalQuestions,
      correct: row.correctQuestions,
    })
  }

  const now = new Date()
  // 使用事务保证多个 upsert 的原子性
  await db.transaction(async (tx) => {
    await Promise.all(
      Array.from(kpStats.entries()).map(async ([kpId, stat]) => {
        const existing = existingByKp.get(kpId)
        const totalQuestions = (existing?.total ?? 0) + stat.total
        const correctQuestions = (existing?.correct ?? 0) + stat.correct
        const masteryLevel = computeMasteryLevel(correctQuestions, totalQuestions)
        await tx
          .insert(knowledgePointMastery)
          .values({
            studentId: submission.studentId,
            knowledgePointId: kpId,
            masteryLevel: String(masteryLevel),
            totalQuestions,
            correctQuestions,
            lastAssessedAt: now,
          })
          .onDuplicateKeyUpdate({
            set: {
              masteryLevel: String(masteryLevel),
              totalQuestions,
              correctQuestions,
              lastAssessedAt: now,
              updatedAt: now,
            },
          })
      }),
    )
  })
}

/**
 * v3-P1-5：从手动录入的成绩更新掌握度。
 *
 * 当教师手动录入关联了 examId 的成绩时，根据得分率（score/fullScore）
 * 更新该考试涉及的所有知识点的掌握度。采用累积模式，与 updateMasteryFromSubmission 一致。
 *
 * 注意：此函数假设学生在所有知识点上的掌握度等于整体得分率，
 * 这是一种近似（无法区分知识点级别的强弱），适用于无题目级别答案的场景。
 */
export async function updateMasteryFromExamScore(
  studentId: string,
  examId: string,
  score: number,
  fullScore: number,
): Promise<void> {
  if (fullScore <= 0) return

  // 获取考试的所有题目
  const examWithQuestions = await getExamWithQuestionsForHomework(examId)
  if (!examWithQuestions || examWithQuestions.questions.length === 0) return

  // 获取题目关联的知识点
  const questionIds = examWithQuestions.questions.map((q) => q.questionId)
  const kpMap = await getKnowledgePointsForQuestions(questionIds)

  // 收集所有涉及的知识点 ID
  const kpIds = new Set<string>()
  for (const links of kpMap.values()) {
    for (const link of links) {
      kpIds.add(link.knowledgePointId)
    }
  }

  if (kpIds.size === 0) return

  // 计算得分率作为掌握度
  const masteryLevel = computeMasteryLevel(score, fullScore)

  // 读取已有掌握度记录，累积计算
  const existingRows = await db
    .select()
    .from(knowledgePointMastery)
    .where(
      and(
        eq(knowledgePointMastery.studentId, studentId),
        inArray(knowledgePointMastery.knowledgePointId, Array.from(kpIds)),
      ),
    )

  const existingByKp = new Map<string, { total: number; correct: number }>()
  for (const row of existingRows) {
    existingByKp.set(row.knowledgePointId, {
      total: row.totalQuestions,
      correct: row.correctQuestions,
    })
  }

  const now = new Date()
  // 使用事务保证多个 upsert 的原子性
  await db.transaction(async (tx) => {
    await Promise.all(
      Array.from(kpIds).map(async (kpId) => {
        const existing = existingByKp.get(kpId)
        // 累积：将本次成绩视为 1 道题，得分率作为掌握度
        const totalQuestions = (existing?.total ?? 0) + 1
        const correctQuestions = (existing?.correct ?? 0) + Math.round(masteryLevel / 100)
        const newMasteryLevel = computeMasteryLevel(correctQuestions, totalQuestions)
        await tx
          .insert(knowledgePointMastery)
          .values({
            studentId,
            knowledgePointId: kpId,
            masteryLevel: String(newMasteryLevel),
            totalQuestions,
            correctQuestions,
            lastAssessedAt: now,
          })
          .onDuplicateKeyUpdate({
            set: {
              masteryLevel: String(newMasteryLevel),
              totalQuestions,
              correctQuestions,
              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 rawRows: RawClassMasteryRow[] = masteryRows.map((r) => ({
    mastery: {
      studentId: r.mastery.studentId,
      knowledgePointId: r.mastery.knowledgePointId,
      masteryLevel: r.mastery.masteryLevel,
    },
    kpName: r.kpName,
  }))

  return buildClassMasterySummary(classId, className, students, rawRows)
})

/** v4-P2-3: 获取年级掌握度摘要 */
export const getGradeMasterySummary = cache(async (gradeId: string): Promise<GradeMasterySummary | null> => {
  // 年级名称 与 学生列表 相互独立，并行拉取
  const [gradeNameResult, studentIds] = await Promise.all([
    getGradeNameById(gradeId),
    getUserIdsByGradeId(gradeId),
  ])
  const gradeName = gradeNameResult ?? "Unknown"

  if (studentIds.length === 0) {
    return { gradeId, gradeName, 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 rawRows: RawClassMasteryRow[] = masteryRows.map((r) => ({
    mastery: {
      studentId: r.mastery.studentId,
      knowledgePointId: r.mastery.knowledgePointId,
      masteryLevel: r.mastery.masteryLevel,
    },
    kpName: r.kpName,
  }))

  return buildGradeMasterySummary(gradeId, gradeName, students, rawRows)
})

/** 获取知识点统计（按班级或年级聚合） */
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 rawRows: RawClassMasteryRow[] = masteryRows.map((r) => ({
    mastery: {
      studentId: r.mastery.studentId,
      knowledgePointId: r.mastery.knowledgePointId,
      masteryLevel: r.mastery.masteryLevel,
    },
    kpName: r.kpName,
  }))

  const { byKp } = aggregateClassMastery(rawRows, studentIds)
  return computeKpStats(byKp)
})

/**
 * v3-P2-5: 获取班级学生在指定知识点上的掌握度列表。
 *
 * 用于"按知识点筛选学生"功能：教师选择某个知识点后，列出班级所有学生
 * 在该知识点上的掌握度，便于针对性辅导。掌握度低于阈值（默认 60）的学生
 * 排在前面并标记为"需关注"。
 */
export const getClassStudentsByKnowledgePoint = cache(
  async (
    classId: string,
    knowledgePointId: string,
    options?: { threshold?: number }
  ): Promise<
    Array<{
      studentId: string
      studentName: string
      masteryLevel: number
      totalQuestions: number
      correctQuestions: number
      lastAssessedAt: string | null
      needsAttention: boolean
    }>
  > => {
    const threshold = options?.threshold ?? 60
    const studentIds = await getActiveStudentIdsByClassId(classId)
    if (studentIds.length === 0) return []

    const [userMap, masteryRows] = await Promise.all([
      getUserNamesByIds(studentIds),
      db
        .select({
          mastery: knowledgePointMastery,
        })
        .from(knowledgePointMastery)
        .where(
          and(
            eq(knowledgePointMastery.knowledgePointId, knowledgePointId),
            inArray(knowledgePointMastery.studentId, studentIds),
          ),
        ),
    ])

    const masteryByStudent = new Map<string, {
      masteryLevel: number
      totalQuestions: number
      correctQuestions: number
      lastAssessedAt: Date | null
    }>()
    for (const row of masteryRows) {
      masteryByStudent.set(row.mastery.studentId, {
        masteryLevel: Number(row.mastery.masteryLevel) || 0,
        totalQuestions: row.mastery.totalQuestions,
        correctQuestions: row.mastery.correctQuestions,
        lastAssessedAt: row.mastery.lastAssessedAt,
      })
    }

    const result = studentIds.map((id) => {
      const info = userMap.get(id)
      const mastery = masteryByStudent.get(id)
      const masteryLevel = mastery?.masteryLevel ?? 0
      return {
        studentId: id,
        studentName: info?.name ?? "Unknown",
        masteryLevel,
        totalQuestions: mastery?.totalQuestions ?? 0,
        correctQuestions: mastery?.correctQuestions ?? 0,
        lastAssessedAt: mastery?.lastAssessedAt
          ? mastery.lastAssessedAt.toISOString()
          : null,
        needsAttention: masteryLevel < threshold,
      }
    })

    // 需关注的学生排前面，相同关注状态按掌握度升序
    result.sort((a, b) => {
      if (a.needsAttention !== b.needsAttention) {
        return a.needsAttention ? -1 : 1
      }
      return a.masteryLevel - b.masteryLevel
    })

    return result
  }
)
