/**
 * L-9 考勤与成绩关联分析：纯函数实现。
 *
 * 与 IO 解耦，便于单测。包含：
 * - Pearson 相关系数计算
 * - 风险等级分类
 * - 相关系数解释
 * - 班级汇总聚合
 *
 * 参考 warning-compute.ts / trend-compute.ts 的纯函数模式。
 */

import type {
  AttendanceGradeCorrelationItem,
  AttendanceGradeCorrelationSummary,
  AttendanceGradeRiskLevel,
} from "./types"

/** 风险阈值：出勤率（百分比）。 */
export const RISK_ATTENDANCE_HIGH_THRESHOLD = 80
export const RISK_ATTENDANCE_MEDIUM_THRESHOLD = 90

/** 风险阈值：成绩（0-100 归一化分数）。 */
export const RISK_SCORE_HIGH_THRESHOLD = 60
export const RISK_SCORE_MEDIUM_THRESHOLD = 75

/** Pearson 相关系数解释阈值（绝对值）。 */
const CORRELATION_STRONG_THRESHOLD = 0.7
const CORRELATION_WEAK_THRESHOLD = 0.3

/** 相关系数解释类型（与 types.ts 中保持一致）。 */
export type CorrelationInterpretation =
  | "strong_negative"
  | "weak_negative"
  | "negligible"
  | "weak_positive"
  | "strong_positive"
  | "insufficient_data"

/**
 * 计算 Pearson 相关系数。
 *
 * @param x 自变量数组（如出勤率）
 * @param y 因变量数组（如平均成绩）
 * @returns r ∈ [-1, +1]；数据不足（<2 个点）或方差为 0 时返回 null
 */
export function computePearsonCorrelation(
  x: readonly number[],
  y: readonly number[]
): number | null {
  if (x.length !== y.length) return null
  if (x.length < 2) return null

  const n = x.length
  let sumX = 0
  let sumY = 0
  let sumXY = 0
  let sumX2 = 0
  let sumY2 = 0

  for (let i = 0; i < n; i++) {
    const xi = x[i]
    const yi = y[i]
    if (!Number.isFinite(xi) || !Number.isFinite(yi)) return null
    sumX += xi
    sumY += yi
    sumXY += xi * yi
    sumX2 += xi * xi
    sumY2 += yi * yi
  }

  const numerator = n * sumXY - sumX * sumY
  const denominator = Math.sqrt(
    (n * sumX2 - sumX * sumX) * (n * sumY2 - sumY * sumY)
  )

  if (denominator === 0) return null
  // 限制到 [-1, 1] 防止浮点误差溢出
  return Math.max(-1, Math.min(1, numerator / denominator))
}

/**
 * 根据出勤率和平均成绩判定风险等级。
 * - high：低出勤（<80%）且低分（<60%）
 * - medium：中低出勤（<90%）且中低分（<75%），但未达 high
 * - low：其余（正常）
 */
export function classifyRiskLevel(
  attendanceRate: number,
  averageScore: number
): AttendanceGradeRiskLevel {
  const isLowAttendance = attendanceRate < RISK_ATTENDANCE_HIGH_THRESHOLD
  const isMediumAttendance =
    attendanceRate < RISK_ATTENDANCE_MEDIUM_THRESHOLD && !isLowAttendance
  const isLowScore = averageScore < RISK_SCORE_HIGH_THRESHOLD
  const isMediumScore =
    averageScore < RISK_SCORE_MEDIUM_THRESHOLD && !isLowScore

  if (isLowAttendance && isLowScore) return "high"
  if ((isLowAttendance || isMediumAttendance) && (isLowScore || isMediumScore))
    return "medium"
  return "low"
}

/**
 * 解释 Pearson 相关系数。
 * - |r| >= 0.7：强相关
 * - 0.3 <= |r| < 0.7：弱相关
 * - |r| < 0.3：可忽略
 */
export function interpretCorrelation(
  r: number | null
): CorrelationInterpretation {
  if (r === null) return "insufficient_data"
  const abs = Math.abs(r)
  if (abs >= CORRELATION_STRONG_THRESHOLD) {
    return r > 0 ? "strong_positive" : "strong_negative"
  }
  if (abs >= CORRELATION_WEAK_THRESHOLD) {
    return r > 0 ? "weak_positive" : "weak_negative"
  }
  return "negligible"
}

/** 风险等级排序权重（用于降序排列：high > medium > low）。 */
const RISK_ORDER: Record<AttendanceGradeRiskLevel, number> = {
  high: 0,
  medium: 1,
  low: 2,
}

/**
 * 聚合班级考勤-成绩关联汇总（纯函数）。
 *
 * 输入：每个学生的原始数据（studentId, studentName, attendanceRate, averageScore, 计数）。
 * 输出：含 Pearson 相关系数、风险分级、排序后的 items。
 *
 * 排除无考勤或无成绩记录的学生（视为数据不全，不参与关联分析）。
 */
export function computeCorrelationSummary(
  classId: string,
  className: string,
  startDate: string,
  endDate: string,
  rawItems: ReadonlyArray<{
    studentId: string
    studentName: string
    attendanceRate: number
    attendanceRecordCount: number
    absentCount: number
    averageScore: number
    gradeRecordCount: number
  }>
): AttendanceGradeCorrelationSummary {
  // 过滤掉无考勤或无成绩记录的学生
  const validItems = rawItems.filter(
    (it) => it.attendanceRecordCount > 0 && it.gradeRecordCount > 0
  )

  const items: AttendanceGradeCorrelationItem[] = validItems.map((it) => ({
    studentId: it.studentId,
    studentName: it.studentName,
    attendanceRate: round2(it.attendanceRate),
    attendanceRecordCount: it.attendanceRecordCount,
    absentCount: it.absentCount,
    averageScore: round2(it.averageScore),
    gradeRecordCount: it.gradeRecordCount,
    riskLevel: classifyRiskLevel(it.attendanceRate, it.averageScore),
  }))

  // 排序：风险等级降序 → 出勤率升序（高风险学生排在最前）
  items.sort((a, b) => {
    const riskDiff = RISK_ORDER[a.riskLevel] - RISK_ORDER[b.riskLevel]
    if (riskDiff !== 0) return riskDiff
    return a.attendanceRate - b.attendanceRate
  })

  // Pearson 相关系数：x=出勤率，y=平均成绩
  const correlation = computePearsonCorrelation(
    items.map((it) => it.attendanceRate),
    items.map((it) => it.averageScore)
  )

  const riskCounts = {
    high: items.filter((it) => it.riskLevel === "high").length,
    medium: items.filter((it) => it.riskLevel === "medium").length,
    low: items.filter((it) => it.riskLevel === "low").length,
  }

  return {
    classId,
    className,
    startDate,
    endDate,
    items,
    correlation: correlation !== null ? round4(correlation) : null,
    correlationInterpretation: interpretCorrelation(correlation),
    riskCounts,
  }
}

/** 保留两位小数。 */
function round2(n: number): number {
  return Math.round(n * 100) / 100
}

/** 保留四位小数（相关系数精度）。 */
function round4(n: number): number {
  return Math.round(n * 10000) / 10000
}
