/**
 * SM-2 间隔重复算法（简化版）
 *
 * 基于 SuperMemo SM-2 算法的简化实现，适用于 K12 错题本场景。
 * 4 级评级：again（重来）/ hard（困难）/ good（良好）/ easy（简单）
 *
 * 设计原则：
 * - 纯函数，无副作用，易于测试
 * - 算法独立于数据访问层，可替换为其他算法（如 FSRS）
 * - 所有函数可单独测试
 */

import type { ErrorBookReviewResult } from "./schema"
import type { ErrorBookStatusValue } from "./types"

/** SM-2 评级 → 间隔天数 & 掌握度变化映射 */
export const REVIEW_INTERVALS: Record<
  ErrorBookReviewResult,
  { interval: number; masteryDelta: number; streakDelta: number }
> = {
  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 },
}

/** 间隔增长倍率 */
export const INTERVAL_MULTIPLIERS: Record<ErrorBookReviewResult, number> = {
  again: 1,
  hard: 1.2,
  good: 1.5,
  easy: 2,
}

/** 掌握度上限 */
export const MAX_MASTERY_LEVEL = 5

/** 掌握度下限 */
export const MIN_MASTERY_LEVEL = 0

/** 已掌握所需的连续答对次数 */
export const MASTERED_REQUIRED_STREAK = 3

/** 已掌握所需的掌握度等级 */
export const MASTERED_REQUIRED_MASTERY = 5

/**
 * 根据当前间隔和评级计算新间隔（指数增长）
 *
 * @param currentInterval 当前间隔天数
 * @param result 复习评级
 * @param reviewCount 已复习次数（首次复习使用基础间隔）
 * @returns 新的间隔天数
 */
export function calculateNewInterval(
  currentInterval: number,
  result: ErrorBookReviewResult,
  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))
}

/**
 * 计算新的掌握度（0-5）
 *
 * @param currentMastery 当前掌握度
 * @param result 复习评级
 * @param correctStreak 连续答对次数
 * @returns 新的掌握度
 */
export function calculateNewMastery(
  currentMastery: number,
  result: ErrorBookReviewResult,
  correctStreak: number
): number {
  const delta = REVIEW_INTERVALS[result].masteryDelta
  const newMastery = Math.max(MIN_MASTERY_LEVEL, Math.min(MAX_MASTERY_LEVEL, currentMastery + delta))
  // 连续 3 次答对则标记为已掌握
  if (correctStreak >= MASTERED_REQUIRED_STREAK) return Math.max(newMastery, MASTERED_REQUIRED_MASTERY)
  return newMastery
}

/**
 * 根据掌握度推断状态
 *
 * @param masteryLevel 掌握度等级
 * @param correctStreak 连续答对次数
 * @returns 错题状态
 */
export function deriveStatus(masteryLevel: number, correctStreak: number): ErrorBookStatusValue {
  if (masteryLevel >= MASTERED_REQUIRED_MASTERY || correctStreak >= MASTERED_REQUIRED_STREAK) return "mastered"
  if (masteryLevel >= 1) return "learning"
  return "new"
}

/**
 * 计算下次复习时间
 *
 * @param intervalDays 间隔天数
 * @param now 当前时间（可选，用于测试注入）
 * @returns 下次复习时间（当天早上 9 点）
 */
export function calculateNextReviewAt(intervalDays: number, now: Date = new Date()): Date {
  const date = new Date(now)
  date.setDate(date.getDate() + intervalDays)
  date.setHours(9, 0, 0, 0) // 早上 9 点复习
  return date
}

/**
 * 计算新的连续答对次数
 *
 * @param currentStreak 当前连续答对次数
 * @param result 复习评级
 * @returns 新的连续答对次数
 */
export function calculateNewCorrectStreak(
  currentStreak: number,
  result: ErrorBookReviewResult
): number {
  const streakDelta = REVIEW_INTERVALS[result].streakDelta
  if (streakDelta < 0) return 0 // again 重置
  return currentStreak + streakDelta
}

/**
 * SM-2 算法完整计算结果
 */
export interface Sm2CalculationResult {
  /** 新的间隔天数 */
  newInterval: number
  /** 新的掌握度 */
  newMasteryLevel: number
  /** 新的连续答对次数 */
  newCorrectStreak: number
  /** 推导出的新状态 */
  newStatus: ErrorBookStatusValue
  /** 下次复习时间 */
  nextReviewAt: Date
}

/**
 * 一次性计算所有 SM-2 相关字段
 *
 * @param params 当前错题状态参数
 * @param result 复习评级
 * @param now 当前时间（可选，用于测试注入）
 * @returns 完整计算结果
 */
export function calculateSm2Result(
  params: {
    currentInterval: number
    currentMastery: number
    currentStreak: number
    reviewCount: number
  },
  result: ErrorBookReviewResult,
  now: Date = new Date()
): Sm2CalculationResult {
  const newCorrectStreak = calculateNewCorrectStreak(params.currentStreak, result)
  const newInterval = calculateNewInterval(params.currentInterval, result, params.reviewCount)
  const newMasteryLevel = calculateNewMastery(params.currentMastery, result, newCorrectStreak)
  const newStatus = deriveStatus(newMasteryLevel, newCorrectStreak)
  const nextReviewAt = calculateNextReviewAt(newInterval, now)

  return {
    newInterval,
    newMasteryLevel,
    newCorrectStreak,
    newStatus,
    nextReviewAt,
  }
}
