function ensurePublicKeyOptions(options) {
  if (!options || typeof options !== 'object') {
    throw new Error('Passkey参数无效')
  }
  return options.publicKey && typeof options.publicKey === 'object' ? options.publicKey : options
}

function base64UrlToUint8Array(base64url) {
  const value = String(base64url || '')
  const padding = '='.repeat((4 - (value.length % 4)) % 4)
  const base64 = (value + padding).replace(/-/g, '+').replace(/_/g, '/')
  const raw = window.atob(base64)
  const bytes = new Uint8Array(raw.length)
  for (let i = 0; i < raw.length; i += 1) {
    bytes[i] = raw.charCodeAt(i)
  }
  return bytes
}

function uint8ArrayToBase64Url(input) {
  const bytes = input instanceof ArrayBuffer ? new Uint8Array(input) : new Uint8Array(input || [])
  let binary = ''
  for (let i = 0; i < bytes.length; i += 1) {
    binary += String.fromCharCode(bytes[i])
  }
  return window
    .btoa(binary)
    .replace(/\+/g, '-')
    .replace(/\//g, '_')
    .replace(/=+$/g, '')
}

function toCreationOptions(rawOptions) {
  const options = ensurePublicKeyOptions(rawOptions)
  const normalized = {
    ...options,
    challenge: base64UrlToUint8Array(options.challenge),
    user: {
      ...options.user,
      id: base64UrlToUint8Array(options.user?.id),
    },
  }

  if (Array.isArray(options.excludeCredentials)) {
    normalized.excludeCredentials = options.excludeCredentials.map((item) => ({
      ...item,
      id: base64UrlToUint8Array(item.id),
    }))
  }

  return normalized
}

function serializeCredential(credential) {
  if (!credential) return null

  const response = credential.response || {}
  const output = {
    id: credential.id,
    rawId: uint8ArrayToBase64Url(credential.rawId),
    type: credential.type,
    authenticatorAttachment: credential.authenticatorAttachment || undefined,
    response: {},
  }

  if (response.clientDataJSON) {
    output.response.clientDataJSON = uint8ArrayToBase64Url(response.clientDataJSON)
  }
  if (response.attestationObject) {
    output.response.attestationObject = uint8ArrayToBase64Url(response.attestationObject)
  }
  if (response.authenticatorData) {
    output.response.authenticatorData = uint8ArrayToBase64Url(response.authenticatorData)
  }
  if (response.signature) {
    output.response.signature = uint8ArrayToBase64Url(response.signature)
  }

  if (response.userHandle) {
    output.response.userHandle = uint8ArrayToBase64Url(response.userHandle)
  } else {
    output.response.userHandle = null
  }

  if (typeof response.getTransports === 'function') {
    output.response.transports = response.getTransports() || []
  }

  return output
}

export function isPasskeyAvailable() {
  return typeof window !== 'undefined' && window.isSecureContext && !!window.PublicKeyCredential && !!navigator.credentials
}

function isMiuiBrowser() {
  const ua = String(window?.navigator?.userAgent || '')
  return /MiuiBrowser|XiaoMi\/MiuiBrowser/i.test(ua)
}

export function getPasskeyClientErrorMessage(error, actionLabel = 'Passkey操作') {
  const name = String(error?.name || '').trim()
  const message = String(error?.message || '').trim()

  if (name === 'NotAllowedError') {
    return `${actionLabel}未完成（可能已取消、超时或设备未响应）`
  }

  if (name === 'NotReadableError') {
    if (/credential manager/i.test(message) && isMiuiBrowser()) {
      return '当前小米浏览器与系统凭据管理器兼容性较差，请改用系统 Chrome 或 Edge 后重试。'
    }
    if (/credential manager/i.test(message)) {
      return '系统凭据管理器返回异常，请确认已设置系统锁屏并改用系统 Chrome/Edge 后重试。'
    }
    return message || `${actionLabel}失败（设备读取异常）`
  }

  if (name === 'SecurityError') {
    return '当前环境安全策略不满足 Passkey 要求，请确认使用 HTTPS 且证书有效。'
  }

  return message || `${actionLabel}失败`
}

export async function createPasskey(rawOptions) {
  const publicKey = toCreationOptions(rawOptions)
  const credential = await navigator.credentials.create({ publicKey })
  return serializeCredential(credential)
}
