import { isRecord } from "@/shared/lib/type-guards"

/**
 * 题目内容解析纯函数
 *
 * 从 `unknown` 类型的题目内容中安全提取文本、选项、正确答案等。
 * 所有函数均为纯函数，无副作用，便于单测。
 */

export type QuestionOption = {
  id: string
  text: string
  isCorrect?: boolean
}

export const getQuestionText = (content: unknown): string => {
  if (!isRecord(content)) return ""
  return typeof content.text === "string" ? content.text : ""
}

/**
 * 从题目内容中提取纯文本（用于 AI 批改输入）。
 *
 * 优先使用 `text` 字段；若不存在则回退到 JSON 字符串，
 * 保证 AI 服务能拿到可读的题目描述。
 */
export const extractQuestionText = (content: unknown): string => {
  const text = getQuestionText(content)
  if (text.trim().length > 0) return text
  if (!isRecord(content)) return ""
  try {
    return JSON.stringify(content)
  } catch {
    return ""
  }
}

export const getOptions = (content: unknown): QuestionOption[] => {
  if (!isRecord(content)) return []
  const raw = content.options
  if (!Array.isArray(raw)) return []
  const out: QuestionOption[] = []
  for (const item of raw) {
    if (!isRecord(item)) continue
    const id = typeof item.id === "string" ? item.id : ""
    const text = typeof item.text === "string" ? item.text : ""
    if (!id || !text) continue
    const isCorrect = item.isCorrect === true
    out.push({ id, text, isCorrect })
  }
  return out
}

export const getChoiceCorrectIds = (content: unknown): string[] => {
  return getOptions(content)
    .filter((o): o is QuestionOption & { isCorrect: true } => o.isCorrect === true)
    .map((o) => o.id)
}

export const getJudgmentCorrectAnswer = (content: unknown): boolean | null => {
  if (!isRecord(content)) return null
  return typeof content.correctAnswer === "boolean" ? content.correctAnswer : null
}

export const getTextCorrectAnswers = (content: unknown): string[] => {
  if (!isRecord(content)) return []
  const raw = content.correctAnswer
  if (typeof raw === "string") return [raw]
  if (Array.isArray(raw)) return raw.filter((x): x is string => typeof x === "string")
  return []
}

export type QuestionType = "single_choice" | "multiple_choice" | "judgment" | "text" | string

export type AnswerShape =
  | { answer: string }
  | { answer: boolean }
  | { answer: string[] }
  | { answer: unknown }

export const toAnswerShape = (questionType: QuestionType, v: unknown): AnswerShape => {
  if (questionType === "text") return { answer: typeof v === "string" ? v : "" }
  if (questionType === "judgment") return { answer: typeof v === "boolean" ? v : false }
  if (questionType === "single_choice") return { answer: typeof v === "string" ? v : "" }
  if (questionType === "multiple_choice") {
    return {
      answer: Array.isArray(v) ? v.filter((x): x is string => typeof x === "string") : [],
    }
  }
  return { answer: v }
}

export const parseSavedAnswer = (saved: unknown, questionType: QuestionType): AnswerShape => {
  if (isRecord(saved) && "answer" in saved) {
    return toAnswerShape(questionType, saved.answer)
  }
  return toAnswerShape(questionType, saved)
}

export const extractAnswerValue = (studentAnswer: unknown): unknown => {
  if (isRecord(studentAnswer) && "answer" in studentAnswer) return studentAnswer.answer
  return studentAnswer
}

export const normalizeText = (v: string): string =>
  v.trim().replace(/\s+/g, " ").toLowerCase()

/**
 * 判断题目是否可自动判分（有标准答案）
 */
export const isAutoGradable = (input: {
  questionType: QuestionType
  questionContent: unknown
}): boolean => {
  if (input.questionType === "single_choice" || input.questionType === "multiple_choice") {
    return getChoiceCorrectIds(input.questionContent).length > 0
  }
  if (input.questionType === "judgment") {
    return getJudgmentCorrectAnswer(input.questionContent) !== null
  }
  if (input.questionType === "text") {
    return getTextCorrectAnswers(input.questionContent).length > 0
  }
  return false
}

export type CorrectnessState = "ungraded" | "correct" | "incorrect" | "partial"

/**
 * 计算单题对错状态
 * @returns "correct" | "incorrect" | "partial" | "ungraded"；无标准答案返回 null
 */
export const computeIsCorrect = (input: {
  questionType: QuestionType
  questionContent: unknown
  studentAnswer: unknown
}): boolean | null => {
  const studentVal = extractAnswerValue(input.studentAnswer)

  if (input.questionType === "single_choice") {
    const correct = getChoiceCorrectIds(input.questionContent)
    if (correct.length === 0) return null
    if (typeof studentVal !== "string") return false
    return correct.includes(studentVal)
  }

  if (input.questionType === "multiple_choice") {
    const correct = getChoiceCorrectIds(input.questionContent)
    if (correct.length === 0) return null
    const studentArr = Array.isArray(studentVal)
      ? studentVal.filter((x): x is string => typeof x === "string")
      : []
    const correctSet = new Set(correct)
    const studentSet = new Set(studentArr)
    if (studentSet.size !== correctSet.size) return false
    for (const id of correctSet) {
      if (!studentSet.has(id)) return false
    }
    return true
  }

  if (input.questionType === "judgment") {
    const correct = getJudgmentCorrectAnswer(input.questionContent)
    if (correct === null) return null
    if (typeof studentVal !== "boolean") return false
    return studentVal === correct
  }

  if (input.questionType === "text") {
    const correctAnswers = getTextCorrectAnswers(input.questionContent)
    if (correctAnswers.length === 0) return null
    if (typeof studentVal !== "string") return false
    const normalizedStudent = normalizeText(studentVal)
    return correctAnswers.some((c) => normalizeText(c) === normalizedStudent)
  }

  return null
}

/**
 * 计算多选题部分分比例（V3-6: 漏选得部分分）
 *
 * 评分策略：
 * - 全部正确选项都选中且无错误选项 → 1.0（满分）
 * - 部分正确选项被选中且无错误选项 → 正确选项数 / 总正确选项数
 * - 包含错误选项 → 0（鼓励不猜题）
 * - 无标准答案 → null（不自动判分）
 *
 * @example
 *   correctIds=[A,B,C], studentIds=[A,B] → 2/3 ≈ 0.667
 *   correctIds=[A,B,C], studentIds=[A,B,D] → 0（含错误选项 D）
 *   correctIds=[A,B,C], studentIds=[A,B,C] → 1.0
 */
export const computeMultipleChoicePartialRatio = (input: {
  questionContent: unknown
  studentAnswer: unknown
}): number | null => {
  const correctIds = getChoiceCorrectIds(input.questionContent)
  if (correctIds.length === 0) return null

  const studentVal = extractAnswerValue(input.studentAnswer)
  const studentArr = Array.isArray(studentVal)
    ? studentVal.filter((x): x is string => typeof x === "string")
    : []

  const correctSet = new Set(correctIds)
  const studentSet = new Set(studentArr)

  // 检查是否有错误选项（学生选了不在正确答案中的选项）
  for (const id of studentSet) {
    if (!correctSet.has(id)) return 0
  }

  // 无错误选项，按正确选项比例给分
  let correctSelected = 0
  for (const id of studentSet) {
    if (correctSet.has(id)) correctSelected += 1
  }

  return correctSelected / correctIds.length
}

/**
 * 根据分数与满分推断对错状态
 */
export const getCorrectnessState = (input: {
  score: number | null
  maxScore: number
}): CorrectnessState => {
  if (input.score === null) return "ungraded"
  if (input.score === input.maxScore) return "correct"
  if (input.score === 0) return "incorrect"
  return "partial"
}

/**
 * 自动判分输入项
 */
export interface AutoGradableAnswer {
  id: string
  questionId: string
  questionType: QuestionType
  questionContent: unknown
  maxScore: number
  studentAnswer: unknown
  score: number | null
  feedback: string | null
  order: number
}

/**
 * 对未判分的题目应用自动判分
 * - 已有分数（score !== null）的不覆盖
 * - 无标准答案的不判分
 * - 多选题支持部分分（漏选得部分分，错选得 0 分）
 * - 其他题型按 computeIsCorrect 给满分或 0 分
 *
 * V3-6: 多选题部分分策略
 *   使用 computeMultipleChoicePartialRatio 计算比例分数
 *   例如：maxScore=6, 正确选项[A,B,C], 学生选[A,B] → 6 * (2/3) = 4 分
 */
export const applyAutoGrades = <T extends AutoGradableAnswer>(incoming: T[]): T[] => {
  return incoming.map((a) => {
    if (a.score !== null) return a
    if (!isAutoGradable({ questionType: a.questionType, questionContent: a.questionContent })) {
      return a
    }

    // V3-6: 多选题使用部分分策略
    if (a.questionType === "multiple_choice") {
      const ratio = computeMultipleChoicePartialRatio({
        questionContent: a.questionContent,
        studentAnswer: a.studentAnswer,
      })
      if (ratio === null) return a
      // 按比例计算分数，四舍五入到整数（DB schema score 为整数）
      const scaledScore = Math.round(a.maxScore * ratio)
      return { ...a, score: scaledScore }
    }

    const isCorrect = computeIsCorrect({
      questionType: a.questionType,
      questionContent: a.questionContent,
      studentAnswer: a.studentAnswer,
    })
    if (isCorrect === null) return a
    return { ...a, score: isCorrect ? a.maxScore : 0 }
  })
}

/**
 * V3-2: 服务端即时自动批改
 *
 * 与 applyAutoGrades 类似，但用于学生提交时服务端回写。
 * 返回批改结果数组（含 score 和 feedback），以及是否全部可自动判分。
 *
 * @returns { answers: 批改后的答案数组, isFullyAutoGraded: 是否全部题目可自动判分 }
 */
export const autoGradeSubmission = <T extends AutoGradableAnswer>(
  incoming: T[]
): { answers: Array<T & { score: number }>; isFullyAutoGraded: boolean } => {
  const graded: Array<T & { score: number }> = []
  let hasUngradable = false

  for (const a of incoming) {
    if (!isAutoGradable({ questionType: a.questionType, questionContent: a.questionContent })) {
      // 主观题：保留原 score（可能为 null），标记为不可全自动判分
      hasUngradable = true
      graded.push({ ...a, score: a.score ?? 0 })
      continue
    }

    // 多选题使用部分分策略
    if (a.questionType === "multiple_choice") {
      const ratio = computeMultipleChoicePartialRatio({
        questionContent: a.questionContent,
        studentAnswer: a.studentAnswer,
      })
      if (ratio === null) {
        hasUngradable = true
        graded.push({ ...a, score: a.score ?? 0 })
        continue
      }
      const scaledScore = Math.round(a.maxScore * ratio)
      graded.push({ ...a, score: scaledScore })
      continue
    }

    const isCorrect = computeIsCorrect({
      questionType: a.questionType,
      questionContent: a.questionContent,
      studentAnswer: a.studentAnswer,
    })
    if (isCorrect === null) {
      hasUngradable = true
      graded.push({ ...a, score: a.score ?? 0 })
      continue
    }
    graded.push({ ...a, score: isCorrect ? a.maxScore : 0 })
  }

  return {
    answers: graded,
    isFullyAutoGraded: !hasUngradable,
  }
}

/**
 * 格式化学生答案为可读字符串
 */
export const formatStudentAnswer = (studentAnswer: unknown): string => {
  const v = extractAnswerValue(studentAnswer)
  if (typeof v === "string") return v
  if (typeof v === "boolean") return v ? "True" : "False"
  if (Array.isArray(v)) {
    return v.map((x) => (typeof x === "string" ? x : JSON.stringify(x))).join(", ")
  }
  if (v == null) return "—"
  return JSON.stringify(v)
}
