"use server"

import { revalidatePath } from "next/cache"

import { requirePermission, PermissionDeniedError } from "@/shared/lib/auth-guard"
import { Permissions, type AuthContext } from "@/shared/types/permissions"
import type { ActionState } from "@/shared/types/action-state"
import { handleActionError } from "@/shared/lib/action-utils"
import {
  getStudentActiveClassId,
  getStudentActiveGradeId,
  verifyTeacherOwnsClass,
} from "@/modules/classes/data-access"

import {
  CreatePracticeSessionSchema,
  SubmitPracticeAnswerSchema,
  CompletePracticeSessionSchema,
  AbandonPracticeSessionSchema,
} from "./schema"
import {
  createPracticeSession,
  submitPracticeAnswer,
  completePracticeSession,
  abandonPracticeSession,
  getPracticeSessionById,
  getPracticeSessions,
  getPracticeStats,
} from "./data-access"
import { parseSourceMeta } from "./lib/source-meta"
import { practiceErrors } from "./lib/errors"
import type {
  PracticeSessionDetail,
  PracticeSessionSummary,
  PracticeStats,
} from "./types"

// ---------------------------------------------------------------------------
// 权限辅助：dataScope 下 studentId 归属校验
// ---------------------------------------------------------------------------

/**
 * 根据 dataScope 解析并校验目标学生 ID。
 *
 * 行级权限校验（P0 安全修复）：
 * - `all`：admin 可访问任意 studentId（不传则用 ctx.userId）
 * - `owned`：仅可访问 ctx.userId（学生本人）
 * - `class_members`：仅可访问 ctx.userId（学生本人）
 * - `children`：仅可访问 childrenIds 中的学生（家长）
 * - `class_taught`：校验学生属于教师所教班级（教师）
 * - `grade_managed`：校验学生属于年级主任所辖年级（年级主任）
 *
 * @throws {PermissionDeniedError} studentId 不在 dataScope 范围内
 * @returns 校验通过的目标 studentId
 */
async function resolveTargetStudentId(
  ctx: AuthContext,
  studentId?: string,
): Promise<string> {
  // 未传 studentId：根据 scope 选取默认值
  if (!studentId || studentId === ctx.userId) {
    if (ctx.dataScope.type === "children") {
      // 家长默认查第一个孩子
      return ctx.dataScope.childrenIds[0] ?? ctx.userId
    }
    return ctx.userId
  }

  // 传入了 studentId：按 scope 校验归属
  switch (ctx.dataScope.type) {
    case "all":
      // admin 无限制
      return studentId

    case "owned":
      // 学生仅能访问自己
      if (studentId !== ctx.userId) {
        throw new PermissionDeniedError(Permissions.ADAPTIVE_PRACTICE_READ)
      }
      return studentId

    case "class_members":
      // 学生仅能访问自己
      if (studentId !== ctx.userId) {
        throw new PermissionDeniedError(Permissions.ADAPTIVE_PRACTICE_READ)
      }
      return studentId

    case "children":
      // 家长仅能访问自己的子女
      if (!ctx.dataScope.childrenIds.includes(studentId)) {
        throw new PermissionDeniedError(Permissions.ADAPTIVE_PRACTICE_READ)
      }
      return studentId

    case "class_taught": {
      // 教师：校验学生属于其所教班级
      const classId = await getStudentActiveClassId(studentId)
      if (!classId) {
        throw new PermissionDeniedError(Permissions.ADAPTIVE_PRACTICE_READ)
      }
      const owns = await verifyTeacherOwnsClass(classId, ctx.userId)
      if (!owns) {
        throw new PermissionDeniedError(Permissions.ADAPTIVE_PRACTICE_READ)
      }
      return studentId
    }

    case "grade_managed": {
      // 年级主任：校验学生属于其所辖年级
      const gradeId = await getStudentActiveGradeId(studentId)
      if (!gradeId || !ctx.dataScope.gradeIds.includes(gradeId)) {
        throw new PermissionDeniedError(Permissions.ADAPTIVE_PRACTICE_READ)
      }
      return studentId
    }

    default:
      throw new PermissionDeniedError(Permissions.ADAPTIVE_PRACTICE_READ)
  }
}

// ---------------------------------------------------------------------------
// 查询 Actions
// ---------------------------------------------------------------------------

export async function getPracticeSessionsAction(
  studentId?: string,
): Promise<ActionState<{ data: PracticeSessionSummary[]; total: number }>> {
  try {
    const ctx = await requirePermission(Permissions.ADAPTIVE_PRACTICE_READ)
    const targetStudentId = await resolveTargetStudentId(ctx, studentId)

    const result = await getPracticeSessions(targetStudentId)
    return { success: true, data: result }
  } catch (e) {
    return handleActionError(e)
  }
}

export async function getPracticeSessionDetailAction(
  sessionId: string,
  studentId?: string,
): Promise<ActionState<PracticeSessionDetail>> {
  try {
    const ctx = await requirePermission(Permissions.ADAPTIVE_PRACTICE_READ)
    const targetStudentId = await resolveTargetStudentId(ctx, studentId)

    const data = await getPracticeSessionById(sessionId, targetStudentId)
    if (!data) {
      // 通过 errorCode 返回，前端 t(`errors.session_not_found`) 查 i18n
      throw practiceErrors.sessionNotFound()
    }
    return { success: true, data }
  } catch (e) {
    return handleActionError(e)
  }
}

export async function getPracticeStatsAction(
  studentId?: string,
): Promise<ActionState<PracticeStats>> {
  try {
    const ctx = await requirePermission(Permissions.ADAPTIVE_PRACTICE_READ)
    const targetStudentId = await resolveTargetStudentId(ctx, studentId)

    const data = await getPracticeStats(targetStudentId)
    return { success: true, data }
  } catch (e) {
    return handleActionError(e)
  }
}

// ---------------------------------------------------------------------------
// 写入 Actions
// ---------------------------------------------------------------------------

export async function createPracticeSessionAction(
  prevState: ActionState<{ sessionId: string; selectedCount: number }> | undefined,
  formData: FormData,
): Promise<ActionState<{ sessionId: string; selectedCount: number }>> {
  try {
    const ctx = await requirePermission(Permissions.ADAPTIVE_PRACTICE_MANAGE)

    const jsonString = formData.get("json")
    if (typeof jsonString !== "string") {
      // errorCode: invalid_input → 前端 t(`errors.invalid_input`)
      return { success: false, message: "提交格式错误", errorCode: "invalid_input" }
    }

    const parsed = CreatePracticeSessionSchema.safeParse(JSON.parse(jsonString))
    if (!parsed.success) {
      return {
        success: false,
        message: "输入验证失败",
        errorCode: "validation_error",
        errors: parsed.error.flatten().fieldErrors,
      }
    }

    // 严格类型守卫：替代 `as unknown as PracticeSourceMeta` 断言
    const sourceMeta = parseSourceMeta(parsed.data.practiceType, parsed.data.sourceMeta)
    if (!sourceMeta) {
      return {
        success: false,
        message: "来源元数据结构与练习类型不匹配",
        errorCode: "invalid_source_meta",
      }
    }

    // 写入操作仅允许学生本人或家长代子女发起
    // 家长 dataScope.type === "children" 时，使用第一个子女 ID
    const targetStudentId = ctx.dataScope.type === "children"
      ? ctx.dataScope.childrenIds[0] ?? ctx.userId
      : ctx.userId

    const result = await createPracticeSession(targetStudentId, {
      practiceType: parsed.data.practiceType,
      subjectId: parsed.data.subjectId,
      sourceMeta,
      questionCount: parsed.data.questionCount,
    })

    revalidatePath("/student/practice")

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

export async function submitPracticeAnswerAction(
  prevState: ActionState<{ isCorrect: boolean | null; score: number | null }> | undefined,
  formData: FormData,
): Promise<ActionState<{ isCorrect: boolean | null; score: number | null }>> {
  try {
    const ctx = await requirePermission(Permissions.ADAPTIVE_PRACTICE_MANAGE)

    const jsonString = formData.get("json")
    if (typeof jsonString !== "string") {
      return { success: false, message: "提交格式错误", errorCode: "invalid_input" }
    }

    const parsed = SubmitPracticeAnswerSchema.safeParse(JSON.parse(jsonString))
    if (!parsed.success) {
      return {
        success: false,
        message: "输入验证失败",
        errorCode: "validation_error",
        errors: parsed.error.flatten().fieldErrors,
      }
    }

    const { sessionId, answerId, answer, skip } = parsed.data

    // 学生本人或家长代子女提交
    const targetStudentId = ctx.dataScope.type === "children"
      ? ctx.dataScope.childrenIds[0] ?? ctx.userId
      : ctx.userId

    const result = await submitPracticeAnswer(
      sessionId,
      targetStudentId,
      answerId,
      answer,
      skip ?? false,
    )

    revalidatePath(`/student/practice/${sessionId}`)

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

export async function completePracticeSessionAction(
  prevState: ActionState<void> | undefined,
  formData: FormData,
): Promise<ActionState<void>> {
  try {
    const ctx = await requirePermission(Permissions.ADAPTIVE_PRACTICE_MANAGE)

    const parsed = CompletePracticeSessionSchema.safeParse({
      sessionId: formData.get("sessionId"),
    })
    if (!parsed.success) {
      return {
        success: false,
        message: "输入验证失败",
        errorCode: "validation_error",
        errors: parsed.error.flatten().fieldErrors,
      }
    }

    const targetStudentId = ctx.dataScope.type === "children"
      ? ctx.dataScope.childrenIds[0] ?? ctx.userId
      : ctx.userId

    await completePracticeSession(parsed.data.sessionId, targetStudentId)

    revalidatePath("/student/practice")
    revalidatePath(`/student/practice/${parsed.data.sessionId}`)

    return { success: true }
  } catch (e) {
    return handleActionError(e)
  }
}

export async function abandonPracticeSessionAction(
  prevState: ActionState<void> | undefined,
  formData: FormData,
): Promise<ActionState<void>> {
  try {
    const ctx = await requirePermission(Permissions.ADAPTIVE_PRACTICE_MANAGE)

    const parsed = AbandonPracticeSessionSchema.safeParse({
      sessionId: formData.get("sessionId"),
    })
    if (!parsed.success) {
      return {
        success: false,
        message: "输入验证失败",
        errorCode: "validation_error",
        errors: parsed.error.flatten().fieldErrors,
      }
    }

    const targetStudentId = ctx.dataScope.type === "children"
      ? ctx.dataScope.childrenIds[0] ?? ctx.userId
      : ctx.userId

    await abandonPracticeSession(parsed.data.sessionId, targetStudentId)

    revalidatePath("/student/practice")

    return { success: true }
  } catch (e) {
    return handleActionError(e)
  }
}
