/**
 * 
 * @file fido2-ui-sdk.js
 * @description FIDO2 UI SDK of amipro FIDO2 Server
 * @version 2025-12-12
 * @author Amipro Co., Ltd. (https://www.amipro.me/)
 * @license Copyright (c) Amipro Co., Ltd. All rights reserved.
 */

(function(window) {
  'use strict';

  // Check required dependencies
  if (typeof authenticateFido2 === 'undefined' || 
      typeof registerFido2 === 'undefined' ||
      typeof listUserDevicesFido2 === 'undefined' ||
      typeof delUserDeviceFido2 === 'undefined' ||
      typeof setFidoServerURL === 'undefined' ||
      typeof logoutFido2UserSession === 'undefined') {
    throw new Error(
      'FIDO2 UI SDK requires dfido2-lib.js to be loaded first. ' +
      'Please add: <script src="files/dfido2-lib.js"></script> before fido2-ui-sdk.js'
    );
  }

  const FIDO2_UI_VERSION = '1.0.0';

  const DEFAULT_CONFIG = {
    serverUrl: 'https://fido2.amipro.me',
    mode: 'modal',
    container: null,
    theme: {
      logo: null,
      primaryColor: '#696cff',
      backgroundColor: '#ffffff',
      textColor: '#333333',
      borderRadius: '8px',
    },
    language: 'zh-CN',
    customI18n: {},
    features: {
      showAddButton: true,
      showDeleteButton: true,
      showUserInfo: true,
      showSessionStatus: true,
      showRecoveryButton: true,
    },
    callbacks: {},
    rpId: null,
    autoRefresh: true,
    refreshInterval: 5000,
    recoverySessionId: null,
  };

  const DEFAULT_I18N = {
    'my_devices': {
      'en-US': 'My devices',
      'zh-CN': '我的设备',
      'ja': 'マイデバイス',
      'es': 'Mis dispositivos',
      'de': 'Meine Geräte',
      'fr': 'Mes appareils',
      'pt': 'Meus dispositivos',
      'ko': '내 장치',
      'ru': 'Мои устройства',
      'it': 'I miei dispositivi'
    },
    'btn_add': {
      'en-US': 'Add device',
      'zh-CN': '添加设备',
      'ja': 'デバイスを追加',
      'es': 'Añadir dispositivo',
      'de': 'Gerät hinzufügen',
      'fr': 'Ajouter un appareil',
      'pt': 'Adicionar dispositivo',
      'ko': '장치 추가',
      'ru': 'Добавить устройство',
      'it': 'Aggiungi dispositivo'
    },
    'title_device': {
      'en-US': 'Device',
      'zh-CN': '设备',
      'ja': 'デバイス',
      'es': 'Dispositivo',
      'de': 'Gerät',
      'fr': 'Appareil',
      'pt': 'Dispositivo',
      'ko': '장치',
      'ru': 'Устройство',
      'it': 'Dispositivo'
    },
    'title_time': {
      'en-US': 'Registered time',
      'zh-CN': '添加时间',
      'ja': '登録時間',
      'es': 'Hora de registro',
      'de': 'Registrierungszeit',
      'fr': 'Heure d\'inscription',
      'pt': 'Hora do registro',
      'ko': '등록 시간',
      'ru': 'Время регистрации',
      'it': 'Ora di registrazione'
    },
    'title_act': {
      'en-US': 'Actions',
      'zh-CN': '操作',
      'ja': '操作',
      'es': 'Acciones',
      'de': 'Aktionen',
      'fr': 'Actions',
      'pt': 'Ações',
      'ko': '작업',
      'ru': 'Действия',
      'it': 'Azioni'
    },
    'title_del': {
      'en-US': 'Delete',
      'zh-CN': '删除',
      'ja': '削除',
      'es': 'Eliminar',
      'de': 'Löschen',
      'fr': 'Supprimer',
      'pt': 'Excluir',
      'ko': '삭제',
      'ru': 'Удалить',
      'it': 'Elimina'
    },
    'title_logout': {
      'en-US': 'Log out',
      'zh-CN': '登出',
      'ja': 'ログアウト',
      'es': 'Cerrar sesión',
      'de': 'Abmelden',
      'fr': 'Déconnexion',
      'pt': 'Sair',
      'ko': '로그아웃',
      'ru': 'Выйти',
      'it': 'Disconnetti'
    },
    'title_empty_list': {
      'en-US': 'No devices, please add.',
      'zh-CN': '无设备，请添加。',
      'ja': 'デバイスがなし、追加してください。',
      'es': 'Sin dispositivos, por favor añada.',
      'de': 'Keine Geräte, bitte hinzufügen.',
      'fr': 'Pas d\'appareils, s\'il vous plaît ajouter.',
      'pt': 'Sem dispositivos, por favor adicione.',
      'ko': '장치가 없습니다. 추가해 주세요.',
      'ru': 'Нет устройств, пожалуйста добавьте.',
      'it': 'Nessun dispositivo, si prega di aggiungere.'
    },
    'msg_register_ok': {
      'en-US': 'Device registered successfully',
      'zh-CN': '添加设备成功',
      'ja': 'デバイス登録完了',
      'es': 'Dispositivo registrado exitosamente',
      'de': 'Gerät erfolgreich registriert',
      'fr': 'Appareil enregistré avec succès',
      'pt': 'Dispositivo registrado com sucesso',
      'ko': '장치 등록 완료',
      'ru': 'Устройство успешно зарегистрировано',
      'it': 'Dispositivo registrato con successo'
    },
    'msg_deldev_ok': {
      'en-US': 'Device deleted successfully',
      'zh-CN': '设备删除成功',
      'ja': 'デバイスを削除しました',
      'es': 'Dispositivo eliminado exitosamente',
      'de': 'Gerät erfolgreich gelöscht',
      'fr': 'Appareil supprimé avec succès',
      'pt': 'Dispositivo excluído com sucesso',
      'ko': '장치 삭제 완료',
      'ru': 'Устройство успешно удалено',
      'it': 'Dispositivo eliminato con successo'
    },
    'msg_confirm_deldev': {
      'en-US': 'Do you want to delete this device?',
      'zh-CN': '确认删除此设备吗？',
      'ja': 'デバイスを削除しますか？',
      'es': '¿Desea eliminar este dispositivo?',
      'de': 'Möchten Sie dieses Gerät löschen?',
      'fr': 'Voulez-vous supprimer cet appareil?',
      'pt': 'Deseja excluir este dispositivo?',
      'ko': '이 장치를 삭제하시겠습니까?',
      'ru': 'Хотите удалить это устройство?',
      'it': 'Vuoi eliminare questo dispositivo?'
    },
    'msg_session_status_ok': {
      'en-US': 'FIDO2 session is valid',
      'zh-CN': 'FIDO2会话正常',
      'ja': 'FIDO2セッションは正常です',
      'es': 'La sesión FIDO2 es válida',
      'de': 'FIDO2-Sitzung ist gültig',
      'fr': 'La session FIDO2 est valide',
      'pt': 'A sessão FIDO2 é válida',
      'ko': 'FIDO2 세션이 정상입니다',
      'ru': 'Сессия FIDO2 действительна',
      'it': 'La sessione FIDO2 è valida'
    },
    'msg_session_status_fail': {
      'en-US': 'FIDO2 session is invalid',
      'zh-CN': 'FIDO2会话无效',
      'ja': 'FIDO2セッションは無効です',
      'es': 'La sesión FIDO2 no es válida',
      'de': 'FIDO2-Sitzung ist ungültig',
      'fr': 'La session FIDO2 n\'est pas valide',
      'pt': 'A sessão FIDO2 é inválida',
      'ko': 'FIDO2 세션이 무효합니다',
      'ru': 'Сессия FIDO2 недействительна',
      'it': 'La sessione FIDO2 non è valida'
    },
    'btn_close': {
      'en-US': 'Close',
      'zh-CN': '关闭',
      'ja': '閉じる',
      'es': 'Cerrar',
      'de': 'Schließen',
      'fr': 'Fermer',
      'pt': 'Fechar',
      'ko': '닫기',
      'ru': 'Закрыть',
      'it': 'Chiudi'
    },
    'title_welcome': {
      'en-US': 'Welcome',
      'zh-CN': '欢迎',
      'ja': 'ようこそ',
      'es': 'Bienvenido',
      'de': 'Willkommen',
      'fr': 'Bienvenue',
      'pt': 'Bem-vindo',
      'ko': '환영합니다',
      'ru': 'Добро пожаловать',
      'it': 'Benvenuto'
    },
    'btn_login': {
      'en-US': 'Login',
      'zh-CN': '重新登录',
      'ja': 'ログイン',
      'es': 'Iniciar sesión',
      'de': 'Anmelden',
      'fr': 'Connexion',
      'pt': 'Login',
      'ko': '로그인',
      'ru': 'Войти',
      'it': 'Accedi'
    },
    'msg_session_invalid': {
      'en-US': 'Session expired, please login again',
      'zh-CN': '会话已过期，请重新登录',
      'ja': 'セッション切れ、再ログインしてください',
      'es': 'Sesión vencida, por favor inicie sesión nuevamente',
      'de': 'Sitzung abgelaufen, bitte erneut anmelden',
      'fr': 'Session expirée, veuillez vous connecter à nouveau',
      'pt': 'Sessão expirada, por favor faça login novamente',
      'ko': '세션이 만료되었습니다. 다시 로그인해 주세요.',
      'ru': 'Сессия истекла, пожалуйста войдите снова',
      'it': 'Sessione scaduta, si prega di accedere nuovamente'
    },
    'title_login': {
      'en-US': 'Login',
      'zh-CN': '登录',
      'ja': 'ログイン',
      'es': 'Iniciar sesión',
      'de': 'Anmelden',
      'fr': 'Connexion',
      'pt': 'Login',
      'ko': '로그인',
      'ru': 'Войти',
      'it': 'Accedi'
    },
    'title_password_login': {
      'en-US': 'Password Login',
      'zh-CN': '密码登录',
      'ja': 'パスワードログイン',
      'es': 'Iniciar sesión con contraseña',
      'de': 'Anmeldung mit Passwort',
      'fr': 'Connexion par mot de passe',
      'pt': 'Login com senha',
      'ko': '비밀번호 로그인',
      'ru': 'Вход по паролю',
      'it': 'Accesso con password'
    },
    'btn_fido2_login': {
      'en-US': 'Use Passkey',
      'zh-CN': '使用Passkey登录',
      'ja': 'Passkeyを使用',
      'es': 'Usar Passkey',
      'de': 'Passkey verwenden',
      'fr': 'Utiliser Passkey',
      'pt': 'Usar Passkey',
      'ko': 'Passkey 사용',
      'ru': 'Использовать Passkey',
      'it': 'Usa Passkey'
    },
    'btn_password_login': {
      'en-US': 'Password Login',
      'zh-CN': '密码登录',
      'ja': 'パスワードログイン',
      'es': 'Iniciar sesión con contraseña',
      'de': 'Anmeldung mit Passwort',
      'fr': 'Connexion par mot de passe',
      'pt': 'Login com senha',
      'ko': '비밀번호 로그인',
      'ru': 'Вход по паролю',
      'it': 'Accesso con password'
    },
    'link_use_password': {
      'en-US': 'Use password instead',
      'zh-CN': '使用密码登录',
      'ja': 'パスワードを使用',
      'es': 'Usar contraseña',
      'de': 'Passwort verwenden',
      'fr': 'Utiliser mot de passe',
      'pt': 'Usar senha',
      'ko': '비밀번호 사용',
      'ru': 'Использовать пароль',
      'it': 'Usa password'
    },
    'link_use_fido2': {
      'en-US': 'Use Passkey instead',
      'zh-CN': '使用Passkey登录',
      'ja': 'Passkeyを使用',
      'es': 'Usar Passkey',
      'de': 'Passkey verwenden',
      'fr': 'Utiliser Passkey',
      'pt': 'Usar Passkey',
      'ko': 'Passkey 사용',
      'ru': 'Использовать Passkey',
      'it': 'Usa Passkey'
    },
    'placeholder_user_id': {
      'en-US': 'Enter User ID',
      'zh-CN': '请输入用户ID',
      'ja': 'ユーザーIDを入力',
      'es': 'Ingrese ID de usuario',
      'de': 'Benutzer-ID eingeben',
      'fr': 'Entrez l\'ID utilisateur',
      'pt': 'Digite o ID do usuário',
      'ko': '사용자 ID 입력',
      'ru': 'Введите ID пользователя',
      'it': 'Inserisci ID utente'
    },
    'placeholder_password': {
      'en-US': 'Enter Password',
      'zh-CN': '请输入密码',
      'ja': 'パスワードを入力',
      'es': 'Ingrese contraseña',
      'de': 'Passwort eingeben',
      'fr': 'Entrez le mot de passe',
      'pt': 'Digite a senha',
      'ko': '비밀번호 입력',
      'ru': 'Введите пароль',
      'it': 'Inserisci password'
    },
    'msg_remaining_attempts': {
      'en-US': 'Remaining attempts: {n}',
      'zh-CN': '剩余尝试次数: {n}',
      'ja': '残り{n}回',
      'es': 'Intentos restantes: {n}',
      'de': 'Verbleibende Versuche: {n}',
      'fr': 'Tentatives restantes: {n}',
      'pt': 'Tentativas restantes: {n}',
      'ko': '남은 시도: {n}',
      'ru': 'Осталось попыток: {n}',
      'it': 'Tentativi rimanenti: {n}'
    },
    'msg_password_exhausted': {
      'en-US': 'Too many failed attempts, login closed',
      'zh-CN': '尝试次数过多，登录已关闭',
      'ja': '試行回数超過、ログイン閉鎖',
      'es': 'Demasiados intentos fallidos, inicio de sesión cerrado',
      'de': 'Zu viele fehlgeschlagene Versuche, Anmeldung geschlossen',
      'fr': 'Trop de tentatives échouées, connexion fermée',
      'pt': 'Muitas tentativas falharam, login fechado',
      'ko': '시도 횟수 초과, 로그인 종료',
      'ru': 'Слишком много неудачных попыток, вход закрыт',
      'it': 'Troppi tentativi falliti, accesso chiuso'
    },
    'title_password_exhausted': {
      'en-US': 'Login Failed',
      'zh-CN': '登录失败',
      'ja': 'ログイン失敗',
      'es': 'Error de inicio de sesión',
      'de': 'Anmeldung fehlgeschlagen',
      'fr': 'Échec de la connexion',
      'pt': 'Falha no login',
      'ko': '로그인 실패',
      'ru': 'Ошибка входа',
      'it': 'Accesso fallito'
    },
    'msg_no_registration': {
      'en-US': 'No device registered',
      'zh-CN': '暂未注册设备',
      'ja': 'デバイス未登録',
      'es': 'No hay dispositivo registrado',
      'de': 'Kein Gerät registriert',
      'fr': 'Aucun appareil enregistré',
      'pt': 'Nenhum dispositivo registrado',
      'ko': '등록된 장치 없음',
      'ru': 'Устройство не зарегистрировано',
      'it': 'Nessun dispositivo registrato'
    },
    'msg_fido2_failed': {
      'en-US': 'Passkey login failed',
      'zh-CN': 'Passkey登录失败',
      'ja': 'Passkeyログイン失敗',
      'es': 'Error de inicio de sesión con Passkey',
      'de': 'Passkey-Anmeldung fehlgeschlagen',
      'fr': 'Échec de la connexion Passkey',
      'pt': 'Falha no login com Passkey',
      'ko': 'Passkey 로그인 실패',
      'ru': 'Ошибка входа Passkey',
      'it': 'Accesso Passkey fallito'
    },
    'msg_fido2_canceled': {
      'en-US': 'Login was canceled',
      'zh-CN': '登录已取消',
      'ja': 'ログインキャンセル',
      'es': 'Inicio de sesión cancelado',
      'de': 'Anmeldung abgebrochen',
      'fr': 'Connexion annulée',
      'pt': 'Login cancelado',
      'ko': '로그인 취소됨',
      'ru': 'Вход отменён',
      'it': 'Accesso annullato'
    },
    'error_invalid_state': {
      'en-US': 'The authenticator contains credentials that are already registered with this website.',
      'zh-CN': '您的认证器包含已在此网站注册的凭据。',
      'ja': 'この認証デバイスには、すでにこの网站に登録されている憑拠が含まれています。',
      'es': 'El autenticador contiene credenciales que ya están registradas en este sitio web.',
      'de': 'Der Authenticator enthält Anmeldeinformationen, die bereits auf dieser Website registriert sind.',
      'fr': 'L\'authentificateur contient des identifiants déjà enregistrés sur ce site Web.',
      'pt': 'O autenticador contém credenciais já registradas neste site.',
      'ko': '인증기에 이미 이 웹사이트에 등록된 자격 증명이 포함되어 있습니다.',
      'ru': 'Аутентификатор содержит учетные данные, уже зарегистрированные на этом веб-сайте.',
      'it': 'L\'autenticatore contiene credenziali già registrate su questo sito web.'
    },
    'error_not_allowed': {
      'en-US': 'The operation was not allowed.',
      'zh-CN': '操作不被允许。',
      'ja': '操作が許可されませんでした。',
      'es': 'La operación no fue permitida.',
      'de': 'Der Vorgang wurde nicht zugelassen.',
      'fr': 'L\'opération n\'a pas été autorisée.',
      'pt': 'A operação não foi permitida.',
      'ko': '작업이 허용되지 않았습니다.',
      'ru': 'Операция не была разрешена.',
      'it': 'L\'operazione non è stata consentita.'
    },
    'error_abort': {
      'en-US': 'The operation was aborted.',
      'zh-CN': '操作已中止。',
      'ja': '操作が中止されました。',
      'es': 'La operación fue abortada.',
      'de': 'Der Vorgang wurde abgebrochen.',
      'fr': 'L\'opération a été abandonnée.',
      'pt': 'A operação foi abortada.',
      'ko': '작업이 중단되었습니다.',
      'ru': 'Операция была прервана.',
      'it': 'L\'operazione è stata interrotta.'
    },
    'error_not_supported': {
      'en-US': 'This operation is not supported on your device.',
      'zh-CN': '您的设备不支持此操作。',
      'ja': 'この機器ではこの操作はサポートされていません。',
      'es': 'Esta operación no es compatible con su dispositivo.',
      'de': 'Dieser Vorgang wird auf Ihrem Gerät nicht unterstützt.',
      'fr': 'Cette opération n\'est pas prise en charge sur votre appareil.',
      'pt': 'Esta operação não é suportada no seu dispositivo.',
      'ko': '이 작업은 해당 장치에서 지원되지 않습니다.',
      'ru': 'Эта операция не поддерживается на вашем устройстве.',
      'it': 'Questa operazione non è supportata sul tuo dispositivo.'
    },
    'error_security': {
      'en-US': 'A security error occurred.',
      'zh-CN': '发生安全错误。',
      'ja': 'セキュリティエラーが発生しました。',
      'es': 'Ocurrió un error de seguridad.',
      'de': 'Ein Sicherheitsfehler ist aufgetreten.',
      'fr': 'Une erreur de sécurité s\'est produite.',
      'pt': 'Ocorreu um erro de segurança.',
      'ko': '보안 오류가 발생했습니다.',
      'ru': 'Произошла ошибка безопасности.',
      'it': 'Si è verificato un errore di sicurezza.'
    },
    'error_network': {
      'en-US': 'A network error occurred.',
      'zh-CN': '发生网络错误。',
      'ja': 'ネットワークエラーが発生しました。',
      'es': 'Ocurrió un error de red.',
      'de': 'Ein Netzwerkfehler ist aufgetreten.',
      'fr': 'Une erreur de réseau s\'est produite.',
      'pt': 'Ocorreu um erro de rede.',
      'ko': '네트워크 오류가 발생했습니다.',
      'ru': 'Произошла ошибка сети.',
      'it': 'Si è verificato un errore di rete.'
    },
    'error_constraint': {
      'en-US': 'The operation failed due to a constraint violation.',
      'zh-CN': '由于约束冲突，操作失败。',
      'ja': '制約違反のため操作に失敗しました。',
      'es': 'La operación falló debido a una violación de restricción.',
      'de': 'Der Vorgang ist aufgrund einer Einschränkungsverletzung fehlgeschlagen.',
      'fr': 'L\'opération a échoué en raison d\'une violation de contrainte.',
      'pt': 'A operação falhou devido a uma violação de restrição.',
      'ko': '제약 조건 위반으로 인해 작업이 실패했습니다.',
      'ru': 'Операция не удалась из-за нарушения ограничения.',
      'it': 'L\'operazione non riuscita a causa di una violazione del vincolo.'
    },
    'error_not_readable': {
      'en-US': 'Could not read the credential.',
      'zh-CN': '无法读取凭据。',
      'ja': '憑拠を読み取れませんでした。',
      'es': 'No se pudo leer la credencial.',
      'de': 'Die Anmeldeinformation konnte nicht gelesen werden.',
      'fr': 'Impossible de lire les identifiants.',
      'pt': 'Não foi possível ler a credencial.',
      'ko': '자격 증명을 읽을 수 없습니다.',
      'ru': 'Не удалось прочитать учетные данные.',
      'it': 'Impossibile leggere le credenziali.'
    },
    'error_encoding': {
      'en-US': 'The data format is invalid.',
      'zh-CN': '数据格式无效。',
      'ja': 'データ形式が無効です。',
      'es': 'El formato de datos no es válido.',
      'de': 'Das Datenformat ist ungültig.',
      'fr': 'Le format de données n\'est pas valide.',
      'pt': 'O formato de dados é inválido.',
      'ko': '데이터 형식이 잘못되었습니다.',
      'ru': 'Формат данных недействителен.',
      'it': 'Il formato dati non è valido.'
    },
    'error_data': {
      'en-US': 'A data error occurred.',
      'zh-CN': '发生数据错误。',
      'ja': 'データエラーが発生しました。',
      'es': 'Ocurrió un error de datos.',
      'de': 'Ein Datenfehler ist aufgetreten.',
      'fr': 'Une erreur de données s\'est produite.',
      'pt': 'Ocorreu um erro de dados.',
      'ko': '데이터 오류가 발생했습니다.',
      'ru': 'Произошла ошибка данных.',
      'it': 'Si è verificato un errore nei dati.'
    },
    'error_timeout': {
      'en-US': 'The operation timed out.',
      'zh-CN': '操作超时。',
      'ja': '操作がタイムアウトしました。',
      'es': 'La operación caducó.',
      'de': 'Der Vorgang ist abgelaufen.',
      'fr': 'L\'opération a expiré.',
      'pt': 'A operação expirou.',
      'ko': '작업 시간이 초과되었습니다.',
      'ru': 'Операция истекла.',
      'it': 'L\'operazione è scaduta.'
    },
    'error_system': {
      'en-US': 'A system error occurred.',
      'zh-CN': '发生系统错误。',
      'ja': 'システムエラーが発生しました。',
      'es': 'Ocurrió un error del sistema.',
      'de': 'Ein Systemfehler ist aufgetreten.',
      'fr': 'Une erreur système s\'est produite.',
      'pt': 'Ocorreu um erro do sistema.',
      'ko': '시스템 오류가 발생했습니다.',
      'ru': 'Произошла системная ошибка.',
      'it': 'Si è verificato un errore di sistema.'
    },
    'error_browser_not_supported': {
      'en-US': 'Your browser does not support FIDO2/WebAuthn.',
      'zh-CN': '您的浏览器不支持FIDO2/WebAuthn。',
      'ja': 'お使いのブラウザは FIDO2/WebAuthn をサポートしていません。',
      'es': 'Su navegador no es compatible con FIDO2/WebAuthn.',
      'de': 'Ihr Browser unterstützt FIDO2/WebAuthn nicht.',
      'fr': 'Votre navigateur ne prend pas en charge FIDO2/WebAuthn.',
      'pt': 'Seu navegador não suporta FIDO2/WebAuthn.',
      'ko': '브라우저가 FIDO2/WebAuthn을 지원하지 않습니다.',
      'ru': 'Ваш браузер не поддерживает FIDO2/WebAuthn.',
      'it': 'Il tuo browser non supporta FIDO2/WebAuthn.'
    },
    'error_fido2lib_101': {
      'en-US': 'Your browser does not support FIDO2/WebAuthn.',
      'zh-CN': '您的浏览器不支持FIDO2/WebAuthn。',
      'ja': 'お使いのブラウザは FIDO2/WebAuthn をサポートしていません。',
      'es': 'Su navegador no es compatible con FIDO2/WebAuthn.',
      'de': 'Ihr Browser unterstützt FIDO2/WebAuthn nicht.',
      'fr': 'Votre navigateur ne prend pas en charge FIDO2/WebAuthn.',
      'pt': 'Seu navegador não suporta FIDO2/WebAuthn.',
      'ko': '브라우저가 FIDO2/WebAuthn을 지원하지 않습니다.',
      'ru': 'Ваш браузер не поддерживает FIDO2/WebAuthn.',
      'it': 'Il tuo browser non supporta FIDO2/WebAuthn.'
    },
    'error_fido2lib_102': {
      'en-US': 'The operation was canceled by the user.',
      'zh-CN': '操作已被用户取消。',
      'ja': 'ユーザーによって操作がキャンセルされました。',
      'es': 'La operación fue cancelada por el usuario.',
      'de': 'Der Vorgang wurde vom Benutzer abgebrochen.',
      'fr': 'L\'opération a été annulée par l\'utilisateur.',
      'pt': 'A operação foi cancelada pelo usuário.',
      'ko': '작업이 사용자에 의해 취소되었습니다.',
      'ru': 'Операция была отменена пользователем.',
      'it': 'L\'operazione è stata annullata dall\'utente.'
    },
    'error_fido2lib_103': {
      'en-US': 'The operation timed out.',
      'zh-CN': '操作超时。',
      'ja': '操作がタイムアウトしました。',
      'es': 'La operación caducó.',
      'de': 'Der Vorgang ist abgelaufen.',
      'fr': 'L\'opération a expiré.',
      'pt': 'A operação expirou.',
      'ko': '작업 시간이 초과되었습니다.',
      'ru': 'Операция истекла.',
      'it': 'L\'operazione è scaduta.'
    },
    'error_fido2lib_105': {
      'en-US': 'The authenticator contains credentials that are already registered with this website.',
      'zh-CN': '您的认证器包含已在此网站注册的凭据。',
      'ja': 'この認証デバイスには、すでにこの网站に登録されている憑拠が含まれています。',
      'es': 'El autenticador contiene credenciales que ya están registradas en este sitio web.',
      'de': 'Der Authenticator enthält Anmeldeinformationen, die bereits auf dieser Website registriert sind.',
      'fr': 'L\'authentificateur contient des identifiants déjà enregistrés sur ce site Web.',
      'pt': 'O autenticador contém credenciais já registradas neste site.',
      'ko': '인증기에 이미 이 웹사이트에 등록된 자격 증명이 포함되어 있습니다.',
      'ru': 'Аутентификатор содержит учетные данные, уже зарегистрированные на этом веб-сайте.',
      'it': 'L\'autenticatore contiene credenziali già registrate su questo sito web.'
    },
    'error_fido2lib_106': {
      'en-US': 'Another request is already in progress.',
      'zh-CN': '另一个请求正在进行中。',
      'ja': '別のリクエストがすでに進行中です。',
      'es': 'Otra solicitud ya está en progreso.',
      'de': 'Eine andere Anfrage wird bereits verarbeitet.',
      'fr': 'Une autre demande est déjà en cours.',
      'pt': 'Outra solicitação já está em andamento.',
      'ko': '다른 요청이 이미 진행 중입니다.',
      'ru': 'Другой запрос уже выполняется.',
      'it': 'Un\'altra richiesta è già in corso.'
    },
    'error_svr_101': {
      'en-US': 'Unregistered enterprise authenticator.',
      'zh-CN': '未注册的企业认证器。',
      'ja': '登録されていないエンタープライズ認証デバイス。',
      'es': 'Autenticador empresarial no registrado.',
      'de': 'Nicht registrierter Enterprise-Authenticator.',
      'fr': 'Authentificateur d\'entreprise non enregistré.',
      'pt': 'Autenticador empresarial não registrado.',
      'ko': '등록되지 않은 엔터프라이즈 인증기.',
      'ru': 'Незарегистрированный корпоративный аутентификатор.',
      'it': 'Autenticatore aziendale non registrato.'
    },
    'error_svr_102': {
      'en-US': 'Cannot authenticate with a unique device bound key from a different device.',
      'zh-CN': '无法使用来自其他设备的唯一设备绑定密钥进行身份验证。',
      'ja': '別のデバイスからの一意のデバイスバインドキーで認証できません。',
      'es': 'No se puede autenticar con una clave de vinculación de dispositivo única desde un dispositivo diferente.',
      'de': 'Authentifizierung mit einem eindeutigen Gerätebindungsschlüssel von einem anderen Gerät nicht möglich.',
      'fr': 'Impossible de s\'authentifier avec une clé de liaison d\'appareil unique à partir d\'un autre appareil.',
      'pt': 'Não é possível autenticar com uma chave de vinculação de dispositivo exclusiva de um dispositivo diferente.',
      'ko': '다른 장치의 고유 장치 바인딩 키로 인증할 수 없습니다.',
      'ru': 'Невозможно аутентифицироваться с ключом привязки уникального устройства с другого устройства.',
      'it': 'Impossibile autenticarsi con una chiave di associazione dispositivo univoca da un dispositivo diverso.'
    },
    'error_svr_103': {
      'en-US': 'Unable to verify signature.',
      'zh-CN': '无法验证签名。',
      'ja': '署名を認証できません。',
      'es': 'No se puede verificar la firma.',
      'de': 'Signatur kann nicht verifiziert werden.',
      'fr': 'Impossible de vérifier la signature.',
      'pt': 'Não foi possível verificar a assinatura.',
      'ko': '서명을 확인할 수 없습니다.',
      'ru': 'Не удалось проверить подпись.',
      'it': 'Impossibile verificare la firma.'
    },
    'error_svr_104': {
      'en-US': 'Key not found.',
      'zh-CN': '认证Key未找到。',
      'ja': 'キーが見つかりません！',
      'es': 'Clave no encontrada.',
      'de': 'Schlüssel nicht gefunden.',
      'fr': 'Clé non trouvée.',
      'pt': 'Chave não encontrada.',
      'ko': '키를 찾을 수 없습니다.',
      'ru': 'Ключ не найден.',
      'it': 'Chiave non trovata.'
    },
    'error_svr_105': {
      'en-US': 'Username does not exist.',
      'zh-CN': '用户名不存在。',
      'ja': 'ユーザー名は存在しません！',
      'es': 'El nombre de usuario no existe.',
      'de': 'Benutzername existiert nicht.',
      'fr': 'Le nom d\'utilisateur n\'existe pas.',
      'pt': 'O nome de usuário não existe.',
      'ko': '사용자 이름이 존재하지 않습니다.',
      'ru': 'Имя пользователя не существует.',
      'it': 'Il nome utente non esiste.'
    },
    'error_svr_106': {
      'en-US': 'Unique Device ID is required.',
      'zh-CN': '需要Unique Device ID。',
      'ja': '固有のデバイスIDが必要です！',
      'es': 'Se requiere ID de dispositivo único.',
      'de': 'Eindeutige Geräte-ID erforderlich.',
      'fr': 'L\'ID d\'appareil unique est requis.',
      'pt': 'É necessário ID de dispositivo exclusivo.',
      'ko': '고유 장치 ID가 필요합니다.',
      'ru': 'Требуется уникальный идентификатор устройства.',
      'it': 'È richiesto l\'ID dispositivo univoco.'
    },
    'error_svr_120': {
      'en-US': 'User has reached the device limit.',
      'zh-CN': '用户已达到设备限制数。',
      'ja': 'ユーザーはデバイスの制限数に達しました！',
      'es': 'El usuario ha alcanzado el límite de dispositivos.',
      'de': 'Benutzer hat das Gerätelimit erreicht.',
      'fr': 'L\'utilisateur a atteint la limite d\'appareils.',
      'pt': 'O usuário atingiu o limite de dispositivos.',
      'ko': '사용자가 장치 제한에 도달했습니다.',
      'ru': 'Пользователь достиг предела устройств.',
      'it': 'L\'utente ha raggiunto il limite del dispositivo.'
    },
    'error': {
      'en-US': 'An error occurred.',
      'zh-CN': '发生错误。',
      'ja': 'エラーが発生しました。',
      'es': 'Ocurrió un error.',
      'de': 'Ein Fehler ist aufgetreten.',
      'fr': 'Une erreur s\'est produite.',
      'pt': 'Ocorreu um erro.',
      'ko': '오류가 발생했습니다.',
      'ru': 'Произошла ошибка.',
      'it': 'Si è verificato un errore.'
    },
    'msg_autofail_hint': {
      'en-US': 'Auto login failed, please enter User ID',
      'zh-CN': '自动登录失败，请输入用户ID',
      'ja': '自動ログイン失敗、ユーザーIDを入力してください',
      'es': 'Error de inicio de sesión automático, ingrese ID de usuario',
      'de': 'Automatische Anmeldung fehlgeschlagen, bitte Benutzer-ID eingeben',
      'fr': 'Échec de la connexion automatique, entrez l\'ID utilisateur',
      'pt': 'Login automático falhou, digite o ID do usuário',
      'ko': '자동 로그인 실패, 사용자 ID 입력하세요',
      'ru': 'Автоматический вход не удался, введите ID пользователя',
      'it': 'Accesso automatico fallito, inserisci ID utente'
    },
    'msg_invalid_user_id': {
      'en-US': 'Please enter User ID',
      'zh-CN': '请输入用户ID',
      'ja': 'ユーザーIDを入力してください',
      'es': 'Por favor ingrese ID de usuario',
      'de': 'Bitte Benutzer-ID eingeben',
      'fr': 'Veuillez entrer l\'ID utilisateur',
      'pt': 'Por favor digite o ID do usuário',
      'ko': '사용자 ID를 입력하세요',
      'ru': 'Пожалуйста, введите ID пользователя',
      'it': 'Per favore inserisci ID utente'
    },
    'msg_invalid_password': {
      'en-US': 'Please enter Password',
      'zh-CN': '请输入密码',
      'ja': 'パスワードを入力してください',
      'es': 'Por favor ingrese contraseña',
      'de': 'Bitte Passwort eingeben',
      'fr': 'Veuillez entrer le mot de passe',
      'pt': 'Por favor digite a senha',
      'ko': '비밀번호를 입력하세요',
      'ru': 'Пожалуйста, введите пароль',
      'it': 'Per favore inserisci password'
    },
    'title_recovery': {
      'en-US': 'Device Recovery',
      'zh-CN': '设备恢复',
      'ja': 'デバイス回復',
      'es': 'Recuperación de dispositivo',
      'de': 'Gerätewiederherstellung',
      'fr': 'Récupération de dispositif',
      'pt': 'Recuperação de dispositivo',
      'ko': '장치 복구',
      'ru': 'Восстановление устройства',
      'it': 'Ripristino dispositivo'
    },
    'msg_recovery_success': {
      'en-US': 'Device recovered successfully',
      'zh-CN': '设备恢复成功',
      'ja': 'デバイス回復完了',
      'es': 'Dispositivo recuperado exitosamente',
      'de': 'Gerät erfolgreich wiederhergestellt',
      'fr': 'Appareil récupéré avec succès',
      'pt': 'Dispositivo recuperado com sucesso',
      'ko': '장치 복구 완료',
      'ru': 'Устройство успешно восстановлено',
      'it': 'Dispositivo ripristinato con successo'
    },
    'msg_recovery_failed': {
      'en-US': 'Recovery failed',
      'zh-CN': '恢复失败',
      'ja': '回復失敗',
      'es': 'Recuperación fallida',
      'de': 'Wiederherstellung fehlgeschlagen',
      'fr': 'Échec de la récupération',
      'pt': 'Falha na recuperação',
      'ko': '복구 실패',
      'ru': 'Ошибка восстановления',
      'it': 'Ripristino fallito'
    },
    'msg_recovery_user_found': {
      'en-US': 'Recovery session verified for user: {user}',
      'zh-CN': '已验证恢复会话，用户: {user}',
      'ja': 'ユーザー {user} の回復セッションが確認されました',
      'es': 'Sesión de recuperación verificada para el usuario: {user}',
      'de': 'Wiederherstellungssitzung für Benutzer verifiziert: {user}',
      'fr': 'Session de récupération vérifiée pour l\'utilisateur: {user}',
      'pt': 'Sessão de recuperação verificada para o usuário: {user}',
      'ko': '사용자 {user}의 복구 세션 확인됨',
      'ru': 'Сессия восстановления подтверждена для пользователя: {user}',
      'it': 'Sessione di ripristino verificata per l\'utente: {user}'
    },
    'btn_start_recovery': {
      'en-US': 'Start Recovery',
      'zh-CN': '开始恢复',
      'ja': '回復を開始',
      'es': 'Iniciar recuperación',
      'de': 'Wiederherstellung starten',
      'fr': 'Démarrer la récupération',
      'pt': 'Iniciar recuperação',
      'ko': '복구 시작',
      'ru': 'Начать восстановление',
      'it': 'Avvia ripristino'
    },
    'btn_cancel': {
      'en-US': 'Cancel',
      'zh-CN': '取消',
      'ja': 'キャンセル',
      'es': 'Cancelar',
      'de': 'Abbrechen',
      'fr': 'Annuler',
      'pt': 'Cancelar',
      'ko': '취소',
      'ru': 'Отмена',
      'it': 'Annulla'
    }
  };

  const LOGIN_DEFAULT_CONFIG = {
    serverUrl: 'https://fido2.amipro.me',
    container: null,
    theme: {
      logo: null,
      primaryColor: '#696cff',
      backgroundColor: '#ffffff',
      textColor: '#333333',
      borderRadius: '8px',
    },
    language: 'zh-CN',
    customI18n: {},
    features: {
      autoAuth: true,
      enablePasswordLogin: true,
      autoShowPassword: false,
      maxPasswordAttempts: 3,
      showRemainingAttempts: true,
    },
    callbacks: {
      onLoginSuccess: null,
      onLoginError: null,
      onPasswordLogin: null,
      onPasswordExhausted: null,
      onLoginClosed: null,
    },
    rpId: null,
  };

  const LoginMode = {
    FIDO2: 'fido2',
    PASSWORD: 'password'
  };

  const LoginState = {
    LOADING: 'loading',
    FIDO2: 'fido2',
    PASSWORD: 'password',
    CLOSED: 'closed'
  };

  function Fido2Login(config) {
    this.config = Object.assign({}, LOGIN_DEFAULT_CONFIG, config);
    this.i18n = new I18nManager(this.config);
    this.themeManager = new ThemeManager(this.config);
    this.eventManager = new EventManager();
    this.state = LoginState.LOADING;
    this.mode = LoginMode.FIDO2;
    this.attemptCount = 0;
    this.maxAttempts = this.config.features.maxPasswordAttempts || 3;
    this.modalElement = null;
    this.containerElement = null;
    this.bootstrapModal = null;
    this.initialized = false;

    if (this.config.serverUrl) {
      setFidoServerURL(this.config.serverUrl);
    }
  }

  Fido2Login.prototype._createModal = function() {
    const container = typeof this.config.container === 'string'
      ? document.querySelector(this.config.container)
      : this.config.container;

    this._debugLog('[Fido2Login] _createModal container:', this.config.container);
    this._debugLog('[Fido2Login] Container element:', container);

    if (!container) {
      throw new Error('Container not found: ' + this.config.container);
    }

    container.innerHTML = '';

    const uniqueId = Date.now() + '_' + Math.random().toString(36).substr(2, 9);
    const modalId = 'fido2LoginModal_' + uniqueId;
    const titleId = 'fido2LoginModalTitle_' + uniqueId;
    const errorId = 'fido2LoginError_' + uniqueId;
    const hintId = 'fido2LoginHint_' + uniqueId;
    const userIdId = 'fido2UserId_' + uniqueId;
    const passwordId = 'fido2Password_' + uniqueId;
    const passwordSectionId = 'fido2PasswordSection_' + uniqueId;
    const mainBtnId = 'fido2MainBtn_' + uniqueId;
    const toggleLinkId = 'fido2ToggleModeLink_' + uniqueId;

    const modal = document.createElement('div');
    modal.className = 'modal fade fido2-sdk-login-modal';
    modal.id = modalId;
    modal.tabIndex = -1;
    modal.setAttribute('aria-hidden', 'true');

    modal.innerHTML = this._getModalHTML(uniqueId, titleId, errorId, hintId, userIdId, passwordId, passwordSectionId, mainBtnId, toggleLinkId);
    container.appendChild(modal);
    this.modalElement = modal;
    this.containerElement = container;
    this._uniqueId = uniqueId;

    this._debugLog('[Fido2Login] Modal element created with ID:', modalId);

    this.themeManager.applyTheme(modal);

    this.bootstrapModal = new window.bootstrap.Modal(modal, {
      backdrop: true,
      keyboard: true
    });

    this._debugLog('[Fido2Login] BootstrapModal instance created');

    modal.addEventListener('hidden.bs.modal', () => {
      this.state = LoginState.CLOSED;
      this._emit('loginClosed');
      this.cleanup();
    });

    this._bindEvents();
    return modal;
  };

  Fido2Login.prototype._getModalHTML = function(uniqueId, titleId, errorId, hintId, userIdId, passwordId, passwordSectionId, mainBtnId, toggleLinkId) {
    const theme = this.config.theme;
    const features = this.config.features;

    return `
      <div class="modal-dialog modal-dialog-centered">
        <div class="modal-content fido2-sdk-card">
          <div class="modal-header fido2-sdk-header">
            ${theme.logo ? `<img src="${theme.logo}" class="fido2-sdk-logo me-2" alt="Logo">` : ''}
            <h5 class="modal-title fido2-sdk-text" id="${titleId}">${this.i18n.getText('title_login')}</h5>
            <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
          </div>
          <div class="modal-body fido2-sdk-container">
            ${this._getBodyHTML(uniqueId, errorId, hintId, userIdId, passwordId, passwordSectionId, mainBtnId, toggleLinkId)}
          </div>
        </div>
      </div>
    `;
  };

  Fido2Login.prototype._getBodyHTML = function(uniqueId, errorId, hintId, userIdId, passwordId, passwordSectionId, mainBtnId, toggleLinkId) {
    const features = this.config.features;
    const theme = this.config.theme;

    let html = '';

    html += `
      <div id="${errorId}" class="alert alert-danger mb-3" style="display:none;"></div>
      <div id="${hintId}" class="alert alert-info mb-3" style="display:none;"></div>
    `;

    html += `
      <div class="mb-3">
        <label for="${userIdId}" class="form-label fido2-sdk-text">${this.i18n.getText('placeholder_user_id')}</label>
        <input type="text" class="form-control" id="${userIdId}" placeholder="${this.i18n.getText('placeholder_user_id')}">
      </div>
    `;

    html += `
      <div class="mb-3" id="${passwordSectionId}" style="display:none;">
        <label for="${passwordId}" class="form-label fido2-sdk-text">${this.i18n.getText('placeholder_password')}</label>
        <input type="password" class="form-control" id="${passwordId}" placeholder="${this.i18n.getText('placeholder_password')}">
      </div>
    `;

    html += `
      <div class="d-grid gap-2">
        <button type="button" class="btn btn-primary fido2-sdk-btn fido2-sdk-btn-primary" id="${mainBtnId}">
          ${this.i18n.getText('btn_fido2_login')}
        </button>
      </div>
    `;

    html += `<hr class="my-4">`;

    if (features.enablePasswordLogin) {
      html += `
        <div class="text-center">
          <a href="javascript:void(0)" class="fido2-sdk-link" id="${toggleLinkId}">${this.i18n.getText('link_use_password')}</a>
        </div>
      `;
    }

    if (features.showRemainingAttempts && this.mode === LoginMode.PASSWORD) {
      const remaining = this.maxAttempts - this.attemptCount;
      html += `
        <div class="text-center mt-3">
          <small class="text-muted fido2-sdk-text" id="fido2RemainingAttempts_${uniqueId}">
            ${this.i18n.getText('msg_remaining_attempts').replace('{n}', remaining)}
          </small>
        </div>
      `;
    }

    return html;
  };

  Fido2Login.prototype._bindEvents = function() {
    const self = this;
    const container = this.modalElement || this.containerElement;
    if (!container) return;

    const mainBtn = container.querySelector('[id^="fido2MainBtn_"]');
    if (mainBtn) {
      mainBtn.addEventListener('click', () => {
        if (self.mode === LoginMode.FIDO2) {
          self._handleFido2Login();
        } else {
          self._handlePasswordLogin();
        }
      });
    }

    const toggleLink = container.querySelector('[id^="fido2ToggleModeLink_"]');
    if (toggleLink) {
      toggleLink.addEventListener('click', () => {
        this._debugLog('[Fido2Login] Toggle link clicked, current mode:', self.mode);
        self._toggleMode();
      });
    }

    const userIdInput = container.querySelector('[id^="fido2UserId_"]');
    if (userIdInput) {
      userIdInput.addEventListener('keypress', (e) => {
        if (e.key === 'Enter') {
          if (self.mode === LoginMode.FIDO2) {
            self._handleFido2Login();
          } else {
            self._handlePasswordLogin();
          }
        }
      });
    }

    const passwordInput = container.querySelector('[id^="fido2Password_"]');
    if (passwordInput) {
      passwordInput.addEventListener('keypress', (e) => {
        if (e.key === 'Enter') {
          self._handlePasswordLogin();
        }
      });
    }
  };

  Fido2Login.prototype._handleFido2Login = function() {
    const userId = this._getUserIdInput();
    if (!userId) {
      this._showError(this.i18n.getText('msg_invalid_user_id'));
      return;
    }

    this._hideError();
    this._startLoading();

    authenticateFido2(userId, this.config.rpId).then(result => {
      this._stopLoading();
      if (result.status === 'ok') {
        this.state = LoginState.CLOSED;
        this._emit('fido2Success', {
          method: 'fido2',
          username: result.username,
          session: result.session
        });
        this._closeUI();
      } else {
        this._emit('fido2Error', {
          method: 'fido2',
          code: 'AUTH_FAILED',
          message: result.errorMessage || this.i18n.getText('msg_fido2_failed'),
          originalError: result
        });
      }
    }).catch(error => {
      this._stopLoading();
      if (error.name === 'AbortError' || (error.message && error.message.toLowerCase().includes('canceled'))) {
        this._emit('fido2Error', {
          method: 'fido2',
          code: 'CANCELED',
          message: this.i18n.getText('msg_fido2_canceled'),
          originalError: error
        });
      } else {
        this._emit('fido2Error', {
          method: 'fido2',
          code: 'AUTH_FAILED',
          message: error.message || this.i18n.getText('msg_fido2_failed'),
          originalError: error
        });
      }
    });
  };

  Fido2Login.prototype._handlePasswordLogin = function() {
    const userId = this._getUserIdInput();
    const password = this._getPasswordInput();

    if (!userId) {
      this._showError(this.i18n.getText('msg_invalid_user_id'));
      return;
    }

    if (!password) {
      this._showError(this.i18n.getText('msg_invalid_password'));
      return;
    }

    this._hideError();
    this._startLoading();

    // Clear any existing FIDO2 session before password login
    if (typeof logoutFido2UserSession === 'function') {
      logoutFido2UserSession();
    }

    const passwordCallback = this.config.callbacks.onPasswordLogin;
    if (typeof passwordCallback !== 'function') {
      this._stopLoading();
      console.error('onPasswordLogin callback is not defined');
      return;
    }

    Promise.resolve(passwordCallback(userId, password)).then(success => {
      this._stopLoading();
      if (success) {
        this.state = LoginState.CLOSED;
        this._emit('fido2Success', {
          method: 'password',
          username: userId,
          session: null
        });
        this._closeUI();
      } else {
        this.attemptCount++;
        if (this.attemptCount >= this.maxAttempts) {
          this._closeUI();
          this._emit('passwordExhausted', userId, this.attemptCount, this.maxAttempts);
        } else {
          this._updateRemainingAttempts();
          const remaining = this.maxAttempts - this.attemptCount;
          this._showError(this.i18n.getText('msg_remaining_attempts').replace('{n}', remaining));
        }
      }
    }).catch(error => {
      this._stopLoading();
      this.attemptCount++;
      if (this.attemptCount >= this.maxAttempts) {
        this._closeUI();
        this._emit('passwordExhausted', userId, this.attemptCount, this.maxAttempts);
      } else {
        this._updateRemainingAttempts();
        const remaining = this.maxAttempts - this.attemptCount;
        this._showError(this.i18n.getText('msg_remaining_attempts').replace('{n}', remaining));
      }
    });
  };

  Fido2Login.prototype._toggleMode = function() {
    const container = this.modalElement || this.containerElement;
    if (!container) return;

    const passwordSection = container.querySelector('[id^="fido2PasswordSection_"]');
    const mainBtn = container.querySelector('[id^="fido2MainBtn_"]');
    const toggleLink = container.querySelector('[id^="fido2ToggleModeLink_"]');
    const titleEl = container.querySelector('[id^="fido2LoginModalTitle_"]');
    const remainingEl = container.querySelector('[id^="fido2RemainingAttempts_"]');

    if (this.mode === LoginMode.FIDO2) {
      this.mode = LoginMode.PASSWORD;
      if (passwordSection) passwordSection.style.display = 'block';
      if (mainBtn) mainBtn.textContent = this.i18n.getText('btn_password_login');
      if (toggleLink) toggleLink.textContent = this.i18n.getText('link_use_fido2');
      if (titleEl) titleEl.textContent = this.i18n.getText('title_password_login');

      this.attemptCount = 0;
      const remaining = this.maxAttempts - this.attemptCount;
      if (remainingEl) {
        remainingEl.textContent = this.i18n.getText('msg_remaining_attempts').replace('{n}', remaining);
      } else if (this.config.features.showRemainingAttempts) {
        const remainingContainer = document.createElement('div');
        remainingContainer.className = 'text-center mt-3';
        remainingContainer.innerHTML = `<small class="text-muted fido2-sdk-text" id="fido2RemainingAttempts_${this._uniqueId}">${this.i18n.getText('msg_remaining_attempts').replace('{n}', remaining)}</small>`;
        toggleLink.parentNode.parentNode.insertBefore(remainingContainer, toggleLink.parentNode.nextSibling);
      }
    } else {
      this.mode = LoginMode.FIDO2;
      if (passwordSection) passwordSection.style.display = 'none';
      if (mainBtn) mainBtn.textContent = this.i18n.getText('btn_fido2_login');
      if (toggleLink) toggleLink.textContent = this.i18n.getText('link_use_password');
      if (titleEl) titleEl.textContent = this.i18n.getText('title_login');

      if (remainingEl) {
        remainingEl.parentNode.remove();
      }
    }

    this._hideError();
  };

  Fido2Login.prototype._getUserIdInput = function() {
    const container = this.modalElement || this.containerElement;
    const input = container ? container.querySelector('[id^="fido2UserId_"]') : null;
    return input ? input.value.trim() : '';
  };

  Fido2Login.prototype._getPasswordInput = function() {
    const container = this.modalElement || this.containerElement;
    const input = container ? container.querySelector('[id^="fido2Password_"]') : null;
    return input ? input.value : '';
  };

  Fido2Login.prototype._showError = function(message) {
    const container = this.modalElement || this.containerElement;
    const errorEl = container ? container.querySelector('[id^="fido2LoginError_"]') : null;
    const hintEl = container ? container.querySelector('[id^="fido2LoginHint_"]') : null;
    if (hintEl) hintEl.style.display = 'none';
    if (errorEl) {
      errorEl.textContent = message;
      errorEl.style.display = 'block';
    }
  };

  Fido2Login.prototype._hideError = function() {
    const container = this.modalElement || this.containerElement;
    const errorEl = container ? container.querySelector('[id^="fido2LoginError_"]') : null;
    if (errorEl) errorEl.style.display = 'none';
  };

  Fido2Login.prototype._showHint = function(message) {
    const container = this.modalElement || this.containerElement;
    const hintEl = container ? container.querySelector('[id^="fido2LoginHint_"]') : null;
    if (hintEl) {
      hintEl.textContent = message;
      hintEl.style.display = 'block';
    }
  };

  Fido2Login.prototype._hideHint = function() {
    const container = this.modalElement || this.containerElement;
    const hintEl = container ? container.querySelector('[id^="fido2LoginHint_"]') : null;
    if (hintEl) hintEl.style.display = 'none';
  };

  Fido2Login.prototype._startLoading = function() {
    const container = this.modalElement || this.containerElement;
    const mainBtn = container ? container.querySelector('[id^="fido2MainBtn_"]') : null;
    if (mainBtn) {
      mainBtn.disabled = true;
      mainBtn.dataset.originalText = mainBtn.textContent;
      mainBtn.textContent = '...';
    }
  };

  Fido2Login.prototype._stopLoading = function() {
    const container = this.modalElement || this.containerElement;
    const mainBtn = container ? container.querySelector('[id^="fido2MainBtn_"]') : null;
    if (mainBtn) {
      mainBtn.disabled = false;
      mainBtn.textContent = mainBtn.dataset.originalText ||
        (this.mode === LoginMode.FIDO2 ? this.i18n.getText('btn_fido2_login') : this.i18n.getText('btn_password_login'));
    }
  };

  Fido2Login.prototype._updateRemainingAttempts = function() {
    const container = this.modalElement || this.containerElement;
    if (!container) return;

    const remainingEl = container.querySelector('[id^="fido2RemainingAttempts_"]');
    if (remainingEl) {
      const remaining = this.maxAttempts - this.attemptCount;
      remainingEl.textContent = this.i18n.getText('msg_remaining_attempts').replace('{n}', remaining);
    }
  };

  Fido2Login.prototype._emit = function(event, data) {
    const callbackName = 'on' + event.charAt(0).toUpperCase() + event.slice(1);
    const callback = this.config.callbacks[callbackName];
    if (typeof callback === 'function') {
      if (event === 'fido2Success') {
        callback(data.username, data.session, data);
      } else if (event === 'fido2Error') {
        callback(data, data.originalError);
      } else {
        callback(data);
      }
    }
  };

  Fido2Login.prototype._closeUI = function() {
    if (this.bootstrapModal) {
      this.bootstrapModal.hide();
    } else if (this.modalElement) {
      const bootstrapModal = window.bootstrap.Modal.getInstance(this.modalElement);
      if (bootstrapModal) {
        bootstrapModal.hide();
      }
    }
  };

  Fido2Login.prototype.show = function() {
    this._debugLog('[Fido2Login] show() called, initialized:', this.initialized);
    
    if (this.initialized) {
      this._debugLog('[Fido2Login] Already initialized, showing modal');
      if (this.bootstrapModal) {
        this.bootstrapModal.show();
      }
      return;
    }

    this._debugLog('[Fido2Login] Creating modal...');
    this._createModal();
    this._initAutoAuth();
    this.initialized = true;

    this._debugLog('[Fido2Login] Modal created, showing...');
    if (this.bootstrapModal) {
      this._debugLog('[Fido2Login] bootstrapModal.show() called');
      this.bootstrapModal.show();
    }
  };

  Fido2Login.prototype._initAutoAuth = function() {
    const self = this;
    const features = this.config.features;

    this._debugLog('[Fido2Login] _initAutoAuth called');
    this._debugLog('[Fido2Login] features.autoAuth:', features.autoAuth);
    this._debugLog('[Fido2Login] canTryAutoAuthentication():', canTryAutoAuthentication());
    this._debugLog('[Fido2Login] dfido2_lib_registered:', localStorage.getItem('dfido2_lib_registered'));

    if (features.autoAuth && canTryAutoAuthentication()) {
      this.state = LoginState.LOADING;
      this._showHint(this.i18n.getText('msg_autofail_hint'));
      this._debugLog('[Fido2Login] Starting auto Fido2 authentication...');

      authenticateFido2(null, this.config.rpId).then(result => {
        this._debugLog('[Fido2Login] authenticateFido2 result:', result);
        this._hideHint();
        if (result.status === 'ok') {
          this.state = LoginState.CLOSED;
          this._emit('fido2Success', {
            method: 'fido2',
            username: result.username,
            session: result.session
          });
          this._closeUI();
        } else {
          this.state = LoginState.FIDO2;
          this._emit('fido2Error', {
            method: 'fido2',
            code: 'AUTH_FAILED',
            message: result.errorMessage || this.i18n.getText('msg_fido2_failed'),
            originalError: result
          });
          this._updateUIForFido2Mode();
        }
      }).catch(error => {
        this._debugLog('[Fido2Login] authenticateFido2 error:', error);
        this._hideHint();
        if (error.name === 'AbortError' || (error.message && error.message.toLowerCase().includes('canceled'))) {
          this.state = LoginState.FIDO2;
          this._emit('fido2Error', {
            method: 'fido2',
            code: 'CANCELED',
            message: this.i18n.getText('msg_fido2_canceled'),
            originalError: error
          });
        } else {
          this.state = LoginState.FIDO2;
          this._emit('fido2Error', {
            method: 'fido2',
            code: 'AUTH_FAILED',
            message: this._getWebAuthnErrorMessage(error),
            originalError: error
          });
        }
        this._updateUIForFido2Mode();
      });
    } else if (!canTryAutoAuthentication()) {
      this._debugLog('[Fido2Login] No registration, going to password mode');
      if (features.enablePasswordLogin) {
        this.state = LoginState.PASSWORD;
        this.mode = LoginMode.PASSWORD;
        this._updateUIForPasswordMode();
      } else {
        this.state = LoginState.FIDO2;
        this._emit('fido2Error', {
          method: 'fido2',
          code: 'NO_REGISTRATION',
          message: this.i18n.getText('msg_no_registration'),
          originalError: null
        });
      }
    } else {
      this._debugLog('[Fido2Login] autoAuth disabled or other condition, going to fido2 mode');
      this.state = LoginState.FIDO2;
      this._updateUIForFido2Mode();
    }
  };

  Fido2Login.prototype._getWebAuthnErrorI18nKey = function(error) {
    if (!error || !error.name) return null;
    
    const errorNameMap = {
      'InvalidStateError': 'error_invalid_state',
      'NotAllowedError': 'error_not_allowed',
      'AbortError': 'error_abort',
      'NotSupportedError': 'error_not_supported',
      'SecurityError': 'error_security',
      'NetworkError': 'error_network',
      'ConstraintError': 'error_constraint',
      'NotReadableError': 'error_not_readable',
      'EncodingError': 'error_encoding',
      'DataError': 'error_data',
      'TimeoutError': 'error_timeout'
    };
    
    return errorNameMap[error.name] || null;
  };

  Fido2Login.prototype._getWebAuthnErrorMessage = function(error) {
    if (!error) return this.i18n.getText('msg_fido2_failed');
    
    // Check for browser-native WebAuthn errors with URL references
    const errorMessage = error.message || '';
    if (errorMessage.includes('https://www.w3.org/TR/webauthn')) {
      // Check for common browser-native error patterns
      if (error.name === 'NotAllowedError' || errorMessage.toLowerCase().includes('not allowed')) {
        return this.i18n.getText('error_not_allowed');
      }
      if (errorMessage.toLowerCase().includes('timeout') || errorMessage.toLowerCase().includes('timed out')) {
        return this.i18n.getText('error_timeout');
      }
      if (errorMessage.toLowerCase().includes('invalid state') || errorMessage.toLowerCase().includes('already registered')) {
        return this.i18n.getText('error_invalid_state');
      }
      // Default to not allowed for browser privacy errors
      return this.i18n.getText('error_not_allowed');
    }
    
    const i18nKey = this._getWebAuthnErrorI18nKey(error);
    if (i18nKey && this.i18n.getText(i18nKey)) {
      return this.i18n.getText(i18nKey);
    }
    
    return error.message || this.i18n.getText('msg_fido2_failed');
  };

  Fido2Login.prototype._debugLog = function() {
    const args = Array.prototype.slice.call(arguments);
    const message = args.map(arg => {
      if (typeof arg === 'object') {
        try {
          return JSON.stringify(arg);
        } catch (e) {
          return String(arg);
        }
      }
      return String(arg);
    }).join(' ');

    if (typeof window.fido2LoginDebug === 'function') {
      window.fido2LoginDebug(message);
    }
    console.log.apply(console, args);
  };

  Fido2Login.prototype._updateUIForFido2Mode = function() {
    const container = this.modalElement || this.containerElement;
    if (!container) return;

    const passwordSection = container.querySelector('[id^="fido2PasswordSection_"]');
    const mainBtn = container.querySelector('[id^="fido2MainBtn_"]');
    const toggleLink = container.querySelector('[id^="fido2ToggleModeLink_"]');

    if (passwordSection) passwordSection.style.display = 'none';
    if (mainBtn) mainBtn.textContent = this.i18n.getText('btn_fido2_login');
    if (toggleLink && this.config.features.enablePasswordLogin) {
      toggleLink.textContent = this.i18n.getText('link_use_password');
      toggleLink.style.display = 'inline';
    } else if (toggleLink) {
      toggleLink.style.display = 'none';
    }
  };

  Fido2Login.prototype._updateUIForPasswordMode = function() {
    const container = this.modalElement || this.containerElement;
    if (!container) return;

    const passwordSection = container.querySelector('[id^="fido2PasswordSection_"]');
    const mainBtn = container.querySelector('[id^="fido2MainBtn_"]');
    const toggleLink = container.querySelector('[id^="fido2ToggleModeLink_"]');
    const titleEl = container.querySelector('[id^="fido2LoginModalTitle_"]');

    if (passwordSection) passwordSection.style.display = 'block';
    if (mainBtn) mainBtn.textContent = this.i18n.getText('btn_password_login');
    if (toggleLink) toggleLink.textContent = this.i18n.getText('link_use_fido2');
    if (titleEl) titleEl.textContent = this.i18n.getText('title_password_login');

    this._updateRemainingAttempts();
  };

  Fido2Login.prototype.hide = function() {
    this._closeUI();
  };

  Fido2Login.prototype.destroy = function() {
    this.state = LoginState.CLOSED;
    if (this.bootstrapModal) {
      this.bootstrapModal.dispose();
    }
    this.cleanup();
  };

  Fido2Login.prototype.cleanup = function() {
    if (this.modalElement) {
      this.themeManager.cleanup(this.modalElement);
      if (this.modalElement.parentNode) {
        this.modalElement.parentNode.removeChild(this.modalElement);
      }
      this.modalElement = null;
    }
    this.containerElement = null;
    this.bootstrapModal = null;
  };

  Fido2Login.prototype.setMode = function(mode) {
    if (mode === LoginMode.PASSWORD && this.mode !== LoginMode.PASSWORD) {
      this.mode = LoginMode.PASSWORD;
      this.attemptCount = 0;
      this._updateUIForPasswordMode();
    } else if (mode === LoginMode.FIDO2 && this.mode !== LoginMode.FIDO2) {
      this.mode = LoginMode.FIDO2;
      this._updateUIForFido2Mode();
    }
  };

  Fido2Login.prototype.getUserId = function() {
    return this._getUserIdInput();
  };

  Fido2Login.prototype.getState = function() {
    return this.state;
  };

  Fido2Login.prototype.getMode = function() {
    return this.mode;
  };

  Fido2Login.prototype.getAttemptCount = function() {
    return this.attemptCount;
  };

  Fido2Login.prototype.getRemainingAttempts = function() {
    return this.maxAttempts - this.attemptCount;
  };

  Fido2Login.prototype.resetPasswordAttempts = function() {
    this.attemptCount = 0;
    this._updateRemainingAttempts();
  };

  function I18nManager(config) {
    this.config = config;
    this.messages = new Map();

    for (let key in DEFAULT_I18N) {
      this.messages.set(key, new Map());
      for (let lang in DEFAULT_I18N[key]) {
        this.messages.get(key).set(lang, DEFAULT_I18N[key][lang]);
      }
    }

    if (config.customI18n) {
      for (let key in config.customI18n) {
        if (!this.messages.has(key)) {
          this.messages.set(key, new Map());
        }
        for (let lang in config.customI18n[key]) {
          this.messages.get(key).set(lang, config.customI18n[key][lang]);
        }
      }
    }
  }

  I18nManager.prototype.getText = function(key) {
    const lang = this.config.language || window.navigator.language || 'en-US';
    const keyMap = this.messages.get(key);

    if (!keyMap) return key;

    let text = keyMap.get(lang);
    if (!text) text = keyMap.get('en-US');

    return text || key;
  };

  function EventManager() {
    this.callbacks = {};
  }

  EventManager.prototype.on = function(event, callback) {
    if (!this.callbacks[event]) {
      this.callbacks[event] = [];
    }
    this.callbacks[event].push(callback);
  };

  EventManager.prototype.off = function(event, callback) {
    if (!this.callbacks[event]) return;

    const index = this.callbacks[event].indexOf(callback);
    if (index > -1) {
      this.callbacks[event].splice(index, 1);
    }
  };

  EventManager.prototype.emit = function(event, data) {
    if (!this.callbacks[event]) return;

    this.callbacks[event].forEach(function(callback) {
      callback(data);
    });
  };

  function ThemeManager(config) {
    this.config = config;
  }

  ThemeManager.prototype.applyTheme = function(element) {
    if (!element) return;

    const theme = this.config.theme;
    const styleId = 'fido2-ui-sdk-theme-' + Date.now();

    let css = '';
    if (theme.primaryColor) {
      css += '.fido2-sdk-btn-primary { background-color: ' + theme.primaryColor + '!important; border-color: ' + theme.primaryColor + '!important; }';
      css += '.fido2-sdk-header { border-bottom: 2px solid ' + theme.primaryColor + '!important; }';
    }
    if (theme.backgroundColor) {
      css += '.fido2-sdk-container { background-color: ' + theme.backgroundColor + '!important; }';
    }
    if (theme.textColor) {
      css += '.fido2-sdk-text { color: ' + theme.textColor + '!important; }';
    }
    if (theme.borderRadius) {
      css += '.fido2-sdk-card, .fido2-sdk-btn { border-radius: ' + theme.borderRadius + '!important; }';
    }

    if (css) {
      let style = document.getElementById(styleId);
      if (style) {
        style.parentNode.removeChild(style);
      }

      style = document.createElement('style');
      style.id = styleId;
      style.textContent = css;
      document.head.appendChild(style);
    }

    element.dataset.themeStyleId = styleId;
  };

  ThemeManager.prototype.cleanup = function(element) {
    if (!element) return;

    const styleId = element.dataset.themeStyleId;
    if (styleId) {
      const style = document.getElementById(styleId);
      if (style) {
        style.parentNode.removeChild(style);
      }
    }
  };

  function DeviceManager(config, i18n, eventManager) {
    this.config = config;
    this.i18n = i18n;
    this.eventManager = eventManager;
    this.devices = [];
    this.refreshTimer = null;
    this.sessionStatus = false;
    this.recoveryMode = false;
    this.recoveryUser = null;
  }

  DeviceManager.prototype.getSessionUserId = function() {
    try {
      const sessionText = sessionStorage.getItem('fido2_user_session');
      if (!sessionText) return null;
      const sessionData = JSON.parse(sessionText);
      return sessionData.uid || null;
    } catch (error) {
      return null;
    }
  };

  DeviceManager.prototype.getEffectiveUserId = function() {
    const sessionUserId = this.getSessionUserId();
    if (sessionUserId) {
      return sessionUserId;
    }
    return this.config.userId || null;
  };

  DeviceManager.prototype.validateUserId = function() {
    const sessionUserId = this.getSessionUserId();
    const inputUserId = this.config.userId;
    
    if (sessionUserId && inputUserId && sessionUserId !== inputUserId) {
      return { valid: false, error: 'User ID mismatch: session user is ' + sessionUserId };
    }
    return { valid: true };
  };

  DeviceManager.prototype.loadDevices = async function() {
    try {
      const result = await listUserDevicesFido2(this.config.rpId);

      if (result.status === 'ok') {
        this.devices = result.devices || [];
        this.eventManager.emit('deviceListLoaded', this.devices);
        return this.devices;
      } else {
        throw new Error(result.errorMessage || 'Failed to load devices');
      }
    } catch (error) {
      this.eventManager.emit('error', error);
      throw error;
    }
  };

  DeviceManager.prototype.addDevice = async function(displayName) {
    try {
      const effectiveUserId = this.getEffectiveUserId();
      if (!effectiveUserId) {
        const err = new Error('No user ID available. Please login first.');
        this.eventManager.emit('error', err);
        throw err;
      }

      const validation = this.validateUserId();
      if (!validation.valid) {
        const err = new Error(validation.error);
        this.eventManager.emit('error', err);
        throw err;
      }

      const result = await registerFido2(effectiveUserId, displayName || 'Device-' + effectiveUserId, this.config.rpId);

      if (result.status === 'ok') {
        // registerFido2 automatically creates/updates session in sessionStorage
        await this.loadDevices();
        this.eventManager.emit('deviceAdded', result);
        return result;
      } else {
        // Create error with original properties preserved
        const err = new Error(result.errorMessage || 'Failed to add device');
        if (result.name) err.name = result.name;
        if (result.errCode) err.errCode = result.errCode;
        this.eventManager.emit('error', err);
        throw err;
      }
    } catch (error) {
      if (!error.name) {
        error.name = 'DeviceManagerError';
      }
      this.eventManager.emit('error', error);
      throw error;
    }
  };

  DeviceManager.prototype.deleteDevice = async function(deviceId) {
    try {
      const effectiveUserId = this.getEffectiveUserId();
      if (!effectiveUserId) {
        const err = new Error('No user ID available. Please login first.');
        this.eventManager.emit('error', err);
        throw err;
      }

      const validation = this.validateUserId();
      if (!validation.valid) {
        const err = new Error(validation.error);
        this.eventManager.emit('error', err);
        throw err;
      }

      const result = await delUserDeviceFido2(deviceId, this.config.rpId);

      if (result.status === 'ok') {
        await this.loadDevices();
        this.eventManager.emit('deviceDeleted', deviceId);
        return result;
      } else {
        // Create error with original properties preserved
        const err = new Error(result.errorMessage || 'Failed to delete device');
        if (result.name) err.name = result.name;
        if (result.errCode) err.errCode = result.errCode;
        this.eventManager.emit('error', err);
        throw err;
      }
    } catch (error) {
      if (!error.name) {
        error.name = 'DeviceManagerError';
      }
      this.eventManager.emit('error', error);
      throw error;
    }
  };

  DeviceManager.prototype.getUserId = function() {
    return this.getEffectiveUserId();
  };

  DeviceManager.prototype.checkSession = async function() {
    try {
      const sessionOk = await validSession(this.config.rpId);
      this.sessionStatus = !!sessionOk;
      this.eventManager.emit('sessionStatusChanged', this.sessionStatus);
      return this.sessionStatus;
    } catch (error) {
      this.sessionStatus = false;
      this.eventManager.emit('sessionStatusChanged', false);
      return false;
    }
  };

  DeviceManager.prototype.getRegistrationUser = async function(regSessionId) {
    try {
      const response = await fetch(getServerUrl() + '/reg/username', {
        method: 'POST',
        cache: 'no-cache',
        headers: {
          'Content-Type': 'application/json'
        },
        body: JSON.stringify({ session_id: regSessionId })
      });
      const resp = await response.json();
      return resp;
    } catch (err) {
      console.log(err);
      return { status: 'failed', errorMessage: err.message };
    }
  };

  DeviceManager.prototype.startRecovery = async function(recoverySessionId) {
    try {
      if (!recoverySessionId) {
        throw new Error('Recovery session ID is required');
      }

      const result = await this.getRegistrationUser(recoverySessionId);

      if (result && result.status === 'ok' && result.username) {
        this.recoveryMode = true;
        this.recoveryUser = {
          username: result.username,
          displayName: result.displayname || result.username,
          sessionId: recoverySessionId
        };

        this.config.userId = result.username;
        this.eventManager.emit('recoveryStarted', this.recoveryUser);
        return {
          success: true,
          user: this.recoveryUser
        };
      } else {
        throw new Error(result.errorMessage || 'Invalid recovery session');
      }
    } catch (error) {
      this.eventManager.emit('error', error);
      throw error;
    }
  };

  DeviceManager.prototype.finishRecovery = async function(displayName) {
    try {
      if (!this.recoveryMode || !this.recoveryUser) {
        throw new Error('Not in recovery mode');
      }

      const effectiveDisplayName = displayName || this.recoveryUser.displayName || 'RecoveredDevice-' + this.recoveryUser.username;

      const result = await registerFido2(this.recoveryUser.username, effectiveDisplayName, this.config.rpId);

      if (result.status === 'ok') {
        const wasRecoveryMode = this.recoveryMode;
        const recoveredUser = this.recoveryUser;

        this.recoveryMode = false;
        this.recoveryUser = null;

        await this.loadDevices();
        this.eventManager.emit('deviceAdded', result);
        this.eventManager.emit('recoveryCompleted', {
          user: recoveredUser,
          device: result
        });

        return {
          success: true,
          user: recoveredUser,
          device: result
        };
      } else {
        throw new Error(result.errorMessage || 'Failed to add device during recovery');
      }
    } catch (error) {
      this.eventManager.emit('error', error);
      throw error;
    }
  };

  DeviceManager.prototype.cancelRecovery = function() {
    if (this.recoveryMode) {
      const canceledUser = this.recoveryUser;
      this.recoveryMode = false;
      this.recoveryUser = null;
      this.eventManager.emit('recoveryCanceled', canceledUser);
      return true;
    }
    return false;
  };

  DeviceManager.prototype.isInRecoveryMode = function() {
    return this.recoveryMode;
  };

  DeviceManager.prototype.getRecoveryUser = function() {
    return this.recoveryUser;
  };

  DeviceManager.prototype.parseDeviceDescription = function(device) {
    if (device.desc && device.desc.length > 0) {
      return device.desc;
    }

    try {
      if (device.userAgent && typeof UAParser !== 'undefined') {
        const parser = new UAParser(device.userAgent);
        if (parser.getOS().name) {
          return parser.getDevice().model + ',' + parser.getOS().name + ',' + parser.getBrowser().name;
        }
      }
      return device.userAgent || 'Unknown Device';
    } catch (error) {
      return 'Unknown Device';
    }
  };

  DeviceManager.prototype.startAutoRefresh = function() {
    if (!this.config.autoRefresh) return;

    this.stopAutoRefresh();

    this.refreshTimer = setInterval(async () => {
      try {
        await this.loadDevices();
        await this.checkSession();
      } catch (error) {
        console.error('Auto refresh error:', error);
      }
    }, this.config.refreshInterval);
  };

  DeviceManager.prototype.stopAutoRefresh = function() {
    if (this.refreshTimer) {
      clearInterval(this.refreshTimer);
      this.refreshTimer = null;
    }
  };

  function UIRenderer(config, i18n, themeManager, eventManager) {
    this.config = config;
    this.i18n = i18n;
    this.themeManager = themeManager;
    this.eventManager = eventManager;
    this.modalElement = null;
    this.containerElement = null;
  }

  UIRenderer.prototype.renderModal = function() {
    if (!this.config.container) {
      throw new Error('Container is required for modal mode');
    }

    const container = typeof this.config.container === 'string'
      ? document.querySelector(this.config.container)
      : this.config.container;

    if (!container) {
      throw new Error('Container not found: ' + this.config.container);
    }

    container.innerHTML = '';

    const uniqueId = Date.now() + '_' + Math.random().toString(36).substr(2, 9);
    const modalId = 'fido2SdkModal_' + uniqueId;
    const sessionStatusId = 'fido2SessionStatus_' + uniqueId;
    const addBtnId = 'fido2AddDeviceBtn_' + uniqueId;
    const devicesListId = 'fido2DevicesList_' + uniqueId;

    const modal = document.createElement('div');
    modal.className = 'modal fade fido2-sdk-modal';
    modal.id = modalId;
    modal.tabIndex = -1;
    modal.setAttribute('aria-hidden', 'true');

    modal.innerHTML = this._getModalHTML(uniqueId, sessionStatusId, addBtnId, devicesListId);
    container.appendChild(modal);
    this.modalElement = modal;
    this.containerElement = container;
    this._uniqueId = uniqueId;

    this.themeManager.applyTheme(modal);

    const bootstrapModal = new window.bootstrap.Modal(modal, {
      backdrop: true,
      keyboard: true
    });

    bootstrapModal.show();

    modal.addEventListener('hidden.bs.modal', () => {
      this.eventManager.emit('close');
      this.cleanup();
    });

    this._bindEvents(uniqueId, addBtnId, devicesListId);

    return modal;
  };

  UIRenderer.prototype._getModalHTML = function(uniqueId, sessionStatusId, addBtnId, devicesListId) {
    const theme = this.config.theme;
    const dm = window.Fido2UIManager ? window.Fido2UIManager.deviceManager : null;
    const isRecoveryMode = dm ? dm.isInRecoveryMode() : false;
    const modalTitle = isRecoveryMode ? this.i18n.getText('title_recovery') : this.i18n.getText('my_devices');

    return `
      <div class="modal-dialog modal-dialog-centered modal-lg">
        <div class="modal-content fido2-sdk-card">
          <div class="modal-header fido2-sdk-header">
            ${theme.logo ? `<img src="${theme.logo}" class="fido2-sdk-logo" alt="Logo">` : ''}
            <h5 class="modal-title fido2-sdk-text">${modalTitle}</h5>
            ${this.config.features.showSessionStatus && !isRecoveryMode ? `<span class="badge fido2-sdk-status-badge" id="${sessionStatusId}"></span>` : ''}
            <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
          </div>
          <div class="modal-body fido2-sdk-container">
            ${this._getBodyHTML(uniqueId, addBtnId, devicesListId)}
          </div>
          <div class="modal-footer">
            ${isRecoveryMode ? `<button type="button" class="btn btn-secondary fido2-sdk-btn" id="fido2CancelRecoveryBtn_${uniqueId}">${this.i18n.getText('btn_cancel')}</button>` : ''}
            <button type="button" class="btn btn-secondary fido2-sdk-btn" data-bs-dismiss="modal">${this.i18n.getText('btn_close')}</button>
          </div>
        </div>
      </div>
    `;
  };

  UIRenderer.prototype._getBodyHTML = function(uniqueId, addBtnId, devicesListId) {
    const features = this.config.features;
    const dm = window.Fido2UIManager ? window.Fido2UIManager.deviceManager : null;
    const userId = dm ? dm.getEffectiveUserId() : (this.config.userId || '');
    const isRecoveryMode = dm ? dm.isInRecoveryMode() : false;
    const recoveryUser = dm ? dm.getRecoveryUser() : null;

    let html = '';

    if (isRecoveryMode && recoveryUser) {
      html += `
        <div class="alert alert-success mb-3 fido2-sdk-text">
          <strong>${this.i18n.getText('msg_recovery_user_found').replace('{user}', recoveryUser.displayName || recoveryUser.username)}</strong>
        </div>
      `;

      html += `
        <button type="button" class="btn btn-success mb-3 fido2-sdk-btn fido2-sdk-btn-primary" id="fido2StartRecoveryBtn_${uniqueId}">
          ${this.i18n.getText('btn_start_recovery')}
        </button>
      `;
    } else {
      if (features.showUserInfo && userId) {
        html += `<div class="fido2-sdk-user-info mb-3 fido2-sdk-text">
          <strong>${this.i18n.getText('title_welcome')}:</strong> ${userId}
        </div>`;
      }

      if (features.showAddButton) {
        html += `
          <button type="button" class="btn btn-info mb-3 fido2-sdk-btn fido2-sdk-btn-primary" id="${addBtnId}">
            ${this.i18n.getText('btn_add')}
          </button>
        `;
      }

      html += `
        <div class="table-responsive">
          <table class="table table-striped fido2-sdk-table">
            <thead>
              <tr>
                <th>${this.i18n.getText('title_device')}</th>
                <th>${this.i18n.getText('title_time')}</th>
                ${features.showDeleteButton ? `<th>${this.i18n.getText('title_act')}</th>` : ''}
              </tr>
            </thead>
            <tbody id="${devicesListId}">
              <tr>
                <td colspan="3" class="text-center fido2-sdk-text">${this.i18n.getText('title_empty_list')}</td>
              </tr>
            </tbody>
          </table>
        </div>
      `;
    }

    return html;
  };

  UIRenderer.prototype._bindEvents = function(uniqueId, addBtnId, devicesListId) {
    const container = this.modalElement || this.containerElement;
    if (!container) return;

    container.querySelectorAll('[data-fido2-action="delete"]').forEach(btn => {
      btn.replaceWith(btn.cloneNode(true));
    });

    const addBtn = container.querySelector('#' + addBtnId);
    if (addBtn) {
      addBtn.replaceWith(addBtn.cloneNode(true));
      const newAddBtn = container.querySelector('#' + addBtnId);
      newAddBtn.addEventListener('click', () => {
        this.eventManager.emit('addDevice');
      });
    }

    const startRecoveryBtn = container.querySelector('[id^="fido2StartRecoveryBtn_"]');
    if (startRecoveryBtn) {
      startRecoveryBtn.replaceWith(startRecoveryBtn.cloneNode(true));
      const newStartRecoveryBtn = container.querySelector('[id^="fido2StartRecoveryBtn_"]');
      if (newStartRecoveryBtn) {
        newStartRecoveryBtn.addEventListener('click', () => {
          this.eventManager.emit('startRecovery');
        });
      }
    }

    const cancelRecoveryBtn = container.querySelector('[id^="fido2CancelRecoveryBtn_"]');
    if (cancelRecoveryBtn) {
      cancelRecoveryBtn.replaceWith(cancelRecoveryBtn.cloneNode(true));
      const newCancelRecoveryBtn = container.querySelector('[id^="fido2CancelRecoveryBtn_"]');
      if (newCancelRecoveryBtn) {
        newCancelRecoveryBtn.addEventListener('click', () => {
          this.eventManager.emit('cancelRecovery');
        });
      }
    }

    container.querySelectorAll('[data-fido2-action="delete"]').forEach(btn => {
      btn.addEventListener('click', (e) => {
        e.preventDefault();
        const deviceId = btn.dataset.deviceId;
        if (deviceId && confirm(this.i18n.getText('msg_confirm_deldev'))) {
          this.eventManager.emit('deleteDevice', deviceId);
        }
      });
    });
  };

  UIRenderer.prototype.updateDevicesList = function(devices) {
    const container = this.modalElement || this.containerElement;
    const tbody = container ? container.querySelector('[id^="fido2DevicesList_"]') : null;
    if (!tbody) return;

    if (!devices || devices.length === 0) {
      tbody.innerHTML = `
        <tr>
          <td colspan="3" class="text-center fido2-sdk-text">${this.i18n.getText('title_empty_list')}</td>
        </tr>
      `;
      return;
    }

    let html = '';
    const dm = window.Fido2UIManager ? window.Fido2UIManager.deviceManager : null;

    devices.forEach(device => {
      const desc = dm ? dm.parseDeviceDescription(device) : device.desc || 'Unknown Device';
      const date = new Date(device.registered_time).toLocaleString();

      html += `
        <tr>
          <td class="fido2-sdk-text"><strong>${desc}</strong></td>
          <td class="fido2-sdk-text">${date}</td>
          ${this.config.features.showDeleteButton ? `
            <td>
              <a href="javascript:void(0)" 
                 class="text-danger" 
                 data-fido2-action="delete" 
                 data-device-id="${device.device_id}">
                <i class="bx bx-trash"></i> ${this.i18n.getText('title_del')}
              </a>
            </td>
          ` : ''}
        </tr>
      `;
    });

    tbody.innerHTML = html;
    this._bindEvents(this._uniqueId, 'fido2AddDeviceBtn_' + this._uniqueId, 'fido2DevicesList_' + this._uniqueId);
  };

  UIRenderer.prototype.updateSessionStatus = function(isValid) {
    const container = this.modalElement || this.containerElement;
    const badge = container ? container.querySelector('[id^="fido2SessionStatus_"]') : null;
    const addBtn = container ? container.querySelector('[id^="fido2AddDeviceBtn_"]') : null;

    if (badge) {
      if (isValid) {
        badge.className = 'badge bg-success fido2-sdk-status-badge';
        badge.textContent = this.i18n.getText('msg_session_status_ok');
      } else {
        badge.className = 'badge bg-danger fido2-sdk-status-badge';
        badge.textContent = this.i18n.getText('msg_session_status_fail');
      }
    }

    if (addBtn) {
      addBtn.disabled = false;
    }
  };

  UIRenderer.prototype.updateRecoveryUI = function() {
    const container = this.modalElement || this.containerElement;
    if (!container) return;

    const dm = window.Fido2UIManager ? window.Fido2UIManager.deviceManager : null;
    if (!dm) return;

    const isRecoveryMode = dm.isInRecoveryMode();
    const uniqueId = this._uniqueId;

    const titleEl = container.querySelector('.modal-title');
    if (titleEl) {
      titleEl.textContent = isRecoveryMode ? this.i18n.getText('title_recovery') : this.i18n.getText('my_devices');
    }

    const sessionBadge = container.querySelector('[id^="fido2SessionStatus_"]');
    if (sessionBadge) {
      sessionBadge.style.display = isRecoveryMode ? 'none' : 'inline';
    }

    const modalBody = container.querySelector('.modal-body');
    if (modalBody) {
      modalBody.innerHTML = this._getBodyHTML(uniqueId, 'fido2AddDeviceBtn_' + uniqueId, 'fido2DevicesList_' + uniqueId);
    }

    const modalFooter = container.querySelector('.modal-footer');
    if (modalFooter) {
      if (isRecoveryMode) {
        let cancelBtn = modalFooter.querySelector('[id^="fido2CancelRecoveryBtn_"]');
        if (!cancelBtn) {
          cancelBtn = document.createElement('button');
          cancelBtn.type = 'button';
          cancelBtn.className = 'btn btn-secondary fido2-sdk-btn';
          cancelBtn.id = 'fido2CancelRecoveryBtn_' + uniqueId;
          cancelBtn.textContent = this.i18n.getText('btn_cancel');
          modalFooter.insertBefore(cancelBtn, modalFooter.firstChild);
        }
      } else {
        const cancelBtn = modalFooter.querySelector('[id^="fido2CancelRecoveryBtn_"]');
        if (cancelBtn) {
          cancelBtn.remove();
        }
      }
    }

    this._bindEvents(uniqueId, 'fido2AddDeviceBtn_' + uniqueId, 'fido2DevicesList_' + uniqueId);
  };

  UIRenderer.prototype.close = function() {
    if (this.modalElement) {
      const bootstrapModal = window.bootstrap.Modal.getInstance(this.modalElement);
      if (bootstrapModal) {
        bootstrapModal.hide();
      }
    }
  };

  UIRenderer.prototype.cleanup = function() {
    if (this.modalElement) {
      this.themeManager.cleanup(this.modalElement);
      if (this.modalElement.parentNode) {
        this.modalElement.parentNode.removeChild(this.modalElement);
      }
      this.modalElement = null;
    }
    this.containerElement = null;
  };


  function Fido2UIManager() {
    this.config = null;
    this.i18n = null;
    this.eventManager = null;
    this.themeManager = null;
    this.deviceManager = null;
    this.uiRenderer = null;
    this.initialized = false;
  }

  Fido2UIManager.prototype.init = function(userConfig) {
    if (this.initialized) {
      console.warn('Fido2UIManager already initialized');
      return this;
    }

    if (!window.bootstrap) {
      throw new Error('Bootstrap is required. Please include Bootstrap JS before fido2-ui-sdk.js');
    }

    this.config = Object.assign({}, DEFAULT_CONFIG, userConfig);
    this.i18n = new I18nManager(this.config);
    this.eventManager = new EventManager();
    this.themeManager = new ThemeManager(this.config);
    this.deviceManager = new DeviceManager(this.config, this.i18n, this.eventManager);
    this.uiRenderer = new UIRenderer(this.config, this.i18n, this.themeManager, this.eventManager);

    if (this.config.serverUrl) {
      setFidoServerURL(this.config.serverUrl);
    }

    this._setupCallbacks();
    this.initialized = true;

    return this;
  };

  Fido2UIManager.prototype._setupCallbacks = function() {
    const callbacks = this.config.callbacks;

    if (callbacks.onInit) {
      this.eventManager.on('init', callbacks.onInit);
    }
    if (callbacks.onDeviceAdded) {
      this.eventManager.on('deviceAdded', callbacks.onDeviceAdded);
    }
    if (callbacks.onDeviceDeleted) {
      this.eventManager.on('deviceDeleted', callbacks.onDeviceDeleted);
    }
    if (callbacks.onDeviceListLoaded) {
      this.eventManager.on('deviceListLoaded', callbacks.onDeviceListLoaded);
    }
    if (callbacks.onError) {
      this.eventManager.on('error', callbacks.onError);
    }
    if (callbacks.onClose) {
      this.eventManager.on('close', callbacks.onClose);
    }
    if (callbacks.onUserMismatch) {
      this.eventManager.on('userMismatch', callbacks.onUserMismatch);
    }
  };

  Fido2UIManager.prototype.renderDeviceManager = function(config) {
    const manager = new Fido2UIManager();
    manager.init(config);

    window.Fido2UIManager = manager;

    const mode = manager.config.mode;

    manager.uiRenderer.renderModal();

    manager._bindInternalEvents();
    manager._loadInitialData();
    manager.deviceManager.startAutoRefresh();

    manager.eventManager.emit('init', manager);

    return manager;
  };

  Fido2UIManager.prototype._getI18nErrorMessage = function(error, defaultKey) {
    if (!error) return this.i18n.getText(defaultKey || 'error');
    
    const errorMessage = error.message || '';
    const errorName = error.name || '';
    
    // Check for browser-native WebAuthn errors with URL references
    if (errorMessage.includes('https://www.w3.org/TR/webauthn')) {
      // Check for common browser-native error patterns
      if (errorName === 'NotAllowedError' || errorMessage.toLowerCase().includes('not allowed')) {
        return this.i18n.getText('error_not_allowed');
      }
      if (errorMessage.toLowerCase().includes('timeout') || errorMessage.toLowerCase().includes('timed out')) {
        return this.i18n.getText('error_timeout');
      }
      if (errorMessage.toLowerCase().includes('invalid state') || errorMessage.toLowerCase().includes('already registered')) {
        return this.i18n.getText('error_invalid_state');
      }
      // Default to not allowed for browser privacy errors
      return this.i18n.getText('error_not_allowed');
    }
    
    // Check WebAuthn DOM error name first
    if (errorName && this._getWebAuthnErrorI18nKey) {
      const i18nKey = this._getWebAuthnErrorI18nKey(error);
      if (i18nKey && this.i18n.getText(i18nKey)) {
        return this.i18n.getText(i18nKey);
      }
    }
    
    // Check for Fido2LibErr codes in error message
    if (errorMessage.includes('Fido2LibErr')) {
      const match = errorMessage.match(/Fido2LibErr(\d+):/);
      if (match) {
        const i18nKey = 'error_fido2lib_' + match[1];
        if (this.i18n.getText(i18nKey)) {
          return this.i18n.getText(i18nKey);
        }
      }
    }
    
    // Check for SvrErr codes in error message
    if (errorMessage.includes('SvrErr')) {
      const match = errorMessage.match(/SvrErr(\d+):/);
      if (match) {
        const i18nKey = 'error_svr_' + match[1];
        if (this.i18n.getText(i18nKey)) {
          return this.i18n.getText(i18nKey);
        }
      }
    }
    
    return errorMessage || this.i18n.getText(defaultKey || 'error');
  };

  Fido2UIManager.prototype._getWebAuthnErrorI18nKey = function(error) {
    if (!error || !error.name) return null;
    
    const errorNameMap = {
      'InvalidStateError': 'error_invalid_state',
      'NotAllowedError': 'error_not_allowed',
      'AbortError': 'error_abort',
      'NotSupportedError': 'error_not_supported',
      'SecurityError': 'error_security',
      'NetworkError': 'error_network',
      'ConstraintError': 'error_constraint',
      'NotReadableError': 'error_not_readable',
      'EncodingError': 'error_encoding',
      'DataError': 'error_data',
      'TimeoutError': 'error_timeout'
    };
    
    return errorNameMap[error.name] || null;
  };

  Fido2UIManager.prototype._bindInternalEvents = function() {
    const self = this;

    this.eventManager.on('addDevice', async () => {
      const addBtn = document.getElementById('fido2AddDeviceBtn');
      if (addBtn) addBtn.disabled = true;
      try {
        await self.deviceManager.addDevice();
        self.uiRenderer.updateDevicesList(self.deviceManager.devices);
        // Re-check session after device list is updated
        await self.deviceManager.checkSession();
        self.uiRenderer.updateSessionStatus(self.deviceManager.sessionStatus);
        alert(self.i18n.getText('msg_register_ok'));
      } catch (error) {
        console.error('Add device error:', error);
        alert(self._getI18nErrorMessage(error, 'error'));
      } finally {
        if (addBtn) addBtn.disabled = false;
      }
    });

    this.eventManager.on('deleteDevice', async (deviceId) => {
      try {
        await self.deviceManager.deleteDevice(deviceId);
        self.uiRenderer.updateDevicesList(self.deviceManager.devices);
        await self.deviceManager.checkSession();
        alert(self.i18n.getText('msg_deldev_ok'));
      } catch (error) {
        console.error('Delete device error:', error);
        alert(self._getI18nErrorMessage(error, 'error'));
      }
    });

    this.eventManager.on('deviceListLoaded', (devices) => {
      self.uiRenderer.updateDevicesList(devices);
    });

    this.eventManager.on('sessionStatusChanged', (isValid) => {
      self.uiRenderer.updateSessionStatus(isValid);
    });

    this.eventManager.on('startRecovery', async () => {
      const recoveryBtn = document.querySelector('[id^="fido2StartRecoveryBtn_"]');
      if (recoveryBtn) recoveryBtn.disabled = true;
      try {
        const result = await self.deviceManager.finishRecovery();
        if (result.success) {
          alert(self.i18n.getText('msg_recovery_success'));
          self.uiRenderer.updateDevicesList(self.deviceManager.devices);
          await self.deviceManager.checkSession();
          self.uiRenderer.updateSessionStatus(self.deviceManager.sessionStatus);
        }
      } catch (error) {
        console.error('Recovery error:', error);
        alert(error.message || self.i18n.getText('msg_recovery_failed'));
      } finally {
        if (recoveryBtn) recoveryBtn.disabled = false;
      }
    });

    this.eventManager.on('cancelRecovery', async () => {
      self.deviceManager.cancelRecovery();
      self.close();
    });

    this.eventManager.on('recoveryStarted', (user) => {
      console.log('Recovery started for user:', user);
      self.uiRenderer.updateRecoveryUI();
    });

    this.eventManager.on('recoveryCompleted', (result) => {
      console.log('Recovery completed:', result);
    });

    this.eventManager.on('recoveryCanceled', (user) => {
      console.log('Recovery canceled for user:', user);
    });

    this.eventManager.on('error', (error) => {
      console.error('FIDO2 SDK Error:', error);
    });
  };

  Fido2UIManager.prototype._loadInitialData = async function() {
    try {
      if (this.config.recoverySessionId) {
        await this.deviceManager.startRecovery(this.config.recoverySessionId);
        this.uiRenderer.cleanup();
        this.uiRenderer.renderModal();
        this._bindInternalEvents();
      } else {
        await this.deviceManager.loadDevices();
        await this.deviceManager.checkSession();
      }
    } catch (error) {
      console.error('Initial data load error:', error);
      if (this.config.recoverySessionId) {
        alert(error.message || this.i18n.getText('msg_recovery_failed'));
        this.close();
      }
    }
  };

  Fido2UIManager.prototype.close = function() {
    if (this.uiRenderer) {
      this.uiRenderer.close();
    }
  };

  Fido2UIManager.prototype.refresh = async function() {
    if (this.deviceManager) {
      await this.deviceManager.loadDevices();
      await this.deviceManager.checkSession();
    }
  };

  Fido2UIManager.prototype.logout = function() {
    if (typeof logoutFido2UserSession === 'function') {
      logoutFido2UserSession();
    }
    this.eventManager.emit('logout');
  };

  Fido2UIManager.prototype.destroy = function() {
    if (this.deviceManager) {
      this.deviceManager.stopAutoRefresh();
    }

    if (this.uiRenderer) {
      this.uiRenderer.cleanup();
    }

    this.initialized = false;
  };

  Fido2UIManager.prototype.renderDeviceManagerWithRecovery = function(config) {
    const recoveryConfig = Object.assign({}, config, {
      recoverySessionId: config.recoverySessionId || config.recoverySessionId
    });
    return this.renderDeviceManager(recoveryConfig);
  };

  Fido2UIManager.prototype.renderFromUrlParams = function(config) {
    const url = new URL(window.location.href);
    const params = url.searchParams;
    const regSessionId = params.get('rid');

    if (regSessionId) {
      return this.renderDeviceManagerWithRecovery(Object.assign({}, config, {
        recoverySessionId: regSessionId
      }));
    } else {
      return this.renderDeviceManager(config);
    }
  };

  Fido2UIManager.prototype.startRecovery = function(recoverySessionId, displayName) {
    return this.deviceManager.finishRecovery(displayName);
  };

  Fido2UIManager.prototype.isRecoveryMode = function() {
    return this.deviceManager ? this.deviceManager.isInRecoveryMode() : false;
  };

  Fido2UIManager.prototype.getRecoveryUser = function() {
    return this.deviceManager ? this.deviceManager.getRecoveryUser() : null;
  };

  Fido2UIManager.prototype.renderLogin = function(config) {
    if (!window.bootstrap) {
      throw new Error('Bootstrap is required. Please include Bootstrap JS before fido2-ui-sdk.js');
    }

    const login = new Fido2Login(config);

    login.show();

    return login;
  };

  window.Fido2UIManager = new Fido2UIManager();
  window.Fido2UIManager.renderDeviceManager = Fido2UIManager.prototype.renderDeviceManager;
  window.Fido2UIManager.renderLogin = Fido2UIManager.prototype.renderLogin;
  window.Fido2UIManager.renderDeviceManagerWithRecovery = Fido2UIManager.prototype.renderDeviceManagerWithRecovery;
  window.Fido2UIManager.renderFromUrlParams = Fido2UIManager.prototype.renderFromUrlParams;
  window.Fido2UIManager.close = function() {
    if (window.Fido2UIManager && window.Fido2UIManager.close) {
      window.Fido2UIManager.close();
    }
  };
  window.Fido2UIManager.refresh = function() {
    if (window.Fido2UIManager && window.Fido2UIManager.refresh) {
      window.Fido2UIManager.refresh();
    }
  };
  window.Fido2UIManager.logout = function() {
    if (window.Fido2UIManager && window.Fido2UIManager.logout) {
      window.Fido2UIManager.logout();
    }
  };
  window.Fido2UIManager.destroy = function() {
    if (window.Fido2UIManager && window.Fido2UIManager.destroy) {
      window.Fido2UIManager.destroy();
    }
  };
  window.Fido2UIManager.isRecoveryMode = function() {
    if (window.Fido2UIManager && window.Fido2UIManager.isRecoveryMode) {
      return window.Fido2UIManager.isRecoveryMode();
    }
    return false;
  };
  window.Fido2UIManager.getRecoveryUser = function() {
    if (window.Fido2UIManager && window.Fido2UIManager.getRecoveryUser) {
      return window.Fido2UIManager.getRecoveryUser();
    }
    return null;
  };
  window.Fido2UIManager.startRecovery = function(recoverySessionId, displayName) {
    if (window.Fido2UIManager && window.Fido2UIManager.startRecovery) {
      return window.Fido2UIManager.startRecovery(recoverySessionId, displayName);
    }
    return Promise.reject(new Error('Recovery not initialized'));
  };

  console.log('FIDO2 UI SDK v' + FIDO2_UI_VERSION + ' loaded');
})(window);
