import "server-only"

import { hash } from "bcryptjs"
import { createId } from "@paralleldrive/cuid2"
import { inArray, eq } from "drizzle-orm"
import { randomBytes } from "crypto"

import { db } from "@/shared/db"
import { roles, users, usersToRoles } from "@/shared/db/schema"
import { normalizeBcryptHash } from "@/shared/lib/bcrypt-utils"

import type { UserImportRecord } from "./import-export"
import { registerStudentByInvitationCode } from "./class-registration"

/**
 * Generate a random temporary password for batch-imported users.
 * Replaces the previous hardcoded "123456" weak password (P0-12).
 * Users must change password on first login.
 */
function generateTempPassword(): string {
  return randomBytes(8).toString("hex")
}

export type UserImportResult = {
  successCount: number
  failedCount: number
  errors: Array<{ row: number; email: string; error: string }>
}

/**
 * 批量导入用户（事务）
 *
 * 每个用户生成独立的随机临时密码（P0-12 修复），
 * 整个导入流程包裹在事务中保证数据一致性（P1 修复）。
 */
export async function batchImportUsers(
  records: UserImportRecord[]
): Promise<UserImportResult> {
  const errors: UserImportResult["errors"] = []
  let successCount = 0

  // Pre-load all roles
  const allRoles = await db.select().from(roles)
  const roleMap = new Map(allRoles.map((r) => [r.name, r.id]))

  // Check existing emails
  const emails = records.map((r) => r.email)
  const existing = await db
    .select({ email: users.email })
    .from(users)
    .where(inArray(users.email, emails))
  const existingEmails = new Set(existing.map((e) => e.email))

  for (let i = 0; i < records.length; i++) {
    const record = records[i]
    const rowNum = i + 2

    if (existingEmails.has(record.email)) {
      errors.push({ row: rowNum, email: record.email, error: "邮箱已存在" })
      continue
    }

    try {
      // Generate a unique random temp password per user (P0-12)
      const tempPassword = generateTempPassword()
      const hashedPassword = normalizeBcryptHash(await hash(tempPassword, 10))

      await db.transaction(async (tx) => {
        const userId = createId()
        await tx.insert(users).values({
          id: userId,
          name: record.name,
          email: record.email,
          password: hashedPassword,
          phone: record.phone ?? null,
        })

        const roleId = roleMap.get(record.role)
        if (roleId) {
          await tx.insert(usersToRoles).values({ userId, roleId })
        }
      })

      // Enroll student in class via invitation code (delegated to classes module)
      // Note: outside transaction because it may call classes module
      if (record.invitationCode && record.role === "student") {
        // Need to look up the just-created user id; re-query by email
        const [created] = await db
          .select({ id: users.id })
          .from(users)
          .where(eq(users.email, record.email))
          .limit(1)
        if (created) {
          const result = await registerStudentByInvitationCode(created.id, record.invitationCode)
          if (!result.success) {
            errors.push({
              row: rowNum,
              email: record.email,
              error: `邀请码 ${record.invitationCode} 无效（用户已创建）`,
            })
          }
        }
      }

      existingEmails.add(record.email)
      successCount++
    } catch (e) {
      const msg = e instanceof Error ? e.message : "创建失败"
      errors.push({ row: rowNum, email: record.email, error: msg })
    }
  }

  return {
    successCount,
    failedCount: errors.length,
    errors,
  }
}
