"use server"

import { revalidatePath } from "next/cache"
import { getTranslations } from "next-intl/server"
import { requirePermission } from "@/shared/lib/auth-guard"
import { handleActionError } from "@/shared/lib/action-utils"
import { Permissions } from "@/shared/types/permissions"

import type { ActionState } from "@/shared/types/action-state"
import {
  enrollStudentByEmail,
  enrollStudentByInvitationCode,
  enrollTeacherByInvitationCode,
  ensureClassInvitationCode,
  regenerateClassInvitationCode,
  setStudentEnrollmentStatus,
  verifyTeacherOwnsClass,
} from "./data-access"
import {
  EnrollStudentByEmailSchema,
} from "./schema"
import { hasAdminScope, hasTeacherScope, hasStudentScope } from "./actions-shared"

/**
 * 校验当前用户对班级的归属权限（P0-2/P0-5 审计修复）。
 * - admin scope：跳过校验
 * - 其他用户：必须为该班级的班主任
 * 返回 null 表示通过，返回 string 表示错误消息。
 */
async function assertClassOwnership(
  ctx: Parameters<typeof hasAdminScope>[0],
  classId: string,
  t: (key: string) => string
): Promise<string | null> {
  if (hasAdminScope(ctx)) return null
  const owns = await verifyTeacherOwnsClass(classId, ctx.userId)
  return owns ? null : t("actions.notPermissionManageClass")
}

export async function enrollStudentByEmailAction(
  classId: string,
  prevState: ActionState | null,
  formData: FormData
): Promise<ActionState> {
  try {
    const ctx = await requirePermission(Permissions.CLASS_ENROLL)
    const t = await getTranslations("classes")

    const parsed = EnrollStudentByEmailSchema.safeParse({
      classId,
      email: formData.get("email"),
    })
    if (!parsed.success) {
      return { success: false, message: t("actions.classAndEmailRequired") }
    }

    // P0-5: 越权校验——教师注册学生前校验班级归属
    const ownErr = await assertClassOwnership(ctx, parsed.data.classId, t)
    if (ownErr) return { success: false, message: ownErr }

    try {
      await enrollStudentByEmail(parsed.data.classId, parsed.data.email)
      revalidatePath("/teacher/classes/students")
      revalidatePath("/teacher/classes/my")
      return { success: true, message: t("actions.studentAdded") }
    } catch (error) {
      return handleActionError(error)
    }
  } catch (e) {
    return handleActionError(e)
  }
}

export async function joinClassByInvitationCodeAction(
  prevState: ActionState<{ classId: string }> | null,
  formData: FormData
): Promise<ActionState<{ classId: string }>> {
  try {
    const ctx = await requirePermission(Permissions.CLASS_ENROLL)
    const t = await getTranslations("classes")

    const code = formData.get("code")
    if (typeof code !== "string" || code.trim().length === 0) {
      return { success: false, message: t("actions.invitationCodeRequired") }
    }

    // v3：rate limit 防爆破（10 次/5 分钟，按 userId）
    const { rateLimit, rateLimitKey } = await import("@/shared/lib/rate-limit")
    const rlKey = rateLimitKey("class-join", ctx.userId)
    const rlResult = await rateLimit({
      key: rlKey,
      limit: 10,
      windowMs: 5 * 60 * 1000,
    })
    if (!rlResult.success) {
      return { success: false, message: t("actions.tooManyAttempts") }
    }

    // P1-1: 使用 dataScope 替代 ctx.roles.includes("teacher") 硬编码
    const isTeacher = hasTeacherScope(ctx)
    const subjectValue = formData.get("subject")
    const subject = isTeacher && typeof subjectValue === "string" ? subjectValue.trim() : null

    if (isTeacher && (!subject || subject.length === 0)) {
      return { success: false, message: t("actions.subjectRequired") }
    }

    try {
      const classId = isTeacher
        ? await enrollTeacherByInvitationCode(ctx.userId, code, subject)
        : await enrollStudentByInvitationCode(ctx.userId, code)

      // 成功后重置 rate limit
      const { resetRateLimit } = await import("@/shared/lib/rate-limit")
      await resetRateLimit(rlKey)

      // 审计日志
      const { logAudit } = await import("@/shared/lib/audit-logger")
      await logAudit({
        action: "class.invitation.consume",
        module: "classes",
        targetId: classId,
        targetType: "class",
        detail: {
          code: String(code).trim().toUpperCase(),
          userId: ctx.userId,
          // P1-1: 使用 dataScope 推断角色名，避免硬编码
          role: hasStudentScope(ctx) ? "student" : "teacher",
          subject,
        },
      })

      if (hasStudentScope(ctx)) {
        revalidatePath("/student/learning/courses")
        revalidatePath("/student/schedule")
      } else {
        revalidatePath("/teacher/classes/my")
      }
      revalidatePath("/profile")
      return { success: true, message: t("actions.joinedClass"), data: { classId } }
    } catch (error) {
      // 审计日志：加入失败
      const { logAudit } = await import("@/shared/lib/audit-logger")
      await logAudit({
        action: "class.invitation.consume_failed",
        module: "classes",
        targetId: String(code).trim().toUpperCase(),
        targetType: "invitation_code",
        detail: {
          userId: ctx.userId,
          reason: error instanceof Error ? error.message : "unknown",
        },
        status: "failure",
      })
      return handleActionError(error)
    }
  } catch (e) {
    return handleActionError(e)
  }
}

export async function ensureClassInvitationCodeAction(classId: string): Promise<ActionState<{ code: string }>> {
  try {
    await requirePermission(Permissions.CLASS_ENROLL)
    const t = await getTranslations("classes")

    if (typeof classId !== "string" || classId.trim().length === 0) {
      return { success: false, message: t("actions.missingClassId") }
    }

    try {
      const code = await ensureClassInvitationCode(classId)
      revalidatePath("/teacher/classes/my")
      revalidatePath(`/teacher/classes/my/${encodeURIComponent(classId)}`)
      return { success: true, message: t("actions.invitationCodeReady"), data: { code } }
    } catch (error) {
      return handleActionError(error)
    }
  } catch (e) {
    return handleActionError(e)
  }
}

export async function regenerateClassInvitationCodeAction(classId: string): Promise<ActionState<{ code: string }>> {
  try {
    await requirePermission(Permissions.CLASS_ENROLL)
    const t = await getTranslations("classes")

    if (typeof classId !== "string" || classId.trim().length === 0) {
      return { success: false, message: t("actions.missingClassId") }
    }

    try {
      const code = await regenerateClassInvitationCode(classId)
      revalidatePath("/teacher/classes/my")
      revalidatePath(`/teacher/classes/my/${encodeURIComponent(classId)}`)
      return { success: true, message: t("actions.invitationCodeUpdated"), data: { code } }
    } catch (error) {
      return handleActionError(error)
    }
  } catch (e) {
    return handleActionError(e)
  }
}

/**
 * v3 新增：生成自定义邀请码（支持有效期/次数/备注）。
 * 对标 Google Classroom / 钉钉教育：管理员/教师可为班级生成带有效期与次数限制的邀请码。
 *
 * 权限：CLASS_ENROLL（沿用现有权限点，避免过度拆分）
 * 审计：调用 logAudit 记录生成操作
 */
export async function createClassInvitationCodeAction(
  prevState: ActionState<{ code: string; id: string }> | null,
  formData: FormData
): Promise<ActionState<{ code: string; id: string }>> {
  try {
    const ctx = await requirePermission(Permissions.CLASS_ENROLL)
    const t = await getTranslations("classes")

    const classId = String(formData.get("classId") ?? "").trim()
    if (!classId) {
      return { success: false, message: t("actions.missingClassId") }
    }

    const expiresInHoursRaw = formData.get("expiresInHours")
    const maxUsesRaw = formData.get("maxUses")
    const note = String(formData.get("note") ?? "").trim() || null

    const expiresInHours =
      expiresInHoursRaw && String(expiresInHoursRaw).trim() !== ""
        ? Number(expiresInHoursRaw)
        : null
    const maxUses =
      maxUsesRaw && String(maxUsesRaw).trim() !== ""
        ? Number(maxUsesRaw)
        : null

    if (expiresInHours !== null && (!Number.isFinite(expiresInHours) || expiresInHours <= 0)) {
      return { success: false, message: t("actions.invalidExpiresInHours") }
    }
    if (maxUses !== null && (!Number.isFinite(maxUses) || maxUses <= 0)) {
      return { success: false, message: t("actions.invalidMaxUses") }
    }

    try {
      const { createInvitationCode } = await import("./data-access-invitations")
      const record = await createInvitationCode(classId, ctx.userId, {
        expiresInHours,
        maxUses,
        note,
      })

      // 审计日志
      const { logAudit } = await import("@/shared/lib/audit-logger")
      await logAudit({
        action: "class.invitation.create",
        module: "classes",
        targetId: classId,
        targetType: "class",
        detail: {
          codeId: record.id,
          code: record.code,
          expiresInHours,
          maxUses,
          note,
        },
      })

      revalidatePath("/teacher/classes/my")
      revalidatePath(`/teacher/classes/my/${encodeURIComponent(classId)}`)
      revalidatePath(`/admin/school/classes`)
      return {
        success: true,
        message: t("actions.invitationCodeGenerated"),
        data: { code: record.code, id: record.id },
      }
    } catch (error) {
      return handleActionError(error)
    }
  } catch (e) {
    return handleActionError(e)
  }
}

/**
 * v3 新增：撤销邀请码（软删除）。
 */
export async function revokeClassInvitationCodeAction(
  prevState: ActionState<null> | null,
  formData: FormData
): Promise<ActionState<null>> {
  try {
    const ctx = await requirePermission(Permissions.CLASS_ENROLL)
    const t = await getTranslations("classes")

    const codeId = String(formData.get("codeId") ?? "").trim()
    if (!codeId) {
      return { success: false, message: t("actions.missingCodeId") }
    }

    try {
      const { revokeInvitationCode } = await import("./data-access-invitations")
      await revokeInvitationCode(codeId, ctx.userId)

      const { logAudit } = await import("@/shared/lib/audit-logger")
      await logAudit({
        action: "class.invitation.revoke",
        module: "classes",
        targetId: codeId,
        targetType: "invitation_code",
        detail: { revokedBy: ctx.userId },
      })

      revalidatePath("/teacher/classes/my")
      revalidatePath(`/admin/school/classes`)
      return { success: true, message: t("actions.invitationCodeRevoked") }
    } catch (error) {
      return handleActionError(error)
    }
  } catch (e) {
    return handleActionError(e)
  }
}

/**
 * v3 新增：列出班级所有邀请码（管理端列表用）。
 */
export async function listClassInvitationCodesAction(
  classId: string
): Promise<ActionState<{ codes: Array<Record<string, unknown>> }>> {
  try {
    const ctx = await requirePermission(Permissions.CLASS_ENROLL)
    const t = await getTranslations("classes")

    if (typeof classId !== "string" || classId.trim().length === 0) {
      return { success: false, message: t("actions.missingClassId") }
    }

    // P0-2: 越权校验——防止教师枚举他班邀请码（admin scope 跳过）
    const ownErr = await assertClassOwnership(ctx, classId, t)
    if (ownErr) return { success: false, message: ownErr }

    try {
      const { listClassInvitationCodes } = await import("./data-access-invitations")
      const codes = await listClassInvitationCodes(classId)
      return {
        success: true,
        data: {
          codes: codes.map((c) => ({
            id: c.id,
            code: c.code,
            status: c.status,
            maxUses: c.maxUses,
            usedCount: c.usedCount,
            expiresAt: c.expiresAt?.toISOString() ?? null,
            createdAt: c.createdAt.toISOString(),
            revokedAt: c.revokedAt?.toISOString() ?? null,
            note: c.note,
          })),
        },
      }
    } catch (error) {
      return handleActionError(error)
    }
  } catch (e) {
    return handleActionError(e)
  }
}

export async function setStudentEnrollmentStatusAction(
  classId: string,
  studentId: string,
  status: "active" | "inactive"
): Promise<ActionState> {
  try {
    await requirePermission(Permissions.CLASS_ENROLL)
    const t = await getTranslations("classes")

    if (!classId?.trim() || !studentId?.trim()) {
      return { success: false, message: t("actions.missingEnrollmentInfo") }
    }

    try {
      await setStudentEnrollmentStatus(classId, studentId, status)
      revalidatePath("/teacher/classes/students")
      revalidatePath("/teacher/classes/my")
      return { success: true, message: t("actions.studentUpdated") }
    } catch (error) {
      return handleActionError(error)
    }
  } catch (e) {
    return handleActionError(e)
  }
}

/**
 * P2-4 批量导入学生：解析 CSV（每行 name,email 或仅 email），逐个调用 enrollStudentByEmail。
 * 用于开学季批量注册，提升配置效率。
 */
export async function bulkEnrollStudentsAction(
  classId: string,
  prevState: ActionState<{ imported: number; failed: number; errors: string[] }> | undefined,
  formData: FormData
): Promise<ActionState<{ imported: number; failed: number; errors: string[] }>> {
  try {
    const ctx = await requirePermission(Permissions.CLASS_ENROLL)
    const t = await getTranslations("classes")

    // P0-5: 越权校验——批量注册学生前校验班级归属
    const ownErr = await assertClassOwnership(ctx, classId, t)
    if (ownErr) return { success: false, message: ownErr }

    const csvText = String(formData.get("csv") ?? "").trim()
    if (!csvText) {
      return { success: false, message: t("actions.csvRequired") }
    }

    // 解析 CSV：每行一个邮箱，格式 name,email 或仅 email
    const lines = csvText.split(/\r?\n/).filter((line) => line.trim().length > 0)
    const entries: Array<{ name?: string; email: string }> = []
    for (const line of lines) {
      const parts = line.split(",").map((p) => p.trim())
      if (parts.length === 1) {
        entries.push({ email: parts[0] })
      } else if (parts.length >= 2) {
        entries.push({ name: parts[0], email: parts[1] })
      }
    }

    if (entries.length === 0) {
      return { success: false, message: t("actions.noValidEntries") }
    }

    // 逐个注册（复用 enrollStudentByEmail data-access 逻辑）
    let imported = 0
    let failed = 0
    const errors: string[] = []

    for (const entry of entries) {
      try {
        await enrollStudentByEmail(classId, entry.email)
        imported += 1
      } catch (error) {
        failed += 1
        const msg = error instanceof Error ? error.message : "Unknown error"
        errors.push(`${entry.email}: ${msg}`)
      }
    }

    revalidatePath("/teacher/classes/students")
    revalidatePath("/admin/school/classes")
    return {
      success: true,
      message: t("actions.bulkImportResult", { imported, failed }),
      data: { imported, failed, errors },
    }
  } catch (e) {
    return handleActionError(e)
  }
}

/**
 * P2-4 批量分配教师：解析 CSV（每行 className,subject,teacherEmail）。
 * 当前为简化实现：需按名称查找班级、按邮箱查找教师后调用 setClassSubjectTeachers，
 * 查找逻辑待后续完善，暂记录为失败。
 */
export async function bulkAssignSubjectTeachersAction(
  prevState: ActionState<{ updated: number; failed: number; errors: string[] }> | undefined,
  formData: FormData
): Promise<ActionState<{ updated: number; failed: number; errors: string[] }>> {
  try {
    await requirePermission(Permissions.CLASS_UPDATE)
    const t = await getTranslations("classes")

    const csvText = String(formData.get("csv") ?? "").trim()
    if (!csvText) {
      return { success: false, message: t("actions.csvRequired") }
    }

    // 解析 CSV：格式 className,subject,teacherEmail
    const lines = csvText.split(/\r?\n/).filter((line) => line.trim().length > 0)
    const entries: Array<{ className: string; subject: string; teacherEmail: string }> = []

    for (const line of lines) {
      const parts = line.split(",").map((p) => p.trim())
      if (parts.length >= 3) {
        entries.push({ className: parts[0], subject: parts[1], teacherEmail: parts[2] })
      }
    }

    if (entries.length === 0) {
      return { success: false, message: t("actions.noValidEntries") }
    }

    const updated = 0
    let failed = 0
    const errors: string[] = []

    for (const entry of entries) {
      try {
        // TODO: 查找班级（按名称）与教师（按邮箱），调用 setClassSubjectTeachers 完成分配。
        // 当前版本暂未实现按名称/邮箱的查找逻辑，记录为失败。
        failed += 1
        errors.push(`${entry.className}/${entry.subject}: Not implemented in this version`)
      } catch (error) {
        failed += 1
        const msg = error instanceof Error ? error.message : "Unknown error"
        errors.push(`${entry.className}/${entry.subject}: ${msg}`)
      }
    }

    revalidatePath("/admin/school/classes")
    return {
      success: true,
      message: t("actions.bulkAssignResult", { updated, failed }),
      data: { updated, failed, errors },
    }
  } catch (e) {
    return handleActionError(e)
  }
}
