"use server"

import { z } from "zod"
import { getTranslations } from "next-intl/server"

import type { ActionState } from "@/shared/types/action-state"
import { rateLimit, rateLimitKey } from "@/shared/lib/rate-limit"
import { resolveClientIp } from "@/shared/lib/http-utils"
import { logLoginEvent } from "@/shared/lib/login-logger"
import { trackAuthEvent, trackEvent } from "@/shared/lib/track-event"
import { checkBreachedPassword } from "@/shared/lib/breached-password"

import {
  validateInvitationCode,
  consumeInvitationCode,
} from "@/modules/invitation-codes/data-access"
import { RegisterSchema } from "./schema"
import { buildRegisterInput, createUser, isEmailAvailable } from "./data-access"
import { preflightTwoFactorByEmail } from "./services/two-factor-service"
import {
  REGISTER_ERROR_CODES,
  type RegisterErrorCode,
  type RegisterResult,
} from "./types"

/**
 * 注册速率限制规则。
 *
 * 独立于 LOGIN 限制，避免恶意用户通过注册接口枚举邮箱或爆破邀请码。
 * 规则：15 分钟内最多 5 次（与 LOGIN 一致，便于运维记忆）。
 */
const REGISTER_RATE_LIMIT = {
  limit: 5,
  windowMs: 15 * 60 * 1000,
} as const

/**
 * 将原始数据库/系统错误归一化为 RegisterErrorCode。
 *
 * 不向客户端暴露 SQL 文案、表名或字段名等敏感信息。
 */
function classifyRegisterError(error: unknown): RegisterErrorCode {
  if (error instanceof Error) {
    const msg = error.message.toLowerCase()
    if (error.message === "DEFAULT_ROLE_NOT_FOUND") {
      return REGISTER_ERROR_CODES.DEFAULT_ROLE_MISSING
    }
    // 捕获 MySQL 唯一索引冲突（不暴露原始 sqlMessage）
    if (
      msg.includes("duplicate") ||
      msg.includes("unique") ||
      msg.includes("er_dup_entry")
    ) {
      return REGISTER_ERROR_CODES.EMAIL_TAKEN
    }
  }
  return REGISTER_ERROR_CODES.UNKNOWN
}

/**
 * P1-1: 通过 next-intl 服务端翻译查找错误消息。
 * i18n 命名空间：`auth.register.errors.{errorCode}`
 */
async function messageForError(
  code: RegisterErrorCode
): Promise<string> {
  const t = await getTranslations("auth.register")
  return t(`errors.${code}`)
}

/**
 * 注册 Server Action（P0-1：从 register/page.tsx 内联实现下沉到模块层）。
 *
 * 安全设计：
 * - Zod 校验输入（含未成年人监护人条件校验）
 * - 独立速率限制 key `register:{ip}`，与 LOGIN 限制隔离
 * - 注册前查询邮箱可用性（不暴露 userId）
 * - 数据库写入委托给 data-access.createUser（不直接操作 schema）
 * - 成功/失败均写入 loginLogs 审计日志（action="signup"）
 * - 错误响应只返回结构化 errorCode，不暴露 SQL/堆栈信息
 *
 * 返回值：
 * - 成功：`{ success: true, data: { userId } }`（userId 仅用于内部跟踪，前端可忽略）
 * - 失败：`{ success: false, errorCode, message }`
 */
export async function registerAction(
  formData: FormData
): Promise<ActionState<RegisterResult>> {
  // 1) 速率限制（独立 key，避免与登录失败计数叠加）
  const ip = await resolveClientIp()
  const limitKey = rateLimitKey("register", ip)
  const limitResult = await rateLimit({
    key: limitKey,
    limit: REGISTER_RATE_LIMIT.limit,
    windowMs: REGISTER_RATE_LIMIT.windowMs,
  })

  if (!limitResult.success) {
    // 速率限制命中也记录日志，便于运维识别攻击
    const email = String(formData.get("email") ?? "")
    await logLoginEvent({
      userEmail: email,
      action: "signup",
      status: "failure",
      errorMessage: "RATE_LIMIT_EXCEEDED",
    }).catch(() => {
      /* 日志失败不影响主流程 */
    })

    return {
      success: false,
      errorCode: REGISTER_ERROR_CODES.RATE_LIMIT_EXCEEDED,
      message: await messageForError(REGISTER_ERROR_CODES.RATE_LIMIT_EXCEEDED),
    }
  }

  // 2) 提取并校验表单字段
  const raw = {
    name: String(formData.get("name") ?? ""),
    email: String(formData.get("email") ?? ""),
    password: String(formData.get("password") ?? ""),
    birthDate: String(formData.get("birthDate") ?? ""),
    guardianName: String(formData.get("guardianName") ?? ""),
    guardianPhone: String(formData.get("guardianPhone") ?? ""),
    guardianRelation: String(formData.get("guardianRelation") ?? ""),
    agreedTerms: formData.get("agreedTerms") === "true",
    agreedGuardian: formData.get("agreedGuardian") === "true",
    // audit-P2-3: 邀请码字段（可选）
    invitationCode: String(formData.get("invitationCode") ?? ""),
  }

  const parsed = RegisterSchema.safeParse(raw)
  if (!parsed.success) {
    return {
      success: false,
      errorCode: REGISTER_ERROR_CODES.VALIDATION_FAILED,
      message: await messageForError(REGISTER_ERROR_CODES.VALIDATION_FAILED),
      errors: parsed.error.flatten().fieldErrors,
    }
  }

  const data = parsed.data
  const normalizedEmail = data.email.toLowerCase()

  // 3) 邮箱可用性预检查（避免直接触发数据库唯一约束，且能给出更友好错误码）
  const available = await isEmailAvailable(normalizedEmail)
  if (!available) {
    await logLoginEvent({
      userEmail: normalizedEmail,
      action: "signup",
      status: "failure",
      errorMessage: "EMAIL_TAKEN",
    }).catch(() => {
      /* ignore */
    })

    return {
      success: false,
      errorCode: REGISTER_ERROR_CODES.EMAIL_TAKEN,
      message: await messageForError(REGISTER_ERROR_CODES.EMAIL_TAKEN),
    }
  }

  // audit-P2-4: Breached password 检测（HIBP k-anonymity API）
  // fail-open 策略：API 不可用时 checkSkipped=true，不阻断注册流程
  const breachCheck = await checkBreachedPassword(data.password)
  if (breachCheck.isBreached) {
    await logLoginEvent({
      userEmail: normalizedEmail,
      action: "signup",
      status: "failure",
      errorMessage: "BREACHED_PASSWORD",
    }).catch(() => {
      /* ignore */
    })

    return {
      success: false,
      errorCode: REGISTER_ERROR_CODES.BREACHED_PASSWORD,
      message: await messageForError(REGISTER_ERROR_CODES.BREACHED_PASSWORD),
    }
  }

  // audit-P2-3: 邀请码校验（可选）
  // 若提供邀请码，校验通过后使用邀请码中的 role 覆盖默认 student。
  // 校验失败返回结构化错误码（不暴露具体失败原因防枚举）。
  let invitedRole: string | undefined
  let invitedClassId: string | undefined
  let invitationCodeForConsume: string | undefined

  if (data.invitationCode && data.invitationCode.length > 0) {
    const codeValue = data.invitationCode.toUpperCase()
    invitationCodeForConsume = codeValue
    const validationResult = await validateInvitationCode(codeValue, normalizedEmail)

    if (!validationResult.valid) {
      await logLoginEvent({
        userEmail: normalizedEmail,
        action: "signup",
        status: "failure",
        errorMessage: "INVITATION_CODE_INVALID",
      }).catch(() => {
        /* ignore */
      })

      return {
        success: false,
        errorCode: REGISTER_ERROR_CODES.INVITATION_CODE_INVALID,
        message: await messageForError(REGISTER_ERROR_CODES.INVITATION_CODE_INVALID),
      }
    }

    if (validationResult.code) {
      invitedRole = validationResult.code.role
      invitedClassId = validationResult.code.classId ?? undefined
    }
  }

  // 4) 调用 data-access 创建用户（含角色分配，但不自动创建角色）
  const input = buildRegisterInput({
    name: data.name,
    email: normalizedEmail,
    password: data.password,
    birthDate: data.birthDate && data.birthDate.length > 0 ? data.birthDate : null,
    guardianName: data.guardianName ?? "",
    guardianPhone: data.guardianPhone ?? "",
    guardianRelation: data.guardianRelation ?? "",
    role: invitedRole,
    classId: invitedClassId,
  })

  const t = await getTranslations("auth.register")

  try {
    const result = await createUser(input)

    // audit-P2-3: 注册成功后标记邀请码为已使用（乐观锁，避免并发使用）
    // 使用失败不阻断注册流程（用户已创建），仅记录日志
    if (invitationCodeForConsume) {
      const consumed = await consumeInvitationCode({
        code: invitationCodeForConsume,
        email: normalizedEmail,
        userId: result.userId,
      }).catch(() => false)

      if (!consumed) {
        // 邀请码已被他人使用（极端并发场景）
        console.warn(
          `[register] Invitation code ${invitationCodeForConsume} was already consumed by another user (email=${normalizedEmail})`,
        )
      } else {
        void trackEvent({
          event: "invitation_codes.consumed",
          userId: result.userId,
          properties: {
            email: normalizedEmail,
            role: invitedRole,
            hasClassId: Boolean(invitedClassId),
          },
        })
      }
    }

    // 5) 成功审计日志
    await logLoginEvent({
      userId: result.userId,
      userEmail: normalizedEmail,
      action: "signup",
      status: "success",
    }).catch(() => {
      /* 日志失败不影响注册成功 */
    })

    // audit-P1-9：注册成功埋点（用于注册转化率、注册渠道分析）
    // 非阻塞：trackEvent 内部已吞掉异常
    await trackAuthEvent("auth.signup", {
      userId: result.userId,
      properties: {
        email: normalizedEmail,
        role: input.role,
        isMinor: input.isMinor,
        usedInvitationCode: Boolean(invitationCodeForConsume),
      },
    })

    return {
      success: true,
      data: result,
      message: t("toast.createSuccess"),
    }
  } catch (error) {
    // 6) 错误分类（不向客户端暴露原始错误信息）
    const errorCode = classifyRegisterError(error)

    await logLoginEvent({
      userEmail: normalizedEmail,
      action: "signup",
      status: "failure",
      errorMessage: errorCode,
    }).catch(() => {
      /* ignore */
    })

    return {
      success: false,
      errorCode,
      message: await messageForError(errorCode),
    }
  }
}

/**
 * 邮箱可用性查询 Server Action。
 *
 * 用于注册表单实时检查邮箱是否已被占用。限流复用 REGISTER 规则，
 * 避免被用于邮箱枚举攻击。
 */
export async function checkEmailAvailabilityAction(
  email: string
): Promise<ActionState<{ available: boolean }>> {
  const ip = await resolveClientIp()
  const limitKey = rateLimitKey("register", ip)
  const limitResult = await rateLimit({
    key: limitKey,
    limit: REGISTER_RATE_LIMIT.limit,
    windowMs: REGISTER_RATE_LIMIT.windowMs,
  })

  if (!limitResult.success) {
    return {
      success: false,
      errorCode: REGISTER_ERROR_CODES.RATE_LIMIT_EXCEEDED,
      message: await messageForError(REGISTER_ERROR_CODES.RATE_LIMIT_EXCEEDED),
    }
  }

  const emailSchema = z.string().trim().email()
  const parsed = emailSchema.safeParse(email)
  if (!parsed.success) {
    const t = await getTranslations("auth.register")
    return {
      success: false,
      errorCode: REGISTER_ERROR_CODES.VALIDATION_FAILED,
      message: t("emailFormatError"),
    }
  }

  const available = await isEmailAvailable(parsed.data.toLowerCase())
  return {
    success: true,
    data: { available },
  }
}

/**
 * 2FA 预检 Server Action（薄封装，供 LoginForm 客户端组件调用）。
 *
 * 委托给 `services/two-factor-service.preflightTwoFactorByEmail`，
 * LoginForm 不再跨模块依赖 `modules/settings/actions-security`，
 * 符合 audit-P0-2 同模块依赖原则。
 *
 * 返回值：
 * - `{ required: true }`  — 用户启用了 2FA，登录表单应展示 2FA 输入框
 * - `{ required: false }`  — 用户未启用 2FA 或不存在（防邮箱枚举）
 */
export async function preflightTwoFactorAction(
  email: string
): Promise<{ required: boolean }> {
  return preflightTwoFactorByEmail(email)
}
