/**
 * Diagnostic statistics pure functions.
 *
 * Extracted from data-access / data-access-reports to keep data-access layer
 * focused on DB I/O and make statistics logic independently testable.
 * All functions are pure (no side effects, no I/O).
 */

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

/** Round to 2 decimal places. */
const round2 = (n: number): number => Math.round(n * 100) / 100

/** Convert unknown to finite number (0 fallback). */
const toNumber = (v: unknown): number => {
  const n = typeof v === "number" ? v : Number(v)
  return Number.isFinite(n) ? n : 0
}

/** Strength threshold (mastery >= 80). */
export const STRENGTH_THRESHOLD = 80

/** Weakness threshold (mastery < 60). */
export const WEAKNESS_THRESHOLD = 60

/**
 * Raw mastery row from DB join (mastery + knowledge point name).
 * Numeric columns may be string | number depending on driver.
 */
export interface RawMasteryWithKpRow {
  mastery: {
    id: string
    studentId: string
    knowledgePointId: string
    masteryLevel: unknown
    totalQuestions: number
    correctQuestions: number
    lastAssessedAt: Date
    createdAt: Date
    updatedAt: Date
  }
  kpName: string | null
  kpDescription?: string | null
}

/**
 * Serialize a raw DB mastery row into the MasteryWithKnowledgePoint shape.
 */
export function serializeMasteryWithKp(r: RawMasteryWithKpRow): MasteryWithKnowledgePoint {
  return {
    id: r.mastery.id,
    studentId: r.mastery.studentId,
    knowledgePointId: r.mastery.knowledgePointId,
    masteryLevel: toNumber(r.mastery.masteryLevel),
    totalQuestions: r.mastery.totalQuestions,
    correctQuestions: r.mastery.correctQuestions,
    lastAssessedAt: r.mastery.lastAssessedAt.toISOString(),
    createdAt: r.mastery.createdAt.toISOString(),
    updatedAt: r.mastery.updatedAt.toISOString(),
    knowledgePointName: r.kpName ?? "Unknown",
    knowledgePointDescription: r.kpDescription ?? null,
  }
}

/**
 * Compute average mastery from a list of mastery levels.
 * Returns 0 for empty list.
 */
export function computeAverageMastery(levels: number[]): number {
  if (levels.length === 0) return 0
  return round2(levels.reduce((a, b) => a + b, 0) / levels.length)
}

/**
 * Classify mastery records into strengths (>=80) and weaknesses (<80).
 * P3-16 修复：弱项阈值从 <60 改为 <80，消除 60-79 盲区，确保所有知识点都被分类。
 * 注意：WEAKNESS_THRESHOLD (60) 仍用于班级级统计（notMasteredCount、需关注学生），
 * 此处仅调整学生摘要中的强弱项分类逻辑。
 */
export function classifyStrengthsWeaknesses(
  mastery: MasteryWithKnowledgePoint[],
): { strengths: MasteryWithKnowledgePoint[]; weaknesses: MasteryWithKnowledgePoint[] } {
  const strengths: MasteryWithKnowledgePoint[] = []
  const weaknesses: MasteryWithKnowledgePoint[] = []
  for (const m of mastery) {
    if (m.masteryLevel >= STRENGTH_THRESHOLD) {
      strengths.push(m)
    } else {
      weaknesses.push(m)
    }
  }
  return { strengths, weaknesses }
}

/**
 * Build StudentMasterySummary from raw mastery data and student name.
 */
export function buildStudentMasterySummary(
  studentId: string,
  studentName: string,
  allMastery: MasteryWithKnowledgePoint[],
): StudentMasterySummary {
  const averageMastery = computeAverageMastery(allMastery.map((m) => m.masteryLevel))
  const { strengths, weaknesses } = classifyStrengthsWeaknesses(allMastery)
  return {
    studentId,
    studentName,
    averageMastery,
    totalKnowledgePoints: allMastery.length,
    strengths,
    weaknesses,
    allMastery,
  }
}

/**
 * Raw row for class mastery aggregation (mastery record + kp name).
 */
export interface RawClassMasteryRow {
  mastery: {
    studentId: string
    knowledgePointId: string
    masteryLevel: unknown
  }
  kpName: string | null
}

/** Internal aggregation bucket per knowledge point. */
interface KpBucket {
  name: string
  levels: number[]
  mastered: number
  notMastered: number
}

/** Internal aggregation bucket per student. */
interface StudentBucket {
  levels: number[]
  weakCount: number
}

/**
 * Aggregate raw class mastery rows into per-kp and per-student buckets.
 */
export function aggregateClassMastery(
  rows: RawClassMasteryRow[],
  studentIds: string[],
): {
  byKp: Map<string, KpBucket>
  byStudent: Map<string, StudentBucket>
} {
  const byKp = new Map<string, KpBucket>()
  const byStudent = new Map<string, StudentBucket>()
  for (const s of studentIds) byStudent.set(s, { levels: [], weakCount: 0 })

  for (const r of rows) {
    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 >= STRENGTH_THRESHOLD) kpEntry.mastered += 1
    if (level < WEAKNESS_THRESHOLD) kpEntry.notMastered += 1
    byKp.set(kpId, kpEntry)

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

  return { byKp, byStudent }
}

/**
 * Compute KnowledgePointStat[] from aggregated kp buckets.
 * totalStudents is the count of students with mastery records (levels.length),
 * NOT the class total enrollment.
 */
export function computeKpStats(byKp: Map<string, KpBucket>): KnowledgePointStat[] {
  return Array.from(byKp.entries()).map(([kpId, e]) => ({
    knowledgePointId: kpId,
    knowledgePointName: e.name,
    averageMastery: computeAverageMastery(e.levels),
    masteredCount: e.mastered,
    notMasteredCount: e.notMastered,
    totalStudents: e.levels.length,
  }))
}

/**
 * Compute class average mastery by averaging each student's personal average
 * (rather than averaging all records, which would bias toward students with
 * more KP records).
 */
export function computeClassAverageMastery(
  students: Array<{ id: string; name: string | null }>,
  byStudent: Map<string, StudentBucket>,
): number {
  const studentAverages: number[] = []
  for (const s of students) {
    const e = byStudent.get(s.id)
    if (e && e.levels.length > 0) {
      studentAverages.push(computeAverageMastery(e.levels))
    }
  }
  return studentAverages.length > 0
    ? round2(studentAverages.reduce((a, b) => a + b, 0) / studentAverages.length)
    : 0
}

/**
 * Build list of students needing attention (average mastery < 60),
 * sorted ascending by average mastery.
 */
export function buildStudentsNeedingAttention(
  students: Array<{ id: string; name: string | null }>,
  byStudent: Map<string, StudentBucket>,
): ClassMasterySummary["studentsNeedingAttention"] {
  return students
    .map((s) => {
      const e = byStudent.get(s.id)
      if (!e) return null
      const avg = computeAverageMastery(e.levels)
      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 < WEAKNESS_THRESHOLD)
    .sort((a, b) => a.averageMastery - b.averageMastery)
}

/**
 * Build ClassMasterySummary from raw data.
 */
export function buildClassMasterySummary(
  classId: string,
  className: string,
  students: Array<{ id: string; name: string | null }>,
  masteryRows: RawClassMasteryRow[],
): ClassMasterySummary {
  const studentIds = students.map((s) => s.id)
  const { byKp, byStudent } = aggregateClassMastery(masteryRows, studentIds)
  const knowledgePointStats = computeKpStats(byKp)
  const averageMastery = computeClassAverageMastery(students, byStudent)
  const studentsNeedingAttention = buildStudentsNeedingAttention(students, byStudent)

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

/**
 * v4-P2-3: Build GradeMasterySummary from raw data.
 * Same aggregation logic as buildClassMasterySummary, but returns GradeMasterySummary type.
 */
export function buildGradeMasterySummary(
  gradeId: string,
  gradeName: string,
  students: Array<{ id: string; name: string | null }>,
  masteryRows: RawClassMasteryRow[],
): GradeMasterySummary {
  const studentIds = students.map((s) => s.id)
  const { byKp, byStudent } = aggregateClassMastery(masteryRows, studentIds)
  const knowledgePointStats = computeKpStats(byKp)
  const averageMastery = computeClassAverageMastery(students, byStudent)
  const studentsNeedingAttention = buildStudentsNeedingAttention(students, byStudent)

  return {
    gradeId,
    gradeName,
    studentCount: students.length,
    averageMastery,
    knowledgePointStats,
    studentsNeedingAttention,
  }
}

/**
 * Translation strings needed for report content generation.
 * Actions layer builds this from next-intl and passes it in,
 * keeping stats-service free of i18n framework dependencies.
 */
export interface ReportContentTranslations {
  studentSummary: (vars: { studentName: string; period: string; score: number; total: number; strengths: number; weaknesses: number }) => string
  studentRecommendation: (vars: { kpName: string; level: number }) => string
  studentNoWeakness: string
  classSummary: (vars: { className: string; period: string; score: number; students: number; attention: number }) => string
  classRecommendation: (vars: { kpName: string; level: number }) => string
  classNoWeakness: string
  /** v4-P2-3: 年级报告翻译 */
  gradeSummary: (vars: { gradeName: string; period: string; score: number; students: number; attention: number }) => string
  gradeRecommendation: (vars: { kpName: string; level: number }) => string
  gradeNoWeakness: string
}

/**
 * Build student report content (strengths/weaknesses/recommendations/summary)
 * from a StudentMasterySummary.
 */
export function buildStudentReportContent(
  summary: StudentMasterySummary,
  period: string,
  translations: ReportContentTranslations,
): {
  summaryText: string
  strengths: string[]
  weaknesses: string[]
  recommendations: string[]
  overallScore: number
} {
  const overallScore = summary.averageMastery
  const strengths = summary.strengths.map(
    (m) => `${m.knowledgePointName} (${m.masteryLevel.toFixed(1)}%)`,
  )
  const weaknesses = summary.weaknesses.map(
    (m) => `${m.knowledgePointName} (${m.masteryLevel.toFixed(1)}%)`,
  )
  const recommendations = summary.weaknesses.map(
    (m) => translations.studentRecommendation({
      kpName: m.knowledgePointName,
      level: m.masteryLevel,
    }),
  )
  if (recommendations.length === 0) {
    recommendations.push(translations.studentNoWeakness)
  }

  const summaryText = translations.studentSummary({
    studentName: summary.studentName,
    period,
    score: overallScore,
    total: summary.totalKnowledgePoints,
    strengths: strengths.length,
    weaknesses: weaknesses.length,
  })

  return { summaryText, strengths, weaknesses, recommendations, overallScore }
}

/**
 * Build class report content (strengths/weaknesses/recommendations/summary)
 * from a ClassMasterySummary. Strengths and weaknesses are limited to top 5.
 */
export function buildClassReportContent(
  summary: ClassMasterySummary,
  period: string,
  translations: ReportContentTranslations,
): {
  summaryText: string
  strengths: string[]
  weaknesses: string[]
  recommendations: string[]
  overallScore: number
} {
  const topWeak = summary.knowledgePointStats
    .filter((k) => k.averageMastery < WEAKNESS_THRESHOLD)
    .sort((a, b) => a.averageMastery - b.averageMastery)
    .slice(0, 5)
  const strengths = summary.knowledgePointStats
    .filter((k) => k.averageMastery >= STRENGTH_THRESHOLD)
    .sort((a, b) => b.averageMastery - a.averageMastery)
    .slice(0, 5)
    .map((k) => `${k.knowledgePointName} (均 ${k.averageMastery.toFixed(1)}%)`)
  const weaknesses = topWeak.map(
    (k) => `${k.knowledgePointName} (均 ${k.averageMastery.toFixed(1)}%)`,
  )
  const recommendations = topWeak.map(
    (k) => translations.classRecommendation({
      kpName: k.knowledgePointName,
      level: k.averageMastery,
    }),
  )
  if (recommendations.length === 0) {
    recommendations.push(translations.classNoWeakness)
  }

  const summaryText = translations.classSummary({
    className: summary.className,
    period,
    score: summary.averageMastery,
    students: summary.studentCount,
    attention: summary.studentsNeedingAttention.length,
  })

  return {
    summaryText,
    strengths,
    weaknesses,
    recommendations,
    overallScore: summary.averageMastery,
  }
}

/**
 * v4-P2-3: Build grade report content (strengths/weaknesses/recommendations/summary)
 * from a GradeMasterySummary. Strengths and weaknesses are limited to top 5.
 */
export function buildGradeReportContent(
  summary: GradeMasterySummary,
  period: string,
  translations: ReportContentTranslations,
): {
  summaryText: string
  strengths: string[]
  weaknesses: string[]
  recommendations: string[]
  overallScore: number
} {
  const topWeak = summary.knowledgePointStats
    .filter((k) => k.averageMastery < WEAKNESS_THRESHOLD)
    .sort((a, b) => a.averageMastery - b.averageMastery)
    .slice(0, 5)
  const strengths = summary.knowledgePointStats
    .filter((k) => k.averageMastery >= STRENGTH_THRESHOLD)
    .sort((a, b) => b.averageMastery - a.averageMastery)
    .slice(0, 5)
    .map((k) => `${k.knowledgePointName} (均 ${k.averageMastery.toFixed(1)}%)`)
  const weaknesses = topWeak.map(
    (k) => `${k.knowledgePointName} (均 ${k.averageMastery.toFixed(1)}%)`,
  )
  const recommendations = topWeak.map(
    (k) => translations.gradeRecommendation({
      kpName: k.knowledgePointName,
      level: k.averageMastery,
    }),
  )
  if (recommendations.length === 0) {
    recommendations.push(translations.gradeNoWeakness)
  }

  const summaryText = translations.gradeSummary({
    gradeName: summary.gradeName,
    period,
    score: summary.averageMastery,
    students: summary.studentCount,
    attention: summary.studentsNeedingAttention.length,
  })

  return {
    summaryText,
    strengths,
    weaknesses,
    recommendations,
    overallScore: summary.averageMastery,
  }
}

/**
 * Compute mastery level from correct/total questions.
 * Used by updateMasteryFromSubmission for cumulative mastery calculation.
 */
export function computeMasteryLevel(correct: number, total: number): number {
  return total > 0 ? round2((correct / total) * 100) : 0
}

/**
 * Serialize a raw mastery record (without kp join) into KnowledgePointMastery.
 */
export function serializeMastery(r: {
  id: string
  studentId: string
  knowledgePointId: string
  masteryLevel: unknown
  totalQuestions: number
  correctQuestions: number
  lastAssessedAt: Date
  createdAt: Date
  updatedAt: Date
}): KnowledgePointMastery {
  return {
    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(),
  }
}
