import "server-only"

import { generateTemplate, parseExcel } from "@/shared/lib/excel"
import { getClassActiveStudentsWithInfo } from "@/modules/classes/data-access"

import type {
  BatchCreateGradeRecordInput,
  ExcelImportRowInput,
} from "./schema"
import type { GradeRecordSemester, GradeRecordType } from "./types"

/**
 * P3-10: 成绩 Excel 导入相关类型。
 */
export interface GradeImportRow {
  /** 行号（从 2 开始，第 1 行为表头） */
  row: number
  /** 学生姓名（必填） */
  studentName: string
  /** 学生 ID（解析时为空，匹配班级学生后填充） */
  studentId?: string
  /** 得分（必填，0-1000） */
  score: number
  /** 备注（选填） */
  remark?: string
  /** 该行的错误列表 */
  errors: string[]
}

export interface GradeImportValidation {
  /** 校验通过的行：studentId 已填充 */
  valid: Array<{
    studentId: string
    studentName: string
    score: number
    remark?: string
  }>
  /** 校验失败的行（含错误原因） */
  invalid: GradeImportRow[]
  /** 文件中检测到的学生姓名与班级学生匹配情况 */
  unmatched: string[]
}

export interface GradeImportParams {
  classId: string
}

/**
 * P3-10: 生成成绩导入模板。
 *
 * 模板列：
 * - 学生姓名（必填，须与班级学生姓名一致）
 * - 得分（必填，0-1000）
 * - 备注（选填）
 *
 * 该模板可由教师下载后填写，再上传以批量录入成绩。
 */
export async function generateGradeImportTemplate(params: {
  classId: string
}): Promise<Buffer> {
  // 拉取班级学生姓名作为示例
  const students = await getClassActiveStudentsWithInfo(params.classId)
  const sampleRows = students.slice(0, 3).map((s, idx) => ({
    学生姓名: s.name,
    得分: String(80 + idx * 5),
    备注: "",
  }))

  return generateTemplate({
    sheets: [
      {
        name: "成绩录入",
        columns: [
          {
            header: "学生姓名",
            key: "studentName",
            width: 24,
            note: "必填，须与班级学生姓名一致",
          },
          {
            header: "得分",
            key: "score",
            width: 14,
            note: "必填，数值，范围 0-1000",
          },
          {
            header: "备注",
            key: "remark",
            width: 30,
            note: "选填",
          },
        ],
        sampleRows,
      },
    ],
  })
}

/**
 * P3-10: 解析 Excel 文件 Buffer 为成绩导入行。
 *
 * 该函数仅做格式解析与基本类型转换（score → number），
 * 不做学生姓名匹配（匹配需要班级信息，由 parseGradeImportData 完成）。
 */
export async function parseGradeExcelBuffer(
  buffer: Buffer
): Promise<GradeImportRow[]> {
  const sheets = await parseExcel(buffer)
  if (sheets.length === 0) {
    return []
  }

  const rows = sheets[0].rows
  const result: GradeImportRow[] = []

  for (let i = 0; i < rows.length; i++) {
    const row = rows[i]
    const rowNum = i + 2 // Excel 行号从 2 开始（第 1 行为表头）

    // 支持中英文表头
    const studentNameRaw = row["学生姓名"] ?? row["studentName"] ?? ""
    const scoreRaw = row["得分"] ?? row["score"] ?? ""
    const remarkRaw = row["备注"] ?? row["remark"] ?? ""

    const studentName = String(studentNameRaw).trim()
    const remark =
      remarkRaw === "" || remarkRaw == null ? undefined : String(remarkRaw).trim()

    // 解析 score
    let score: number | undefined
    if (scoreRaw !== "" && scoreRaw != null) {
      const num = Number(scoreRaw)
      if (Number.isFinite(num)) {
        score = num
      }
    }

    const errors: string[] = []
    if (!studentName) errors.push("学生姓名不能为空")
    if (score === undefined) errors.push("得分不能为空且必须为数值")
    else if (score < 0 || score > 1000) errors.push("得分必须在 0-1000 之间")

    result.push({
      row: rowNum,
      studentName,
      score: score ?? 0,
      remark,
      errors,
    })
  }

  return result
}

/**
 * P3-10: 校验成绩导入数据，匹配学生 ID。
 *
 * 步骤：
 * 1. 拉取班级所有学生信息
 * 2. 按姓名匹配学生 ID
 * 3. 校验行级数据完整性
 * 4. 返回 valid / invalid / unmatched
 */
export async function parseGradeImportData(
  rows: GradeImportRow[],
  params: GradeImportParams
): Promise<GradeImportValidation> {
  const students = await getClassActiveStudentsWithInfo(params.classId)

  // 按姓名构建索引（同一班级可能存在重名学生，需提醒）
  const nameToStudents = new Map<string, typeof students>()
  for (const s of students) {
    const existing = nameToStudents.get(s.name)
    if (existing) {
      existing.push(s)
    } else {
      nameToStudents.set(s.name, [s])
    }
  }

  const valid: GradeImportValidation["valid"] = []
  const invalid: GradeImportRow[] = []
  const unmatchedSet = new Set<string>()

  for (const row of rows) {
    const errors = [...row.errors]

    // 学生姓名匹配
    if (row.studentName) {
      const matched = nameToStudents.get(row.studentName)
      if (!matched || matched.length === 0) {
        errors.push(`未在班级中找到学生"${row.studentName}"`)
        unmatchedSet.add(row.studentName)
      } else if (matched.length > 1) {
        errors.push(
          `班级中存在 ${matched.length} 个名为"${row.studentName}"的学生，无法自动匹配`
        )
      } else {
        row.studentId = matched[0].id
      }
    }

    if (errors.length > 0 || !row.studentId) {
      invalid.push({ ...row, errors: errors.length > 0 ? errors : ["学生匹配失败"] })
    } else {
      valid.push({
        studentId: row.studentId,
        studentName: row.studentName,
        score: row.score,
        remark: row.remark,
      })
    }
  }

  return {
    valid,
    invalid,
    unmatched: Array.from(unmatchedSet),
  }
}

/**
 * P3-10: 将校验通过的导入行转换为 batchCreateGradeRecords 的输入格式。
 */
export function buildBatchInputFromImport(params: {
  classId: string
  subjectId: string
  title: string
  examId?: string
  academicYearId?: string
  fullScore?: number
  type?: GradeRecordType
  semester?: GradeRecordSemester
  rows: Array<{
    studentId: string
    score: number
    remark?: string
  }>
}): BatchCreateGradeRecordInput {
  return {
    classId: params.classId,
    subjectId: params.subjectId,
    title: params.title,
    examId: params.examId,
    academicYearId: params.academicYearId,
    fullScore: params.fullScore,
    type: params.type,
    semester: params.semester,
    records: params.rows.map((r) => ({
      studentId: r.studentId,
      score: r.score,
      remark: r.remark,
    })),
  }
}

export interface GradeImportResult {
  successCount: number
  failedCount: number
  invalidRows: GradeImportRow[]
  unmatchedStudents: string[]
  /** 创建的记录 ID 列表（用于撤销） */
  createdIds: string[]
}

/**
 * 兼容类型：从 ExcelImportRowInput 转换到 GradeImportRow（仅类型层使用）。
 */
export function fromSchemaRow(row: ExcelImportRowInput, rowNum: number): GradeImportRow {
  return {
    row: rowNum,
    studentName: row.studentName,
    score: row.score,
    remark: row.remark,
    errors: [],
  }
}
