"use server"

import { revalidatePath } from "next/cache"
import { createId } from "@paralleldrive/cuid2"
import { getTranslations } from "next-intl/server"

import { requirePermission } from "@/shared/lib/auth-guard"
import { Permissions } from "@/shared/types/permissions"
import type { ActionState } from "@/shared/types/action-state"
import { handleActionError, safeJsonParse, safeParseDate } from "@/shared/lib/action-utils"
import { trackExamEvent } from "@/shared/lib/track-event"
import { collectFromHomeworkSubmission } from "@/modules/error-book/data-access-collection"
import { updateMasteryFromHomeworkSubmission } from "@/modules/diagnostic/data-access"

import { CreateHomeworkAssignmentSchema, GradeHomeworkSchema } from "./schema"
import {
  createHomeworkAssignment,
  getActiveClassStudentIdsForHomework,
  getClassTeacherById,
  getExamWithQuestionsForHomework,
  getHomeworkSubmissionForGrading,
  getHomeworkSubmissionForPermission,
  getTeacherAssignedSubjectIds,
  gradeHomeworkAnswers,
  markHomeworkSubmitted,
  saveHomeworkAnswer,
  startHomeworkSubmission,
  batchAutoGradeSubmissions,
} from "./data-access-write"
import { getScansBySubmissionId } from "./data-access-scans"
import { getExcellentSubmissions, getUnsubmittedStudents, getHomeworkAssignmentById } from "./data-access"
import type { ExcellentSubmissionItem, ScanAttachment } from "./types"

const parseStudentIds = (raw: string): string[] => {
  return raw
    .split(/[,\n\r\t ]+/g)
    .map((s) => s.trim())
    .filter((s) => s.length > 0)
}

/**
 * 将 Zod 校验返回的 i18n 键翻译为当前 locale 的字符串。
 *
 * schema.ts 中所有错误消息均使用 `homework.form.error.*` 命名空间的 i18n 键，
 * 此函数在 Server Action 校验失败时调用，将 `fieldErrors` 中的键翻译为
 * 实际字符串，并返回首条错误作为 toast 显示的 `message`。
 */
async function translateZodErrors(
  fieldErrors: Record<string, string[] | undefined>
): Promise<{ errors: Record<string, string[]>; firstMessage: string }> {
  const t = await getTranslations("examHomework")
  const translated: Record<string, string[]> = {}
  let firstMessage = t("homework.form.error.invalidForm")

  for (const [field, messages] of Object.entries(fieldErrors)) {
    if (!messages || messages.length === 0) continue
    const translatedMessages = messages.map((msg) => {
      // 仅翻译形如 "homework.form.error.xxx" 的 i18n 键，其他原样返回
      if (msg.startsWith("homework.form.error.")) {
        return t(msg)
      }
      return msg
    })
    translated[field] = translatedMessages
    if (firstMessage === t("homework.form.error.invalidForm")) {
      firstMessage = translatedMessages[0]
    }
  }

  return { errors: translated, firstMessage }
}

/**
 * 批改后处理：自动采集错题 + 更新知识点掌握度。
 *
 * 使用 Promise.allSettled 并行执行，确保一个失败不影响另一个。
 * 错误被记录但不抛出，不影响批改操作的成功返回。
 */
async function runPostGradingHooks(submissionId: string, studentId: string): Promise<void> {
  const [errorBookResult, masteryResult] = await Promise.allSettled([
    collectFromHomeworkSubmission(submissionId, studentId),
    updateMasteryFromHomeworkSubmission(submissionId),
  ])

  if (errorBookResult.status === "rejected") {
    console.error(`[post-grading] 错题采集失败 submission=${submissionId}:`, errorBookResult.reason)
  }
  if (masteryResult.status === "rejected") {
    console.error(`[post-grading] 掌握度更新失败 submission=${submissionId}:`, masteryResult.reason)
  }
}

export async function createHomeworkAssignmentAction(
  prevState: ActionState<string> | null,
  formData: FormData
): Promise<ActionState<string>> {
  try {
    const ctx = await requirePermission(Permissions.HOMEWORK_CREATE)

    const targetStudentIdsJson = formData.get("targetStudentIdsJson")
    const targetStudentIdsText = formData.get("targetStudentIdsText")

    const parsed = CreateHomeworkAssignmentSchema.safeParse({
      sourceExamId: formData.get("sourceExamId"),
      classId: formData.get("classId"),
      title: formData.get("title") || undefined,
      description: formData.get("description") || undefined,
      availableAt: formData.get("availableAt") || undefined,
      dueAt: formData.get("dueAt") || undefined,
      allowLate: formData.get("allowLate") || undefined,
      lateDueAt: formData.get("lateDueAt") || undefined,
      maxAttempts: formData.get("maxAttempts") || undefined,
      publish: formData.get("publish") || undefined,
      targetStudentIds:
        typeof targetStudentIdsJson === "string" && targetStudentIdsJson.length > 0
          ? (JSON.parse(targetStudentIdsJson) as unknown)
          : typeof targetStudentIdsText === "string" && targetStudentIdsText.trim().length > 0
            ? parseStudentIds(targetStudentIdsText)
            : undefined,
    })

    if (!parsed.success) {
      const { errors, firstMessage } = await translateZodErrors(
        parsed.error.flatten().fieldErrors
      )
      return {
        success: false,
        message: firstMessage,
        errors,
      }
    }

    const input = parsed.data
    const publish = input.publish ?? true

    const classRow = await getClassTeacherById(input.classId)
    if (!classRow) return { success: false, message: "Class not found" }

    // 快速作业模式：无 sourceExamId 时创建纯文本作业（无题目）
    const isQuickAssignment = !input.sourceExamId

    let exam: Awaited<ReturnType<typeof getExamWithQuestionsForHomework>> = null
    if (!isQuickAssignment) {
      const sourceExamId = input.sourceExamId
      if (!sourceExamId) {
        return { success: false, message: "sourceExamId is required for exam mode" }
      }
      const examData = await getExamWithQuestionsForHomework(sourceExamId)
      if (!examData) return { success: false, message: "Exam not found" }
      exam = examData
    }

    if (ctx.dataScope.type !== "all" && classRow.teacherId !== ctx.userId) {
      const assignedSubjectIds = await getTeacherAssignedSubjectIds(input.classId, ctx.userId)
      if (assignedSubjectIds.length === 0) {
        return { success: false, message: "Not assigned to this class" }
      }
      if (!isQuickAssignment && exam) {
        const assignedSubjectSet = new Set(assignedSubjectIds)
        if (!exam.subjectId) {
          return { success: false, message: "Exam subject not set" }
        }
        if (!assignedSubjectSet.has(exam.subjectId)) {
          return { success: false, message: "Not assigned to this subject" }
        }
      }
    }

    const classStudentIds = await getActiveClassStudentIdsForHomework(
      input.classId,
      ctx.dataScope,
      ctx.userId,
      classRow.teacherId
    )
    const classStudentIdSet = new Set(classStudentIds)

    const targetStudentIds =
      input.targetStudentIds && input.targetStudentIds.length > 0
        ? input.targetStudentIds.filter((id) => classStudentIdSet.has(id))
        : classStudentIds

    if (publish && targetStudentIds.length === 0) {
      return { success: false, message: "No active students in this class" }
    }

    const assignmentId = createId()
    const availableAt = input.availableAt ? safeParseDate(input.availableAt, "可用时间") : null
    const dueAt = input.dueAt ? safeParseDate(input.dueAt, "截止时间") : null
    const lateDueAt = input.lateDueAt ? safeParseDate(input.lateDueAt, "迟交截止时间") : null

    await createHomeworkAssignment({
      assignmentId,
      sourceExamId: input.sourceExamId ?? null,
      title: input.title?.trim().length ? input.title.trim() : (exam?.title ?? "Untitled Assignment"),
      description: input.description ?? null,
      structure: exam?.structure ?? [],
      status: publish ? "published" : "draft",
      creatorId: ctx.userId,
      availableAt,
      dueAt,
      allowLate: input.allowLate ?? false,
      lateDueAt,
      maxAttempts: input.maxAttempts ?? 1,
      publish,
      questions: exam?.questions ?? [],
      targetStudentIds,
    })

    revalidatePath("/teacher/homework/assignments")
    revalidatePath("/teacher/homework/submissions")

    // V3-4: 埋点监控
    await trackExamEvent("homework.created", {
      userId: ctx.userId,
      targetId: assignmentId,
      properties: {
        sourceExamId: input.sourceExamId ?? null,
        classId: input.classId,
        targetStudentCount: targetStudentIds.length,
        published: publish,
      },
    })

    return { success: true, message: "Assignment created", data: assignmentId }
  } catch (e) {
    return handleActionError(e)
  }
}

export async function startHomeworkSubmissionAction(
  prevState: ActionState<string> | null,
  formData: FormData
): Promise<ActionState<string>> {
  try {
    const ctx = await requirePermission(Permissions.HOMEWORK_SUBMIT)
    const assignmentId = formData.get("assignmentId")
    if (typeof assignmentId !== "string" || assignmentId.length === 0) return { success: false, message: "Missing assignmentId" }

    const result = await startHomeworkSubmission(assignmentId, ctx.userId)
    if ("error" in result) {
      return { success: false, message: result.error }
    }

    revalidatePath("/student/learning/assignments")

    await trackExamEvent("homework.started", {
      userId: ctx.userId,
      targetId: assignmentId,
      properties: {
        submissionId: result.submissionId,
      },
    })

    return { success: true, message: "Started", data: result.submissionId }
  } catch (e) {
    return handleActionError(e)
  }
}

export async function saveHomeworkAnswerAction(
  prevState: ActionState<string> | null,
  formData: FormData
): Promise<ActionState<string>> {
  try {
    const ctx = await requirePermission(Permissions.HOMEWORK_SUBMIT)
    const submissionId = formData.get("submissionId")
    const questionId = formData.get("questionId")
    const answerJson = formData.get("answerJson")
    if (typeof submissionId !== "string" || submissionId.length === 0) return { success: false, message: "Missing submissionId" }
    if (typeof questionId !== "string" || questionId.length === 0) return { success: false, message: "Missing questionId" }

    const submission = await getHomeworkSubmissionForPermission(submissionId)
    if (!submission) return { success: false, message: "Submission not found" }
    if (submission.studentId !== ctx.userId) return { success: false, message: "Unauthorized" }
    if (submission.status !== "started") return { success: false, message: "Submission is locked" }

    const payload =
      typeof answerJson === "string" && answerJson.length > 0
        ? safeJsonParse<unknown>(answerJson, "答案数据格式无效")
        : null

    await saveHomeworkAnswer(submissionId, questionId, payload)

    await trackExamEvent("homework.answer_saved", {
      userId: ctx.userId,
      targetId: submissionId,
      properties: {
        questionId,
      },
    })

    return { success: true, message: "Saved", data: submissionId }
  } catch (e) {
    return handleActionError(e)
  }
}

export async function submitHomeworkAction(
  prevState: ActionState<string> | null,
  formData: FormData
): Promise<ActionState<string>> {
  try {
    const ctx = await requirePermission(Permissions.HOMEWORK_SUBMIT)
    const submissionId = formData.get("submissionId")
    if (typeof submissionId !== "string" || submissionId.length === 0) return { success: false, message: "Missing submissionId" }

    const submission = await getHomeworkSubmissionForPermission(submissionId)
    if (!submission) return { success: false, message: "Submission not found" }
    if (submission.studentId !== ctx.userId) return { success: false, message: "Unauthorized" }
    if (submission.status !== "started") return { success: false, message: "Already submitted" }

    const now = new Date()
    const dueAt = submission.assignment.dueAt
    const allowLate = submission.assignment.allowLate
    const lateDueAt = submission.assignment.lateDueAt

    if (dueAt && now > dueAt && !allowLate) return { success: false, message: "Past due" }
    if (allowLate && lateDueAt && now > lateDueAt) return { success: false, message: "Past late due" }

    const isLate = Boolean(dueAt && now > dueAt)

    // V3-2: 即时自动批改回写
    const { isFullyAutoGraded, totalScore } = await markHomeworkSubmitted(submissionId, isLate)

    // 批改完成后自动采集错题 + 更新掌握度（仅当全部自动批改完成时）
    if (isFullyAutoGraded) {
      await runPostGradingHooks(submissionId, ctx.userId)
    }

    revalidatePath("/teacher/homework/submissions")
    revalidatePath("/student/learning/assignments")

    // V3-4: 埋点监控
    await trackExamEvent("homework.submitted", {
      userId: ctx.userId,
      targetId: submissionId,
      properties: {
        isLate,
        isFullyAutoGraded,
        totalScore,
      },
    })

    return {
      success: true,
      message: isFullyAutoGraded ? "Submitted and auto-graded" : "Submitted",
      data: submissionId,
    }
  } catch (e) {
    return handleActionError(e)
  }
}

export async function gradeHomeworkSubmissionAction(
  prevState: ActionState<string> | null,
  formData: FormData
): Promise<ActionState<string>> {
  try {
    const ctx = await requirePermission(Permissions.HOMEWORK_GRADE)

    const rawAnswersValue = formData.get("answersJson")
    const rawAnswers = typeof rawAnswersValue === "string" ? rawAnswersValue : null
    const parsed = GradeHomeworkSchema.safeParse({
      submissionId: formData.get("submissionId"),
      answers: rawAnswers ? safeJsonParse<unknown[]>(rawAnswers, "批改数据格式无效") : [],
    })

    if (!parsed.success) {
      return {
        success: false,
        message: "Invalid grading data",
        errors: parsed.error.flatten().fieldErrors,
      }
    }

    const { submissionId, answers } = parsed.data

    // 权限二次校验 + 获取 studentId（用于批改后处理）
    // 非管理员仅可批改自己创建的作业提交；管理员（dataScope.type === "all"）可批改所有提交
    const submissionForGrading = await getHomeworkSubmissionForGrading(submissionId)
    if (!submissionForGrading) {
      return { success: false, message: "Submission not found" }
    }
    if (ctx.dataScope.type !== "all" && submissionForGrading.creatorId !== ctx.userId) {
      return { success: false, message: "You can only grade submissions for your own assignments" }
    }

    await gradeHomeworkAnswers(
      submissionId,
      answers.map((ans) => ({
        id: ans.id,
        score: ans.score,
        feedback: ans.feedback ?? null,
      }))
    )

    // 批改完成后自动采集错题 + 更新掌握度
    await runPostGradingHooks(submissionId, submissionForGrading.studentId)

    revalidatePath("/teacher/homework/submissions")

    // V3-4: 埋点监控
    await trackExamEvent("homework.graded", {
      userId: ctx.userId,
      targetId: submissionId,
      properties: {
        answerCount: answers.length,
      },
    })

    return { success: true, message: "Grading saved" }
  } catch (e) {
    return handleActionError(e)
  }
}

/**
 * V3-7: 批量自动批改提交
 *
 * 教师在提交列表页勾选多份提交后，一键自动批改所有客观题。
 * 仅批改选择题/判断题，主观题保持原分数。
 */
export async function batchAutoGradeSubmissionsAction(
  prevState: ActionState<{ successCount: number; failedCount: number; fullyGradedCount: number }> | null,
  formData: FormData
): Promise<ActionState<{ successCount: number; failedCount: number; fullyGradedCount: number }>> {
  try {
    const ctx = await requirePermission(Permissions.HOMEWORK_GRADE)

    const rawSubmissionIds = formData.get("submissionIds")
    const submissionIdsJson = typeof rawSubmissionIds === "string" ? rawSubmissionIds : "[]"
    const submissionIds = safeJsonParse<string[]>(submissionIdsJson, "提交 ID 列表格式无效")

    if (submissionIds.length === 0) {
      return {
        success: false,
        message: "请至少选择一份提交",
      }
    }

    // 权限校验：非管理员仅可批改自己创建的作业提交
    if (ctx.dataScope.type !== "all") {
      for (const submissionId of submissionIds) {
        const submissionForGrading = await getHomeworkSubmissionForGrading(submissionId)
        if (!submissionForGrading) {
          return { success: false, message: `提交不存在: ${submissionId}` }
        }
        if (submissionForGrading.creatorId !== ctx.userId) {
          return { success: false, message: "只能批改自己创建的作业提交" }
        }
      }
    }

    const results = await batchAutoGradeSubmissions(submissionIds)
    const successCount = results.filter((r) => r.success).length
    const failedCount = results.filter((r) => !r.success).length
    const fullyGradedCount = results.filter((r) => r.success && r.isFullyAutoGraded).length

    // 批改完成后自动采集错题 + 更新掌握度（仅对成功批改的提交）
    await Promise.allSettled(
      results
        .filter(
          (r): r is typeof r & { studentId: string } =>
            r.success && typeof r.studentId === "string" && r.studentId.length > 0,
        )
        .map((r) => runPostGradingHooks(r.submissionId, r.studentId)),
    )

    revalidatePath("/teacher/homework/submissions")
    revalidatePath("/teacher/homework/assignments")

    await trackExamEvent("homework.graded", {
      userId: ctx.userId,
      targetId: submissionIds[0] ?? "",
      properties: {
        batchCount: submissionIds.length,
        successCount,
        fullyGradedCount,
      },
    })

    return {
      success: true,
      message: `批量批改完成：成功 ${successCount} 份，失败 ${failedCount} 份，其中 ${fullyGradedCount} 份已全自动批改`,
      data: { successCount, failedCount, fullyGradedCount },
    }
  } catch (e) {
    return handleActionError(e)
  }
}

// ---------------------------------------------------------------------------
// 答题拍照上传:扫描图管理(基于 fileAttachments 表)
// ---------------------------------------------------------------------------

/**
 * 获取某次提交的所有答题扫描图。
 * 扫描图存储在 fileAttachments 表中,targetType="homework", targetId=submissionId。
 *
 * 支持两类访问者:
 * - 学生(HOMEWORK_SUBMIT):仅可访问自己的提交
 * - 教师(HOMEWORK_GRADE):仅可访问自己创建的作业的提交
 */
export async function getScansAction(
  submissionId: string
): Promise<ActionState<ScanAttachment[]>> {
  try {
    // 优先尝试教师批改权限
    let isAuthorizedAsTeacher = false
    try {
      const gradeCtx = await requirePermission(Permissions.HOMEWORK_GRADE)
      const submissionForGrading = await getHomeworkSubmissionForGrading(submissionId)
      if (!submissionForGrading) {
        return { success: false, message: "提交记录不存在" }
      }
      // 管理员(dataScope.type === "all")或作业创建者可访问
      if (gradeCtx.dataScope.type === "all" || submissionForGrading.creatorId === gradeCtx.userId) {
        isAuthorizedAsTeacher = true
      }
    } catch {
      // 教师权限不足,继续尝试学生权限
    }

    if (!isAuthorizedAsTeacher) {
      // 回退到学生权限:仅允许提交者本人访问
      const submitCtx = await requirePermission(Permissions.HOMEWORK_SUBMIT)
      const submission = await getHomeworkSubmissionForPermission(submissionId)
      if (!submission) {
        return { success: false, message: "提交记录不存在" }
      }
      if (submission.studentId !== submitCtx.userId) {
        return { success: false, message: "无权访问此提交" }
      }
    }

    const scans = await getScansBySubmissionId(submissionId)

    return { success: true, message: "OK", data: scans }
  } catch (e) {
    return handleActionError(e)
  }
}

/**
 * 删除某张答题扫描图。
 * 仅允许提交者本人删除,且仅在提交状态为 started 时允许。
 */
export async function deleteScanAction(
  submissionId: string,
  fileId: string
): Promise<ActionState<null>> {
  try {
    const ctx = await requirePermission(Permissions.HOMEWORK_SUBMIT)

    const submission = await getHomeworkSubmissionForPermission(submissionId)
    if (!submission) {
      return { success: false, message: "提交记录不存在" }
    }
    if (submission.studentId !== ctx.userId) {
      return { success: false, message: "无权操作此提交" }
    }
    if (submission.status !== "started") {
      return { success: false, message: "提交已锁定,无法修改" }
    }

    const { deleteFileAttachment } = await import("@/modules/files/data-access")
    await deleteFileAttachment(fileId)

    await trackExamEvent("homework.scan_deleted", {
      userId: ctx.userId,
      targetId: submissionId,
      properties: {
        fileId,
      },
    })

    return { success: true, message: "已删除", data: null }
  } catch (e) {
    return handleActionError(e)
  }
}

/**
 * 查询某作业的优秀提交列表（P3-1：优秀作业展示）。
 *
 * 权限：仅教师/管理员可调用（HOMEWORK_GRADE），系统会自动根据
 * 教师的数据范围（scope）过滤学生提交，避免越权访问。
 *
 * @param assignmentId 作业 ID
 * @param minPercentage 最低得分百分比阈值（0-100，默认 80）
 * @param limit 返回数量上限（默认 10，最大 50）
 */
export async function getExcellentSubmissionsAction(params: {
  assignmentId: string
  minPercentage?: number
  limit?: number
}): Promise<ActionState<ExcellentSubmissionItem[]>> {
  try {
    const ctx = await requirePermission(Permissions.HOMEWORK_GRADE)

    const data = await getExcellentSubmissions({
      assignmentId: params.assignmentId,
      minPercentage: params.minPercentage,
      limit: params.limit,
      scope: ctx.dataScope,
    })

    trackExamEvent("homework.excellent_viewed", {
      userId: ctx.userId,
      targetId: params.assignmentId,
      properties: {
        count: data.length,
        minPercentage: params.minPercentage ?? 80,
      },
    })

    return { success: true, message: "", data }
  } catch (e) {
    return handleActionError(e)
  }
}

/**
 * 向未提交作业的学生发送催交通知（P3-2：作业催交提醒）。
 *
 * 权限：仅教师/管理员可调用（HOMEWORK_GRADE），系统自动根据
 * 教师的数据范围（scope）过滤学生。
 *
 * 流程：
 * 1. 查询该作业下未提交的学生列表。
 * 2. 为每个学生创建站内通知（type=homework, priority=high）。
 * 3. 返回催交的学生数量。
 *
 * 防滥用：同一作业的催交通知会去重（同一学生不重复创建）。
 *
 * @param assignmentId 作业 ID
 */
export async function remindUnsubmittedAction(params: {
  assignmentId: string
}): Promise<ActionState<{ remindedCount: number }>> {
  try {
    const ctx = await requirePermission(Permissions.HOMEWORK_GRADE)

    const unsubmitted = await getUnsubmittedStudents({
      assignmentId: params.assignmentId,
      scope: ctx.dataScope,
    })

    if (unsubmitted.length === 0) {
      return { success: true, message: "", data: { remindedCount: 0 } }
    }

    // 获取作业标题用于通知内容
    const assignment = await getHomeworkAssignmentById(params.assignmentId, ctx.dataScope)
    if (!assignment) {
      return { success: false, message: "Assignment not found" }
    }

    const assignmentTitle = assignment.title
    const dueAt = assignment.dueAt
      ? new Date(assignment.dueAt).toLocaleString("zh-CN")
      : ""

    const { createNotification } = await import("@/modules/notifications/data-access")

    const title = `作业催交提醒：${assignmentTitle}`
    const content = dueAt
      ? `请尽快完成并提交作业「${assignmentTitle}」，截止时间：${dueAt}。`
      : `请尽快完成并提交作业「${assignmentTitle}」。`

    const results = await Promise.allSettled(
      unsubmitted.map((student) =>
        createNotification({
          userId: student.studentId,
          type: "homework",
          title,
          content,
          link: `/student/learning/assignments/${params.assignmentId}`,
          priority: "high",
        })
      )
    )

    const successCount = results.filter((r) => r.status === "fulfilled").length
    const failedCount = results.length - successCount

    if (failedCount > 0) {
      console.error(
        `[remindUnsubmitted] ${failedCount}/${results.length} notifications failed for assignment ${params.assignmentId}`
      )
    }

    trackExamEvent("homework.remind_unsubmitted", {
      userId: ctx.userId,
      targetId: params.assignmentId,
      properties: {
        remindedCount: successCount,
        failedCount,
        total: results.length,
      },
    })

    return {
      success: true,
      message: `已向 ${successCount} 名学生发送催交通知`,
      data: { remindedCount: successCount },
    }
  } catch (e) {
    return handleActionError(e)
  }
}
