"use server"

/**
 * 富文本编辑器 Server Actions —— 从 exams/actions.ts 拆分而来。
 *
 * 职责：
 * - createExamFromRichEditorAction：从富文本编辑器保存试卷草稿
 * - updateExamFromRichEditorAction：从富文本编辑器更新已有试卷（P0-4 修复：DB 事务下沉到 data-access）
 * - 共享辅助函数：convertStructureNode（递归转换 EditorStructureNode → AiGeneratedStructureNode）
 */

import { revalidatePath } from "next/cache"
import { getTranslations } from "next-intl/server"
import type { ActionState } from "@/shared/types/action-state"
import { requirePermission, PermissionDeniedError } from "@/shared/lib/auth-guard"
import { Permissions } from "@/shared/types/permissions"
import { z } from "zod"
import { handleActionError, safeJsonParse } from "@/shared/lib/action-utils"
import { createQuestionWithRelations } from "@/modules/questions/data-access"
import {
  getExamCreatorId,
  persistAiGeneratedExamDraft,
  updateExamWithRichEditorData,
} from "./data-access"
import type { AiGeneratedStructureNode } from "./ai-pipeline"
import type { QuestionContentResult } from "./ai-pipeline/parse"
import type { EditorDoc, EditorStructureNode } from "./editor/exam-rich-editor-types"
import { toStandaloneQuestionType } from "./editor/exam-rich-editor-types"
import {
  failState,
  getStringValue,
  invalidFormState,
  parseExamModeConfig,
  prepareExamCreateContext,
  successState,
} from "./actions-helpers"

// ---------------------------------------------------------------------------
// Schemas
// ---------------------------------------------------------------------------

const RichExamCreateSchema = z.object({
  title: z.string().min(1, "标题不能为空"),
  subject: z.string().min(1),
  grade: z.string().min(1),
  difficulty: z.coerce.number().int().min(1).max(5),
  totalScore: z.coerce.number().int().min(1),
  durationMin: z.coerce.number().int().min(1),
  scheduledAt: z.string().optional().nullable(),
  /** Tiptap JSONContent 文档(JSON 字符串) */
  editorDoc: z.string().min(1, "试卷内容不能为空"),
})

const RichExamUpdateSchema = z.object({
  examId: z.string().min(1),
  title: z.string().min(1, "标题不能为空"),
  editorDoc: z.string().min(1, "试卷内容不能为空"),
})

// ---------------------------------------------------------------------------
// 共享辅助函数
// ---------------------------------------------------------------------------

/**
 * 递归转换 structure 节点,保留已有 questionId。
 * 被 createExamFromRichEditorAction 和 updateExamFromRichEditorAction 共享。
 */
const convertStructureNode = (node: EditorStructureNode): AiGeneratedStructureNode => {
  if (node.type === "section") {
    return {
      id: node.id,
      type: "section",
      title: node.title ?? "",
      level: node.level,
      children: (node.children ?? []).map(convertStructureNode),
    }
  }
  if (node.type === "group") {
    return {
      id: node.id,
      type: "group",
      title: node.title ?? "",
      instruction: node.instruction,
      children: (node.children ?? []).map(convertStructureNode),
    }
  }
  return {
    id: node.id,
    type: "question",
    questionId: node.questionId ?? "",
    score: node.score ?? 0,
  }
}

// ---------------------------------------------------------------------------
// Server Actions
// ---------------------------------------------------------------------------

/**
 * 从富文本编辑器保存试卷草稿。
 * 将 Tiptap JSONContent 转换为 questions + structure 后持久化。
 */
export async function createExamFromRichEditorAction(
  prevState: ActionState<string> | null,
  formData: FormData
): Promise<ActionState<string>> {
  const t = await getTranslations("examHomework")
  try {
    const ctx = await requirePermission(Permissions.EXAM_CREATE)

    const parsed = RichExamCreateSchema.safeParse({
      title: getStringValue(formData, "title"),
      subject: getStringValue(formData, "subject"),
      grade: getStringValue(formData, "grade"),
      difficulty: getStringValue(formData, "difficulty"),
      totalScore: getStringValue(formData, "totalScore"),
      durationMin: getStringValue(formData, "durationMin"),
      scheduledAt: getStringValue(formData, "scheduledAt") ?? null,
      editorDoc: getStringValue(formData, "editorDoc"),
    })
    if (!parsed.success) {
      return invalidFormState<string>(parsed.error, { useFirstMessage: true })
    }

    const input = parsed.data
    const editorDoc = safeJsonParse(input.editorDoc, t("exam.actionMessages.contentInvalid"))
    if (!editorDoc) {
      return failState<string>(t("exam.actionMessages.contentParseFailed"))
    }

    const context = await prepareExamCreateContext({
      subject: input.subject,
      grade: input.grade,
      difficulty: input.difficulty,
      totalScore: input.totalScore,
      durationMin: input.durationMin,
      scheduledAt: input.scheduledAt,
    })

    // 将 Tiptap doc 转换为 EditorDoc 结构
    // safeJsonParse 返回 unknown，此处从 unknown 收窄为 EditorDoc
    const { editorDocToStructure } = await import("./editor/editor-to-structure")
    const structure = editorDocToStructure(editorDoc as unknown as EditorDoc, input.title)

    // 转换为 AI 生成格式以复用 persistAiGeneratedExamDraft
    // RichQuestionContent 与 QuestionContentResult 的 options.isCorrect 可选性不同，
    // 此处从 unknown 收窄为 QuestionContentResult（运行时由下游 Zod 校验保证安全）
    // P2-1 修复：composite 题型无法直接落库，通过 toStandaloneQuestionType 安全收窄
    const generated = structure.questions.map((q) => ({
      id: q.id,
      type: toStandaloneQuestionType(q.type, "text"),
      difficulty: input.difficulty,
      score: q.score,
      content: q.content as unknown as QuestionContentResult,
    }))

    const aiStructure: AiGeneratedStructureNode[] = structure.structure.map(convertStructureNode)

    await persistAiGeneratedExamDraft({
      examId: context.examId,
      title: input.title,
      creatorId: ctx.userId,
      subjectId: input.subject,
      gradeId: input.grade,
      scheduledAt: context.scheduled,
      description: context.buildDescription(),
      structure: aiStructure,
      generated,
      examModeConfig: parseExamModeConfig(formData),
    })

    revalidatePath("/teacher/exams/all")
    return successState(context.examId, t("exam.actionMessages.draftCreated"))
  } catch (error) {
    if (error instanceof PermissionDeniedError) {
      return failState<string>(error.message)
    }
    console.error("[createExamFromRichEditorAction]", error instanceof Error ? error.message : String(error))
    return handleActionError(error)
  }
}

/**
 * 从富文本编辑器更新已有试卷。
 * 将 Tiptap JSONContent 转换为 structure 后更新 exam.structure 和 exam_questions。
 * 对于 questionId 为空的新题目,创建 question 记录后 remap ID。
 * P0-4 修复：DB 事务逻辑已下沉到 data-access.updateExamWithRichEditorData。
 */
export async function updateExamFromRichEditorAction(
  prevState: ActionState<string> | null,
  formData: FormData
): Promise<ActionState<string>> {
  const t = await getTranslations("examHomework")
  try {
    const ctx = await requirePermission(Permissions.EXAM_UPDATE)

    const parsed = RichExamUpdateSchema.safeParse({
      examId: getStringValue(formData, "examId"),
      title: getStringValue(formData, "title"),
      editorDoc: getStringValue(formData, "editorDoc"),
    })
    if (!parsed.success) {
      return invalidFormState<string>(parsed.error, { useFirstMessage: true })
    }

    const input = parsed.data
    const editorDoc = safeJsonParse(input.editorDoc, t("exam.actionMessages.contentInvalid"))
    if (!editorDoc) {
      return failState<string>(t("exam.actionMessages.contentParseFailed"))
    }

    // Ownership check
    if (ctx.dataScope.type !== "all") {
      const creatorId = await getExamCreatorId(input.examId)
      if (!creatorId || creatorId !== ctx.userId) {
        return failState<string>(t("exam.actionMessages.onlyOwnUpdate"))
      }
    }

    const { editorDocToStructure } = await import("./editor/editor-to-structure")
    // safeJsonParse 返回 unknown，此处从 unknown 收窄为 EditorDoc
    const structure = editorDocToStructure(editorDoc as unknown as EditorDoc, input.title)

    const aiStructure: AiGeneratedStructureNode[] = structure.structure.map(convertStructureNode)

    // 对于 questionId 为空的新题目,创建 question 记录
    const questionIdMapping = new Map<string, string>()
    const newQuestions = structure.questions.filter((q) => !q.id || q.id.startsWith("temp"))
    for (const q of newQuestions) {
      const newQuestionId = await createQuestionWithRelations(
        {
          // RichQuestionContent 与 QuestionContentResult 的 options.isCorrect 可选性不同，
          // 此处从 unknown 收窄为 QuestionContentResult（运行时由下游 Zod 校验保证安全）
          content: q.content as unknown as QuestionContentResult,
          // P2-1 修复：通过 toStandaloneQuestionType 安全收窄，避免 composite 落库
          type: toStandaloneQuestionType(q.type, "text"),
          difficulty: 3,
        },
        ctx.userId
      )
      questionIdMapping.set(q.id, newQuestionId)
    }

    // Remap structure 中的 questionId
    const remapStructure = (nodes: AiGeneratedStructureNode[]): AiGeneratedStructureNode[] => {
      return nodes.map((node) => {
        if (node.type === "question") {
          const mapped = questionIdMapping.get(node.questionId ?? "") ?? node.questionId
          return { ...node, questionId: mapped }
        }
        if (node.type === "group" || node.type === "section") {
          return { ...node, children: remapStructure(node.children ?? []) }
        }
        return node
      })
    }

    const finalStructure = remapStructure(aiStructure)

    // 提取有序题目列表
    const orderedQuestions: Array<{ id: string; score: number }> = []
    const collectOrder = (nodes: AiGeneratedStructureNode[]) => {
      for (const node of nodes) {
        if (node.type === "question" && node.questionId) {
          orderedQuestions.push({ id: node.questionId, score: node.score ?? 0 })
          continue
        }
        if ((node.type === "group" || node.type === "section") && node.children) {
          collectOrder(node.children)
        }
      }
    }
    collectOrder(finalStructure)

    // P0-4 修复：DB 事务下沉到 data-access 层
    await updateExamWithRichEditorData(input.examId, {
      title: input.title,
      structure: finalStructure,
      orderedQuestions,
    })

    revalidatePath("/teacher/exams/all")
    revalidatePath(`/teacher/exams/${input.examId}/build`)
    return successState(input.examId, t("exam.actionMessages.examUpdated"))
  } catch (error) {
    if (error instanceof PermissionDeniedError) {
      return failState<string>(error.message)
    }
    console.error("[updateExamFromRichEditorAction]", error instanceof Error ? error.message : String(error))
    return handleActionError(error)
  }
}
