/**
 * 权限位图编解码（audit-P1-7）
 *
 * 目的：将 67 个权限点字符串数组（约 1.1KB JSON）压缩为 ~14 字符的 base36 字符串，
 * 使 JWT cookie 体积降至可接受范围（远低于 4KB 限制）。
 *
 * 编码方案：
 * - 权限点按 `PERMISSION_BITMAP_ORDER` 数组中的位置分配 bit（0-66）
 * - 67 bit 打包为 BigInt，转为 base36 字符串
 * - base36 字符集 [0-9a-z]，URL 安全，无需 padding
 *
 * 设计约束：
 * - `PERMISSION_BITMAP_ORDER` 必须与 `Permissions` 枚举值一一对应
 * - 顺序固定，新增权限只能追加到数组末尾（不破坏已有 bit 位映射）
 * - 编解码为纯函数，可在 edge runtime 使用（proxy.ts 直接调用）
 *
 * 注意：`BigInt` 不支持按 radix 解析字符串，需手工按位累加 base36 → BigInt。
 * 切勿用 `parseInt(bitmap, 36)` —— `Number` 仅支持 53 bit 精度，
 * 67 bit 的位图会丢失精度。
 */

import { Permissions, type Permission } from "@/shared/types/permissions"

const BASE36 = 36n
const BASE36_DIGITS = "0123456789abcdefghijklmnopqrstuvwxyz"

/**
 * 将 base36 字符串解析为 BigInt。
 * 输入仅接受 `[0-9a-z]`（大小写不敏感），无效字符抛出异常。
 */
function parseBase36ToBigInt(input: string): bigint {
  let result = 0n
  for (const ch of input.toLowerCase()) {
    const digit = BASE36_DIGITS.indexOf(ch)
    if (digit === -1) {
      throw new Error(`Invalid base36 character: ${ch}`)
    }
    result = result * BASE36 + BigInt(digit)
  }
  return result
}

/**
 * 权限位图的稳定顺序。
 *
 * **重要**：此数组的顺序一经确定即不可更改，新增权限只能追加到末尾。
 * 顺序变更会导致已签发的 JWT 中权限位错位。
 *
 * 当前包含 67 个权限点，覆盖 `Permissions` 枚举的全部值。
 */
export const PERMISSION_BITMAP_ORDER: readonly Permission[] = [
  // Exam
  Permissions.EXAM_CREATE,
  Permissions.EXAM_READ,
  Permissions.EXAM_UPDATE,
  Permissions.EXAM_DELETE,
  Permissions.EXAM_DUPLICATE,
  Permissions.EXAM_PUBLISH,
  Permissions.EXAM_AI_GENERATE,
  Permissions.EXAM_SUBMIT,
  // Homework
  Permissions.HOMEWORK_CREATE,
  Permissions.HOMEWORK_GRADE,
  Permissions.HOMEWORK_SUBMIT,
  // Question
  Permissions.QUESTION_CREATE,
  Permissions.QUESTION_READ,
  Permissions.QUESTION_UPDATE,
  Permissions.QUESTION_DELETE,
  // Textbook
  Permissions.TEXTBOOK_CREATE,
  Permissions.TEXTBOOK_READ,
  Permissions.TEXTBOOK_UPDATE,
  Permissions.TEXTBOOK_DELETE,
  // Class
  Permissions.CLASS_CREATE,
  Permissions.CLASS_READ,
  Permissions.CLASS_UPDATE,
  Permissions.CLASS_DELETE,
  Permissions.CLASS_ENROLL,
  Permissions.CLASS_SCHEDULE,
  // School management
  Permissions.SCHOOL_MANAGE,
  Permissions.GRADE_MANAGE,
  Permissions.USER_MANAGE,
  Permissions.USER_PROFILE_UPDATE,
  // AI
  Permissions.AI_CHAT,
  Permissions.AI_CONFIGURE,
  // Settings
  Permissions.SETTINGS_ADMIN,
  // Audit
  Permissions.AUDIT_LOG_READ,
  // Announcement
  Permissions.ANNOUNCEMENT_MANAGE,
  Permissions.ANNOUNCEMENT_READ,
  // Grade Record
  Permissions.GRADE_RECORD_MANAGE,
  Permissions.GRADE_RECORD_READ,
  // File
  Permissions.FILE_UPLOAD,
  Permissions.FILE_READ,
  Permissions.FILE_DELETE,
  // Course Plan
  Permissions.COURSE_PLAN_MANAGE,
  Permissions.COURSE_PLAN_READ,
  // Attendance
  Permissions.ATTENDANCE_MANAGE,
  Permissions.ATTENDANCE_READ,
  // Leave Request
  Permissions.LEAVE_REQUEST_CREATE,
  Permissions.LEAVE_REQUEST_READ,
  Permissions.LEAVE_REQUEST_REVIEW,
  // Message
  Permissions.MESSAGE_SEND,
  Permissions.MESSAGE_READ,
  Permissions.MESSAGE_DELETE,
  // Scheduling
  Permissions.SCHEDULE_AUTO,
  Permissions.SCHEDULE_ADJUST,
  // Elective
  Permissions.ELECTIVE_MANAGE,
  Permissions.ELECTIVE_READ,
  Permissions.ELECTIVE_SELECT,
  // Exam Proctoring
  Permissions.EXAM_PROCTOR,
  Permissions.EXAM_PROCTOR_READ,
  // Diagnostic
  Permissions.DIAGNOSTIC_MANAGE,
  Permissions.DIAGNOSTIC_READ,
  // Lesson Plan
  Permissions.LESSON_PLAN_CREATE,
  Permissions.LESSON_PLAN_READ,
  Permissions.LESSON_PLAN_UPDATE,
  Permissions.LESSON_PLAN_DELETE,
  Permissions.LESSON_PLAN_PUBLISH,
  // Dashboard
  Permissions.DASHBOARD_ADMIN_READ,
  Permissions.DASHBOARD_TEACHER_READ,
  Permissions.DASHBOARD_STUDENT_READ,
  Permissions.DASHBOARD_PARENT_READ,
  // Error Book
  Permissions.ERROR_BOOK_READ,
  Permissions.ERROR_BOOK_MANAGE,
  Permissions.ERROR_BOOK_ANALYTICS_READ,
  // Adaptive Practice
  Permissions.ADAPTIVE_PRACTICE_READ,
  Permissions.ADAPTIVE_PRACTICE_MANAGE,
  // RBAC
  Permissions.ROLE_CREATE,
  Permissions.ROLE_READ,
  Permissions.ROLE_UPDATE,
  Permissions.ROLE_DELETE,
  Permissions.ROLE_ASSIGN,
  Permissions.PERMISSION_READ,
] as const

/** 权限 → bit 位的映射表（懒构造，进程级复用） */
const permissionToBit = new Map<Permission, number>(
  PERMISSION_BITMAP_ORDER.map((p, i) => [p, i]),
)

/**
 * 将权限数组编码为 base36 位图字符串。
 *
 * 例如 admin 的 67 个权限编码后约为 14 字符的字符串，
 * 相比 JSON 数组的 ~1.1KB 体积减少约 99%。
 *
 * 未知权限（不在 `PERMISSION_BITMAP_ORDER` 中）会被静默忽略，
 * 避免新增权限但位图未更新时抛错。
 */
export function encodePermissionsBitmap(permissions: Permission[]): string {
  let bits = 0n
  for (const p of permissions) {
    const bit = permissionToBit.get(p)
    if (bit !== undefined) {
      bits |= 1n << BigInt(bit)
    }
  }
  if (bits === 0n) return "0"
  return bits.toString(36)
}

/**
 * 将 base36 位图字符串解码为权限数组。
 *
 * 无效或无法识别的字符返回空数组（容错处理）。
 */
export function decodePermissionsBitmap(bitmap: string): Permission[] {
  if (!bitmap || bitmap === "0") return []

  try {
    const bits = parseBase36ToBigInt(bitmap)
    if (bits === 0n) return []

    const result: Permission[] = []
    for (let i = 0; i < PERMISSION_BITMAP_ORDER.length; i++) {
      if ((bits & (1n << BigInt(i))) !== 0n) {
        result.push(PERMISSION_BITMAP_ORDER[i])
      }
    }
    return result
  } catch {
    return []
  }
}

/**
 * 检查位图中是否包含指定权限，无需完整解码。
 *
 * 用于 proxy.ts 等对性能敏感的场景，避免每次路由检查都解码全部权限。
 */
export function hasPermissionInBitmap(
  bitmap: string,
  permission: Permission,
): boolean {
  const bit = permissionToBit.get(permission)
  if (bit === undefined || !bitmap || bitmap === "0") return false

  try {
    const bits = parseBase36ToBigInt(bitmap)
    return (bits & (1n << BigInt(bit))) !== 0n
  } catch {
    return false
  }
}
