/**
 * 错题本种子数据脚本
 *
 * 从现有的考试/作业提交中自动采集错题，并模拟部分复习记录。
 * 直接操作数据库，不依赖 data-access.ts（避免 server-only 包冲突）。
 * 用法：npx tsx scripts/seed-error-book.ts
 */

import "dotenv/config"
import { db } from "../src/shared/db"
import {
  examSubmissions,
  homeworkSubmissions,
  submissionAnswers,
  homeworkAnswers,
  examQuestions,
  homeworkAssignmentQuestions,
  errorBookItems,
  errorBookReviews,
  questionsToKnowledgePoints,
  exams,
  homeworkAssignments,
} from "../src/shared/db/schema"
import { eq, and, inArray, sql } from "drizzle-orm"
import { createId } from "@paralleldrive/cuid2"

// SM-2 算法常量（与 sm2-algorithm.ts 保持一致）
const REVIEW_INTERVALS = {
  again: { interval: 1, masteryDelta: -1, streakDelta: -999 },
  hard: { interval: 2, masteryDelta: 0, streakDelta: 0 },
  good: { interval: 4, masteryDelta: 1, streakDelta: 1 },
  easy: { interval: 7, masteryDelta: 2, streakDelta: 1 },
} as const

const INTERVAL_MULTIPLIERS = {
  again: 1,
  hard: 1.2,
  good: 1.5,
  easy: 2,
} as const

type ReviewResult = keyof typeof REVIEW_INTERVALS

function calculateNewInterval(currentInterval: number, result: ReviewResult, reviewCount: number): number {
  const base = REVIEW_INTERVALS[result]
  if (result === "again") return 1
  if (reviewCount === 0) return base.interval
  const multiplier = INTERVAL_MULTIPLIERS[result]
  return Math.max(base.interval, Math.round(currentInterval * multiplier))
}

function calculateNewMastery(currentMastery: number, result: ReviewResult, correctStreak: number): number {
  const delta = REVIEW_INTERVALS[result].masteryDelta
  const newMastery = Math.max(0, Math.min(5, currentMastery + delta))
  if (correctStreak >= 3) return Math.max(newMastery, 5)
  return newMastery
}

function deriveStatus(masteryLevel: number, correctStreak: number): "new" | "learning" | "mastered" {
  if (masteryLevel >= 5 || correctStreak >= 3) return "mastered"
  if (masteryLevel >= 1) return "learning"
  return "new"
}

function calculateNewCorrectStreak(currentStreak: number, result: ReviewResult): number {
  const streakDelta = REVIEW_INTERVALS[result].streakDelta
  if (streakDelta < 0) return 0
  return currentStreak + streakDelta
}

async function collectFromExamSubmission(submissionId: string, studentId: string): Promise<number> {
  const submission = await db.query.examSubmissions.findFirst({
    where: and(eq(examSubmissions.id, submissionId), eq(examSubmissions.studentId, studentId)),
  })
  if (!submission) return 0

  // 查询考试以获取 subjectId
  const exam = await db.query.exams.findFirst({
    where: eq(exams.id, submission.examId),
  })
  const examSubjectId = exam?.subjectId ?? null

  const answers = await db
    .select({
      questionId: submissionAnswers.questionId,
      answerContent: submissionAnswers.answerContent,
      score: submissionAnswers.score,
      feedback: submissionAnswers.feedback,
    })
    .from(submissionAnswers)
    .where(eq(submissionAnswers.submissionId, submissionId))

  const questionIds = answers.map((a) => a.questionId)
  const examQuestionScores = await db
    .select({ questionId: examQuestions.questionId, maxScore: examQuestions.score })
    .from(examQuestions)
    .where(and(eq(examQuestions.examId, submission.examId), inArray(examQuestions.questionId, questionIds)))

  const maxScoreMap = new Map(examQuestionScores.map((q) => [q.questionId, q.maxScore ?? 0]))
  const wrongAnswers = answers.filter((a) => {
    const max = maxScoreMap.get(a.questionId) ?? 0
    return (a.score ?? 0) < max
  })

  if (wrongAnswers.length === 0) return 0

  // 去重
  const existing = await db
    .select({ questionId: errorBookItems.questionId })
    .from(errorBookItems)
    .where(
      and(
        eq(errorBookItems.studentId, studentId),
        inArray(errorBookItems.questionId, wrongAnswers.map((a) => a.questionId))
      )
    )
  const existingSet = new Set(existing.map((e) => e.questionId))

  // 查询知识点
  const kpRows = await db
    .select({
      questionId: questionsToKnowledgePoints.questionId,
      knowledgePointId: questionsToKnowledgePoints.knowledgePointId,
    })
    .from(questionsToKnowledgePoints)
    .where(inArray(questionsToKnowledgePoints.questionId, wrongAnswers.map((a) => a.questionId)))
  const kpMap = new Map<string, string[]>()
  for (const kp of kpRows) {
    const list = kpMap.get(kp.questionId) ?? []
    list.push(kp.knowledgePointId)
    kpMap.set(kp.questionId, list)
  }

  const now = new Date()
  const toInsert = wrongAnswers
    .filter((a) => !existingSet.has(a.questionId))
    .map((a) => ({
      id: createId(),
      studentId,
      questionId: a.questionId,
      sourceType: "exam" as const,
      sourceId: submissionId,
      studentAnswer: a.answerContent,
      correctAnswer: null,
      subjectId: examSubjectId, // 从考试中获取学科
      knowledgePointIds: kpMap.get(a.questionId) ?? null,
      status: "new" as const,
      masteryLevel: 0,
      nextReviewAt: now,
      reviewInterval: 1,
      reviewCount: 0,
      correctStreak: 0,
      note: a.feedback ?? null,
      errorTags: null,
    }))

  if (toInsert.length > 0) {
    await db.insert(errorBookItems).values(toInsert)
  }

  return toInsert.length
}

async function collectFromHomeworkSubmission(submissionId: string, studentId: string): Promise<number> {
  const submission = await db.query.homeworkSubmissions.findFirst({
    where: and(eq(homeworkSubmissions.id, submissionId), eq(homeworkSubmissions.studentId, studentId)),
  })
  if (!submission) return 0

  // 查询作业以获取 subjectId。
  // homeworkAssignments 表本身没有 subjectId 字段，
  // 若作业派生自试卷（sourceExamId 不为空），则从源试卷的 subjectId 获取。
  const assignment = await db.query.homeworkAssignments.findFirst({
    where: eq(homeworkAssignments.id, submission.assignmentId),
  })
  let hwSubjectId: string | null = null
  if (assignment?.sourceExamId) {
    const sourceExam = await db.query.exams.findFirst({
      where: eq(exams.id, assignment.sourceExamId),
    })
    hwSubjectId = sourceExam?.subjectId ?? null
  }

  const answers = await db
    .select({
      questionId: homeworkAnswers.questionId,
      answerContent: homeworkAnswers.answerContent,
      score: homeworkAnswers.score,
      feedback: homeworkAnswers.feedback,
    })
    .from(homeworkAnswers)
    .where(eq(homeworkAnswers.submissionId, submissionId))

  const questionIds = answers.map((a) => a.questionId)
  const hwQuestionScores = await db
    .select({ questionId: homeworkAssignmentQuestions.questionId, maxScore: homeworkAssignmentQuestions.score })
    .from(homeworkAssignmentQuestions)
    .where(
      and(
        eq(homeworkAssignmentQuestions.assignmentId, submission.assignmentId),
        inArray(homeworkAssignmentQuestions.questionId, questionIds)
      )
    )

  const maxScoreMap = new Map(hwQuestionScores.map((q) => [q.questionId, q.maxScore ?? 0]))
  const wrongAnswers = answers.filter((a) => {
    const max = maxScoreMap.get(a.questionId) ?? 0
    return (a.score ?? 0) < max
  })

  if (wrongAnswers.length === 0) return 0

  const existing = await db
    .select({ questionId: errorBookItems.questionId })
    .from(errorBookItems)
    .where(
      and(
        eq(errorBookItems.studentId, studentId),
        inArray(errorBookItems.questionId, wrongAnswers.map((a) => a.questionId))
      )
    )
  const existingSet = new Set(existing.map((e) => e.questionId))

  const kpRows = await db
    .select({
      questionId: questionsToKnowledgePoints.questionId,
      knowledgePointId: questionsToKnowledgePoints.knowledgePointId,
    })
    .from(questionsToKnowledgePoints)
    .where(inArray(questionsToKnowledgePoints.questionId, wrongAnswers.map((a) => a.questionId)))
  const kpMap = new Map<string, string[]>()
  for (const kp of kpRows) {
    const list = kpMap.get(kp.questionId) ?? []
    list.push(kp.knowledgePointId)
    kpMap.set(kp.questionId, list)
  }

  const now = new Date()
  const toInsert = wrongAnswers
    .filter((a) => !existingSet.has(a.questionId))
    .map((a) => ({
      id: createId(),
      studentId,
      questionId: a.questionId,
      sourceType: "homework" as const,
      sourceId: submissionId,
      studentAnswer: a.answerContent,
      correctAnswer: null,
      subjectId: hwSubjectId, // 从作业中获取学科
      knowledgePointIds: kpMap.get(a.questionId) ?? null,
      status: "new" as const,
      masteryLevel: 0,
      nextReviewAt: now,
      reviewInterval: 1,
      reviewCount: 0,
      correctStreak: 0,
      note: a.feedback ?? null,
      errorTags: null,
    }))

  if (toInsert.length > 0) {
    await db.insert(errorBookItems).values(toInsert)
  }

  return toInsert.length
}

async function recordReview(itemId: string, studentId: string, result: ReviewResult): 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 newStreak = calculateNewCorrectStreak(item.correctStreak, result)
  const newInterval = calculateNewInterval(item.reviewInterval, result, item.reviewCount)
  const newMastery = calculateNewMastery(item.masteryLevel, result, newStreak)
  const newStatus = deriveStatus(newMastery, newStreak)
  const nextReviewAt = newStatus === "mastered" ? null : new Date(Date.now() + newInterval * 86400_000)

  await db.insert(errorBookReviews).values({
    id: createId(),
    itemId,
    studentId,
    reviewResult: result,
    reviewedAt: new Date(),
    newInterval,
    newMasteryLevel: newMastery,
  })

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

async function seedErrorBook() {
  console.log("🌱 开始生成错题本种子数据...")

  // 1. 查询所有已批改的考试提交
  const gradedExamSubmissions = await db
    .select({
      id: examSubmissions.id,
      studentId: examSubmissions.studentId,
      examId: examSubmissions.examId,
    })
    .from(examSubmissions)
    .where(eq(examSubmissions.status, "graded"))

  console.log(`📋 找到 ${gradedExamSubmissions.length} 份已批改考试提交`)

  // 2. 从考试提交中自动采集错题
  let examCollected = 0
  for (const sub of gradedExamSubmissions) {
    try {
      const count = await collectFromExamSubmission(sub.id, sub.studentId)
      examCollected += count
    } catch (e) {
      console.warn(`⚠️ 考试提交 ${sub.id} 采集失败:`, (e as Error).message)
    }
  }
  console.log(`✓ 从考试提交中采集 ${examCollected} 条错题`)

  // 3. 查询所有已批改的作业提交
  const gradedHomeworkSubmissions = await db
    .select({
      id: homeworkSubmissions.id,
      studentId: homeworkSubmissions.studentId,
    })
    .from(homeworkSubmissions)
    .where(eq(homeworkSubmissions.status, "graded"))

  console.log(`📋 找到 ${gradedHomeworkSubmissions.length} 份已批改作业提交`)

  // 4. 从作业提交中自动采集错题
  let homeworkCollected = 0
  for (const sub of gradedHomeworkSubmissions) {
    try {
      const count = await collectFromHomeworkSubmission(sub.id, sub.studentId)
      homeworkCollected += count
    } catch (e) {
      console.warn(`⚠️ 作业提交 ${sub.id} 采集失败:`, (e as Error).message)
    }
  }
  console.log(`✓ 从作业提交中采集 ${homeworkCollected} 条错题`)

  // 5. 模拟复习记录：为每个学生的前 3 条错题添加复习
  const allErrorItems = await db
    .select({
      id: errorBookItems.id,
      studentId: errorBookItems.studentId,
    })
    .from(errorBookItems)
    .where(eq(errorBookItems.status, "new"))

  const byStudent = new Map<string, string[]>()
  for (const item of allErrorItems) {
    const list = byStudent.get(item.studentId) ?? []
    list.push(item.id)
    byStudent.set(item.studentId, list)
  }

  let reviewCount = 0
  const reviewResults: ReviewResult[] = ["again", "hard", "good"]
  for (const [studentId, itemIds] of byStudent) {
    // 为前 3 条错题添加复习记录
    for (let i = 0; i < Math.min(3, itemIds.length); i++) {
      const itemId = itemIds[i]
      const result = reviewResults[i % 3]
      try {
        await recordReview(itemId, studentId, result)
        reviewCount++
      } catch (e) {
        console.warn(`⚠️ 复习记录 ${itemId} 创建失败:`, (e as Error).message)
      }
    }
  }
  console.log(`✓ 创建 ${reviewCount} 条复习记录`)

  // 6. 统计结果
  const [totalItems] = await db
    .select({ count: sql<number>`count(*)` })
    .from(errorBookItems)
  const [totalReviews] = await db
    .select({ count: sql<number>`count(*)` })
    .from(errorBookReviews)
  const [statusStats] = await db
    .select({
      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)`,
    })
    .from(errorBookItems)

  console.log("\n📊 错题本数据统计:")
  console.log(`  总错题数: ${totalItems.count}`)
  console.log(`  总复习记录: ${totalReviews.count}`)
  console.log(`  状态分布: new=${statusStats.newCount}, learning=${statusStats.learningCount}, mastered=${statusStats.masteredCount}`)
  console.log("\n✅ 错题本种子数据生成完成")
  process.exit(0)
}

seedErrorBook().catch((e) => {
  console.error("❌ 错题本种子数据生成失败:", e)
  process.exit(1)
})
