// 导入所需的依赖
import { ICONS } from './icons.js';
import {
  ENABLE_DATA_SYNC_KEY,
  getDataSyncEnabled,
  setDataSyncEnabled,
  setSyncedData
} from './data-sync.js';
import { checkForUpdate, compareVersions, formatUpdateStatus } from './update-checker.js';
import { clearFaviconCache, applyFaviconWithFallback } from './favicon-utils.js';
import {
  AI_PROVIDER_PRESETS,
  HOSTED_AI_MODELS,
  DEFAULT_HOSTED_MODEL,
  loadAiOrganizeSettings,
  saveAiOrganizeSettings,
  ensureAiHostPermission,
  isHostedAiProvider
} from './ai-bookmark-organize.js';
import {
  PRO_FEATURES,
  refreshMembershipStatus,
  getCachedMembershipStatus,
  signInWithGoogle,
  clearMembershipSession,
  startProCheckout,
  openBillingPortal,
  getMembershipErrorMessage,
  isFeatureEntitled,
  requireProFeature,
  fetchProductCatalog,
  formatCatalogPrice,
  formatBillingPlanLabel,
  planHasCheckout,
  getAvailableBillingIntervals,
  getCatalogTiers,
  getCatalogPromo,
  isCatalogPromoLive,
  formatPromoCountdown,
  formatDiscountDurationLabel,
  redeemActivationCode,
  featureLabelFromCatalog,
  labelFromFeatureDef,
  getPlanLimitForFeature,
  isPlanConfigFeatureId
} from './membership.js';

// 设置管理器类
class SettingsManager {
  constructor() {
    this.settingsModal = document.getElementById('settings-modal');
    this.settingsSidebar = document.getElementById('settings-sidebar');
    this.settingsOverlay = document.getElementById('settings-overlay');
    this.settingsIcon = document.querySelector('.settings-icon a');
    this.closeButton = document.querySelector('.settings-sidebar-close');
    this.tabButtons = document.querySelectorAll('.settings-tab-button');
    this.tabContents = document.querySelectorAll('.settings-tab-content');
    this.bgOptions = document.querySelectorAll('.settings-bg-option');
    this.accentOptions = document.querySelectorAll('.settings-accent-option');
    this.enableQuickLinksCheckbox = document.getElementById('enable-quick-links');
    this.openInNewTabCheckbox = document.getElementById('open-in-new-tab');
    
    this.widthSettings = document.getElementById('floating-width-settings');
    this.widthSlider = document.getElementById('width-slider');
    this.widthValue = document.getElementById('width-value');
    this.widthPreviewCount = document.getElementById('width-preview-count');
    this.settingsModalContent = document.querySelector('.settings-modal-content');
    this.showHistorySuggestionsCheckbox = document.getElementById('show-history-suggestions');
    this.showBookmarkSuggestionsCheckbox = document.getElementById('show-bookmark-suggestions');
    this.enableWheelSwitchingCheckbox = document.getElementById('enable-wheel-switching');
    this.openSearchInNewTabCheckbox = document.getElementById('open-search-in-new-tab');
    this.init();
  }

  init() {
    this.loadSavedSettings();
    this.initEventListeners();
    this.initTheme();
    this.initGlassOpacitySettings();
    this.initDataSyncSettings();
    this.initAiOrganizeSettings();
    this.initAccountSettings();

    const uiLanguageSelect = document.getElementById('ui-language-select');
    if (uiLanguageSelect) {
      this.initLanguageSettings();
    }
    
    // 只在相关元素存在时才调用各个初始化方法
    if (this.enableQuickLinksCheckbox) {
      this.initQuickLinksSettings();
    }

    if (document.getElementById('enable-plugin-bar')) {
      this.initPluginBarSettings();
    }
    
    if (this.openInNewTabCheckbox) {
      this.initLinkOpeningSettings();
    }
    
    // 检查宽度设置相关元素
    if (this.widthSlider && this.widthValue) {
      this.initBookmarkWidthSettings();
    }
    
    // 检查高度设置相关元素
    const heightSlider = document.getElementById('height-slider');
    const heightValue = document.getElementById('height-value');
    if (heightSlider && heightValue) {
      this.initCardHeightSettings();
    }
    
    // 检查容器宽度设置相关元素
    const containerWidthSlider = document.getElementById('container-width-slider');
    if (containerWidthSlider) {
      this.initContainerWidthSettings();
    }
    
    // 检查布局设置相关元素
    const showSearchBoxCheckbox = document.getElementById('show-search-box');
    const showWelcomeMessageCheckbox = document.getElementById('show-welcome-message');
    const showFooterCheckbox = document.getElementById('show-footer');
    if (showSearchBoxCheckbox || showWelcomeMessageCheckbox || showFooterCheckbox) {
      this.initLayoutSettings();
    }
    
    // 检查搜索建议设置相关元素
    if (this.showHistorySuggestionsCheckbox || this.showBookmarkSuggestionsCheckbox) {
      this.initSearchSuggestionsSettings();
    }
    
    // 检查滚轮切换设置相关元素
    if (this.enableWheelSwitchingCheckbox) {
      this.initWheelSwitchingTab();
    }

    this.initUpdateCheck();
    this.initQuickGuideButton();
    this.initClearCache();
  }

  initQuickGuideButton() {
    const btn = document.getElementById('show-quick-guide-button');
    if (!btn) return;
    btn.addEventListener('click', () => {
      this.closeSettingsSidebar();
      const start = window.SuperMarkQuickGuide?.start;
      if (typeof start === 'function') {
        // Allow settings close animation to finish so highlights land on the page
        setTimeout(() => start({ force: true }), 180);
      }
    });
  }

  initEventListeners() {
    // 打开设置模态窗口
    this.settingsIcon.addEventListener('click', (e) => {
      e.preventDefault();
      this.openSettingsSidebar();
    });

    // 关闭设置模态窗口
    if (this.closeButton) {
      this.closeButton.addEventListener('click', () => {
        this.closeSettingsSidebar();
        
        // 关闭时更新欢迎消息
        if (window.WelcomeManager) {
          window.WelcomeManager.updateWelcomeMessage();
        }
      });
    }

    // 标签切换
    this.tabButtons.forEach(button => {
      button.addEventListener('click', () => {
        const tabName = button.getAttribute('data-tab');
        this.switchTab(tabName);
      });
    });

    // Theme color (accent) + Background color
    this.accentOptions.forEach((option) => {
      option.addEventListener('click', () => this.handleAccentChange(option));
    });
    this.bgOptions.forEach((option) => {
      option.addEventListener('click', () => this.handleBackgroundChange(option));
    });

    this.initCustomThemeColorPicker();
    this.initCustomBackgroundColorPicker();

    // ESC 关闭
    document.addEventListener('keydown', (e) => {
      if (e.key === 'Escape' && this.settingsSidebar && this.settingsSidebar.classList.contains('open')) {
        this.closeSettingsSidebar();
      }
    });

    // 点击遮罩关闭
    if (this.settingsOverlay) {
      this.settingsOverlay.addEventListener('click', () => {
        this.closeSettingsSidebar();
        if (window.WelcomeManager) {
          window.WelcomeManager.updateWelcomeMessage();
        }
      });
    }
    
    // 阻止模态内容点击冒泡到遮罩/文档
    if (this.settingsSidebar) {
      this.settingsSidebar.addEventListener('click', (e) => {
        if (e.target.tagName === 'A' || e.target.closest('a')) {
          return;
        }
        e.stopPropagation();
      });
    }
    
    // 阻止设置图标点击冒泡
    this.settingsIcon.addEventListener('click', (e) => {
      e.stopPropagation();
    });
  }

  // 打开设置模态窗口
  openSettingsSidebar() {
    if (this.settingsSidebar) {
      this.settingsSidebar.classList.add('open');
    }
    if (this.settingsOverlay) {
      this.settingsOverlay.classList.add('open');
    }
    document.body.style.overflow = 'hidden';
    // 自动检查开启时：打开设置后台检查，发现新版本则弹窗
    this.maybeAutoCheckUpdates({ silent: true, promptIfAvailable: true });
  }
  
  // 关闭设置模态窗口
  closeSettingsSidebar() {
    if (this.settingsSidebar) {
      this.settingsSidebar.classList.remove('open');
    }
    if (this.settingsOverlay) {
      this.settingsOverlay.classList.remove('open');
    }
    document.body.style.overflow = '';
  }

  switchTab(tabName) {
    // 移除所有标签的 active 类
    this.tabButtons.forEach(button => {
      button.classList.remove('active');
    });
    
    // 移除所有内容的 active 类
    this.tabContents.forEach(content => {
      content.classList.remove('active');
    });
    
    // 添加当前标签的 active 类
    const selectedButton = document.querySelector(`[data-tab="${tabName}"]`);
    const selectedContent = document.getElementById(`${tabName}-settings`);
    
    if (selectedButton && selectedContent) {
      selectedButton.classList.add('active');
      selectedContent.classList.add('active');
      // 更新 UI 语言
      window.updateUILanguage();
      
      // 确保欢迎消息也被更新
      if (window.WelcomeManager) {
        window.WelcomeManager.updateWelcomeMessage();
      }

      if (tabName === 'about') {
        this.runUpdateCheck({
          force: false,
          silent: false,
          promptIfAvailable: this.isAutoCheckUpdatesEnabled()
        });
      }
    }
  }

  initCustomThemeColorPicker() {
    this.customThemeColorPicker = document.getElementById('custom-theme-color-picker');
    this.customThemeColorOption = document.querySelector('.settings-custom-accent-option');
    if (!this.customThemeColorPicker) return;

    const savedCustom =
      (typeof window.getCustomThemeColor === 'function' && window.getCustomThemeColor()) ||
      localStorage.getItem('customThemeColor') ||
      '#4A9FE8';
    this.customThemeColorPicker.value = savedCustom;
    if (this.customThemeColorOption) {
      this.customThemeColorOption.style.setProperty('--custom-swatch-color', savedCustom);
    }
    if (typeof window.isCustomThemeAccentActive === 'function' && window.isCustomThemeAccentActive()) {
      this.customThemeColorOption?.classList.add('active');
      this.accentOptions.forEach((opt) => opt.classList.remove('active'));
    }

    this.customThemeColorPicker.addEventListener('input', (event) => {
      this.handleCustomThemeColorChange(event.target.value);
    });
    this.customThemeColorPicker.addEventListener('change', (event) => {
      this.handleCustomThemeColorChange(event.target.value, { persistSync: true });
    });
  }

  initCustomBackgroundColorPicker() {
    this.customBackgroundColorPicker = document.getElementById('custom-background-color-picker');
    this.customBackgroundColorOption = document.querySelector('.settings-custom-bg-option');
    if (!this.customBackgroundColorPicker) return;

    const savedCustom =
      (typeof window.getCustomBackgroundColor === 'function' && window.getCustomBackgroundColor()) ||
      localStorage.getItem('customBackgroundColor') ||
      '#4A9FE8';
    this.customBackgroundColorPicker.value = savedCustom;
    if (this.customBackgroundColorOption) {
      this.customBackgroundColorOption.style.setProperty('--custom-swatch-color', savedCustom);
    }
    if (
      localStorage.getItem('useDefaultBackground') === 'true' &&
      localStorage.getItem('selectedBackground') === (window.CUSTOM_THEME_BG_CLASS || 'custom-theme-background')
    ) {
      this.customBackgroundColorOption?.classList.add('active');
      this.bgOptions.forEach((opt) => opt.classList.remove('active'));
    }

    this.customBackgroundColorPicker.addEventListener('input', (event) => {
      this.handleCustomBackgroundColorChange(event.target.value);
    });
    this.customBackgroundColorPicker.addEventListener('change', (event) => {
      this.handleCustomBackgroundColorChange(event.target.value, { persistSync: true });
    });
  }

  handleAccentChange(option) {
    const accentKey = option.getAttribute('data-accent');
    this.accentOptions.forEach((opt) => opt.classList.remove('active'));
    this.customThemeColorOption?.classList.remove('active');
    option.classList.add('active');

    if (typeof window.applyThemeAccent === 'function') {
      window.applyThemeAccent(accentKey, { persist: true });
    }
  }

  handleCustomThemeColorChange(hex, { persistSync = false } = {}) {
    if (typeof window.applyCustomThemeAccent !== 'function') return;

    // Accent only — do not change page background or clear wallpaper
    window.applyCustomThemeAccent(hex, { applyBackground: false, persist: true });
    this.accentOptions.forEach((opt) => opt.classList.remove('active'));
    this.customThemeColorOption?.classList.add('active');

    if (persistSync) {
      const normalized = window.normalizeThemeHex?.(hex) || hex;
      setSyncedData({
        customThemeColor: normalized,
        themeAccentMode: 'custom',
        themeAccentBackground: window.CUSTOM_THEME_BG_CLASS || 'custom-theme-background'
      });
    }
  }

  handleCustomBackgroundColorChange(hex, { persistSync = false } = {}) {
    if (typeof window.applyCustomPageBackground !== 'function') return;

    window.applyCustomPageBackground(hex, { persist: true, clearWallpaper: true });
    this.bgOptions.forEach((opt) => opt.classList.remove('active'));
    this.customBackgroundColorOption?.classList.add('active');

    if (window.WelcomeManager) {
      window.WelcomeManager.updateWelcomeMessage();
    }

    if (persistSync) {
      const normalized = window.normalizeThemeHex?.(hex) || hex;
      setSyncedData({
        selectedBackground: window.CUSTOM_THEME_BG_CLASS || 'custom-theme-background',
        useDefaultBackground: true,
        customBackgroundColor: normalized
      });
    }
  }

  handleBackgroundChange(option) {
    const bgClass = option.getAttribute('data-bg');

    this.bgOptions.forEach((opt) => opt.classList.remove('active'));
    this.customBackgroundColorOption?.classList.remove('active');
    option.classList.add('active');

    if (typeof window.applyPageBackground === 'function') {
      window.applyPageBackground(bgClass, { persist: true, clearWallpaper: true });
    } else {
      document.documentElement.className = bgClass;
      localStorage.setItem('selectedBackground', bgClass);
      localStorage.setItem('useDefaultBackground', 'true');
      this.clearWallpaper();
    }

    if (window.WelcomeManager) {
      window.WelcomeManager.updateWelcomeMessage();
    }
  }

  clearWallpaper() {
    document.querySelectorAll('.wallpaper-option, .bing-wallpaper-item').forEach((opt) => {
      opt.classList.remove('active');
    });

    const mainElement = document.querySelector('main');
    if (mainElement) {
      mainElement.style.backgroundImage = 'none';
      document.body.style.backgroundImage = 'none';
    }
    document.body.classList.remove('has-wallpaper');
    localStorage.removeItem('originalWallpaper');
    if (typeof window.persistThemePreference === 'function') {
      window.persistThemePreference({ useDefaultBackground: true });
    }

    const welcomeElement = document.getElementById('welcome-message');
    if (welcomeElement && window.WelcomeManager) {
      window.WelcomeManager.adjustTextColor(welcomeElement);
    }
  }

  loadSavedSettings() {
    const applyAccentUi = () => {
      const mode = localStorage.getItem('themeAccentMode');
      const accentKey = localStorage.getItem('themeAccentBackground');
      const customColor = localStorage.getItem('customThemeColor');

      if (mode === 'custom' && customColor) {
        if (typeof window.applyCustomThemeAccent === 'function') {
          window.applyCustomThemeAccent(customColor, { applyBackground: false, persist: false });
        }
        this.accentOptions.forEach((opt) => opt.classList.remove('active'));
        this.customThemeColorOption?.classList.add('active');
        if (this.customThemeColorPicker) this.customThemeColorPicker.value = customColor;
        return;
      }

      const key = accentKey && accentKey !== (window.CUSTOM_THEME_BG_CLASS || 'custom-theme-background')
        ? accentKey
        : 'gradient-background-7';
      if (typeof window.applyThemeAccent === 'function') {
        window.applyThemeAccent(key, { persist: false });
      }
      this.accentOptions.forEach((option) => {
        option.classList.toggle('active', option.getAttribute('data-accent') === key);
      });
      this.customThemeColorOption?.classList.remove('active');
    };

    const applyBackgroundUi = () => {
      const hasWallpaper = !!localStorage.getItem('originalWallpaper');
      const useDefault = localStorage.getItem('useDefaultBackground') === 'true';
      if (hasWallpaper && !useDefault) {
        this.bgOptions.forEach((opt) => opt.classList.remove('active'));
        this.customBackgroundColorOption?.classList.remove('active');
        return;
      }
      if (!useDefault) return;

      const savedBg = localStorage.getItem('selectedBackground');
      if (!savedBg) return;

      if (savedBg === (window.CUSTOM_THEME_BG_CLASS || 'custom-theme-background')) {
        const hex =
          localStorage.getItem('customBackgroundColor') ||
          localStorage.getItem('customThemeColor') ||
          '#4A9FE8';
        if (typeof window.applyCustomPageBackground === 'function') {
          window.applyCustomPageBackground(hex, { persist: false, clearWallpaper: false });
        }
        this.bgOptions.forEach((opt) => opt.classList.remove('active'));
        this.customBackgroundColorOption?.classList.add('active');
        if (this.customBackgroundColorPicker) this.customBackgroundColorPicker.value = hex;
        return;
      }

      if (typeof window.applyPageBackground === 'function') {
        window.applyPageBackground(savedBg, { persist: false, clearWallpaper: false });
      } else {
        document.documentElement.className = savedBg;
      }
      this.bgOptions.forEach((option) => {
        option.classList.toggle('active', option.getAttribute('data-bg') === savedBg);
      });
      this.customBackgroundColorOption?.classList.remove('active');
    };

    applyAccentUi();
    applyBackgroundUi();

    chrome.storage.sync.get(
      [
        'selectedBackground',
        'useDefaultBackground',
        'customThemeColor',
        'customBackgroundColor',
        'themeAccentMode',
        'themeAccentBackground'
      ],
      (result) => {
        if (result.customThemeColor) localStorage.setItem('customThemeColor', result.customThemeColor);
        if (result.customBackgroundColor) {
          localStorage.setItem('customBackgroundColor', result.customBackgroundColor);
        }
        if (result.themeAccentMode) localStorage.setItem('themeAccentMode', result.themeAccentMode);
        if (result.themeAccentBackground) {
          localStorage.setItem('themeAccentBackground', result.themeAccentBackground);
        }
        if (result.selectedBackground) {
          localStorage.setItem('selectedBackground', result.selectedBackground);
        }
        if (result.useDefaultBackground != null) {
          const hasWallpaper = !!localStorage.getItem('originalWallpaper');
          if (hasWallpaper && (result.useDefaultBackground === false || result.useDefaultBackground === 'false')) {
            localStorage.setItem('useDefaultBackground', 'false');
          } else if (hasWallpaper && (result.useDefaultBackground === true || result.useDefaultBackground === 'true')) {
            // Prefer saved wallpaper over stale solid-bg sync flag
            localStorage.setItem('useDefaultBackground', 'false');
          } else if (result.useDefaultBackground === true || result.useDefaultBackground === 'true') {
            localStorage.setItem('useDefaultBackground', 'true');
          } else if (result.useDefaultBackground === false || result.useDefaultBackground === 'false') {
            localStorage.setItem('useDefaultBackground', 'false');
          }
        }
        applyAccentUi();
        applyBackgroundUi();
      }
    );
  }

  initDataSyncSettings() {
    const checkbox = document.getElementById('enable-data-sync');
    if (!checkbox) return;

    getDataSyncEnabled().then((enabled) => {
      checkbox.checked = enabled;
    });

    checkbox.addEventListener('change', async () => {
      const enabled = checkbox.checked;
      await setDataSyncEnabled(enabled);
      if (enabled) {
        const theme = localStorage.getItem('theme') || 'auto';
        const selectedBackground = localStorage.getItem('selectedBackground');
        const payload = { theme };
        if (selectedBackground) {
          payload.selectedBackground = selectedBackground;
          payload.useDefaultBackground = localStorage.getItem('useDefaultBackground') === 'true';
        }
        const customThemeColor = localStorage.getItem('customThemeColor');
        const customBackgroundColor = localStorage.getItem('customBackgroundColor');
        const themeAccentMode = localStorage.getItem('themeAccentMode');
        const themeAccentBackground = localStorage.getItem('themeAccentBackground');
        if (customThemeColor) payload.customThemeColor = customThemeColor;
        if (customBackgroundColor) payload.customBackgroundColor = customBackgroundColor;
        if (themeAccentMode) payload.themeAccentMode = themeAccentMode;
        if (themeAccentBackground) payload.themeAccentBackground = themeAccentBackground;
        chrome.storage.sync.set(payload);
      }
    });

    chrome.storage.onChanged.addListener((changes, area) => {
      if (!changes[ENABLE_DATA_SYNC_KEY]) return;
      if (area !== 'sync' && area !== 'local') return;
      const enabled = changes[ENABLE_DATA_SYNC_KEY].newValue !== false;
      checkbox.checked = enabled;
    });
  }

  initAiOrganizeSettings() {
    const providerSelect = document.getElementById('ai-organize-provider');
    const apiKeyInput = document.getElementById('ai-organize-api-key');
    const baseUrlInput = document.getElementById('ai-organize-base-url');
    const modelInput = document.getElementById('ai-organize-model');
    const hostedModelSelect = document.getElementById('ai-organize-hosted-model');
    const saveButton = document.getElementById('ai-organize-save-button');
    const statusEl = document.getElementById('ai-organize-settings-status');
    const byokFields = document.querySelectorAll('[data-ai-byok-field]');
    const hostedFields = document.querySelectorAll('[data-ai-hosted-field]');

    if (!providerSelect || !apiKeyInput || !baseUrlInput || !modelInput || !saveButton) {
      return;
    }

    const setStatus = (message, type = '') => {
      if (!statusEl) return;
      if (!message) {
        statusEl.hidden = true;
        statusEl.textContent = '';
        statusEl.classList.remove('is-success', 'is-error');
        return;
      }
      statusEl.hidden = false;
      statusEl.textContent = message;
      statusEl.classList.toggle('is-success', type === 'success');
      statusEl.classList.toggle('is-error', type === 'error');
    };

    const setHostedMode = (hosted) => {
      byokFields.forEach((el) => {
        el.hidden = hosted;
        el.classList.toggle('ai-byok-hidden', hosted);
      });
      hostedFields.forEach((el) => {
        el.hidden = !hosted;
        el.classList.toggle('ai-byok-hidden', !hosted);
      });
    };

    const fillProviderOptions = () => {
      providerSelect.innerHTML = '';
      for (const preset of AI_PROVIDER_PRESETS) {
        const option = document.createElement('option');
        option.value = preset.id;
        option.textContent = this.i18nMessage(preset.labelKey, preset.id);
        providerSelect.appendChild(option);
      }
    };

    const fillHostedModelOptions = () => {
      if (!hostedModelSelect) return;
      hostedModelSelect.innerHTML = '';
      for (const model of HOSTED_AI_MODELS) {
        const option = document.createElement('option');
        option.value = model.id;
        option.textContent = model.label;
        hostedModelSelect.appendChild(option);
      }
    };

    const applyPresetFields = (providerId) => {
      const preset = AI_PROVIDER_PRESETS.find((item) => item.id === providerId)
        || AI_PROVIDER_PRESETS[0];
      setHostedMode(preset.id === 'hosted');
      if (preset.id === 'hosted' || preset.id === 'custom') return;
      baseUrlInput.value = preset.baseUrl;
      modelInput.value = preset.model;
    };

    fillProviderOptions();
    fillHostedModelOptions();
    setHostedMode(true);

    loadAiOrganizeSettings().then(async (settings) => {
      providerSelect.value = settings.provider || 'hosted';
      apiKeyInput.value = settings.apiKey || '';
      baseUrlInput.value = settings.baseUrl || '';
      modelInput.value = settings.model || '';
      if (hostedModelSelect) {
        hostedModelSelect.value = settings.hostedModel || DEFAULT_HOSTED_MODEL;
      }
      setHostedMode(isHostedAiProvider(settings) || providerSelect.value === 'hosted');
      if (!isHostedAiProvider(settings) && (!baseUrlInput.value || !modelInput.value)) {
        applyPresetFields(providerSelect.value);
      }
    });

    providerSelect.addEventListener('change', async () => {
      if (providerSelect.value !== 'hosted') {
        const allowed = await requireProFeature(PRO_FEATURES.CUSTOMIZE_MODEL_PROVIDERS);
        if (!allowed) {
          providerSelect.value = 'hosted';
          applyPresetFields('hosted');
          setHostedMode(true);
          return;
        }
      }
      applyPresetFields(providerSelect.value);
      setStatus('');
    });

    // Persist hosted model as soon as the user picks one
    hostedModelSelect?.addEventListener('change', async () => {
      if (providerSelect.value !== 'hosted') return;
      try {
        await saveAiOrganizeSettings({
          provider: 'hosted',
          hostedModel: hostedModelSelect.value || DEFAULT_HOSTED_MODEL,
          apiKey: apiKeyInput.value,
          baseUrl: baseUrlInput.value,
          model: modelInput.value
        });
        setStatus(
          this.i18nMessage('aiOrganizeSettingsSaved', 'AI settings saved'),
          'success'
        );
      } catch (error) {
        console.error('Failed to save hosted model:', error);
      }
    });

    saveButton.addEventListener('click', async () => {
      setStatus('');
      saveButton.disabled = true;
      try {
        if (providerSelect.value === 'hosted') {
          await saveAiOrganizeSettings({
            provider: 'hosted',
            hostedModel: hostedModelSelect?.value || DEFAULT_HOSTED_MODEL,
            apiKey: apiKeyInput.value,
            baseUrl: baseUrlInput.value,
            model: modelInput.value
          });
          setStatus(
            this.i18nMessage('aiOrganizeSettingsSaved', 'AI settings saved'),
            'success'
          );
          return;
        }

        const allowedByok = await requireProFeature(PRO_FEATURES.CUSTOMIZE_MODEL_PROVIDERS);
        if (!allowedByok) {
          providerSelect.value = 'hosted';
          applyPresetFields('hosted');
          setHostedMode(true);
          return;
        }

        const draft = {
          provider: providerSelect.value,
          apiKey: apiKeyInput.value,
          baseUrl: baseUrlInput.value,
          model: modelInput.value,
          hostedModel: hostedModelSelect?.value || DEFAULT_HOSTED_MODEL
        };

        const normalizedBase = String(draft.baseUrl || '').trim().replace(/\/+$/, '');
        let permissionGranted = true;

        if (normalizedBase) {
          try {
            permissionGranted = await ensureAiHostPermission(normalizedBase);
          } catch (permError) {
            console.error('AI host permission error:', permError);
            if (permError?.message === 'INVALID_API_BASE_URL') {
              setStatus(
                this.i18nMessage(
                  'aiOrganizeInvalidBaseUrl',
                  'Invalid API Base URL. Use a full URL like https://openrouter.ai/api/v1'
                ),
                'error'
              );
              return;
            }
            permissionGranted = false;
          }
        }

        await saveAiOrganizeSettings(draft);

        if (normalizedBase && !permissionGranted) {
          setStatus(
            this.i18nMessage(
              'aiOrganizePermissionDenied',
              'Network permission denied for the AI API'
            ),
            'error'
          );
          return;
        }

        setStatus(
          this.i18nMessage('aiOrganizeSettingsSaved', 'AI settings saved'),
          'success'
        );
      } catch (error) {
        console.error('Failed to save AI organize settings:', error);
        setStatus(
          this.i18nMessage('aiOrganizeSettingsSaveFailed', 'Failed to save AI settings')
          + (error?.message ? `: ${error.message}` : ''),
          'error'
        );
      } finally {
        saveButton.disabled = false;
      }
    });
  }

  openAiOrganizeSettings() {
    this.openSettingsSidebar();
    this.switchTab('ai');
    const panel = document.querySelector('.ai-organize-settings-panel');
    if (panel) {
      setTimeout(() => {
        panel.scrollIntoView({ behavior: 'smooth', block: 'nearest' });
      }, 100);
    }
  }

  openAccountSettings({ highlightUpgrade = false } = {}) {
    this.openSettingsSidebar();
    this.switchTab('account');
    this.renderAccountSettings().catch(() => {});
    if (highlightUpgrade) {
      document.querySelectorAll('.account-plan-upgrade-button').forEach((btn) => {
        btn.classList.add('account-button-pulse');
        setTimeout(() => btn.classList.remove('account-button-pulse'), 1600);
      });
    }
  }

  setAccountSettingsLoading(loading) {
    const skeleton = document.getElementById('account-settings-skeleton');
    const body = document.getElementById('account-settings-body');
    if (skeleton) {
      skeleton.hidden = !loading;
      skeleton.setAttribute('aria-busy', loading ? 'true' : 'false');
    }
    if (body) body.hidden = loading;
  }

  initAccountSettings() {
    const signInBtn = document.getElementById('account-google-signin-button');
    const signOutBtn = document.getElementById('account-signout-button');
    const billingBtn = document.getElementById('account-manage-billing-button');
    const refreshBtn = document.getElementById('account-refresh-button');
    const openActivationBtn = document.getElementById('account-open-activation-button');
    const openActivationSignedOutBtn = document.getElementById('account-open-activation-signed-out-button');
    const activationDialog = document.getElementById('account-activation-dialog');
    const activateInput = document.getElementById('account-activation-code');
    const activateSubmitBtn = document.getElementById('account-activation-submit');
    const activateCancelBtn = document.getElementById('account-activation-cancel');
    const activateCloseBtn = document.getElementById('account-activation-close');
    const activateDialogStatus = document.getElementById('account-activation-dialog-status');
    const plansEl = document.getElementById('account-plans');
    if (!signInBtn) return;

    this._accountSettingsReady = false;
    this.setAccountSettingsLoading(true);

    const setStatus = (message, type = 'info') => {
      const statusEl = document.getElementById('account-settings-status');
      if (!statusEl) return;
      if (this._accountStatusTimer) {
        clearTimeout(this._accountStatusTimer);
        this._accountStatusTimer = null;
      }
      statusEl.hidden = !message;
      statusEl.textContent = message || '';
      statusEl.classList.remove('is-success', 'is-error');
      if (type === 'success') statusEl.classList.add('is-success');
      if (type === 'error') statusEl.classList.add('is-error');
      if (message && type !== 'error') {
        this._accountStatusTimer = setTimeout(() => {
          statusEl.hidden = true;
          statusEl.textContent = '';
          statusEl.classList.remove('is-success', 'is-error');
          this._accountStatusTimer = null;
        }, 3200);
      }
    };

    const withBusy = async (button, fn) => {
      if (!button) return fn();
      button.disabled = true;
      try {
        return await fn();
      } finally {
        button.disabled = false;
      }
    };

    signInBtn.addEventListener('click', async () => {
      await withBusy(signInBtn, async () => {
        try {
          setStatus(this.i18nMessage('accountSigningIn', 'Signing in…'));
          await signInWithGoogle();
          await this.renderAccountSettings({ showSkeleton: true });
          setStatus(this.i18nMessage('accountSignInSuccess', 'Signed in successfully'), 'success');
        } catch (error) {
          console.error('Google sign-in failed:', error);
          setStatus(getMembershipErrorMessage(error), 'error');
        }
      });
    });

    signOutBtn?.addEventListener('click', async () => {
      await withBusy(signOutBtn, async () => {
        await clearMembershipSession();
        await this.renderAccountSettings({ showSkeleton: true });
        setStatus(this.i18nMessage('accountSignedOut', 'Signed out'), 'success');
      });
    });

    this._accountBillingByTier = {};
    this._accountCatalog = null;

    plansEl?.addEventListener('click', async (event) => {
      const billingOption = event.target.closest('[data-interval]');
      if (billingOption && plansEl.contains(billingOption)) {
        const tierId = billingOption.closest('[data-plan]')?.getAttribute('data-plan');
        const next = billingOption.getAttribute('data-interval');
        if (!tierId || !next) return;
        this._accountBillingByTier[tierId] = next;
        this.renderAccountPricing(this._accountCatalog);
        return;
      }

      const upgradeBtn = event.target.closest('[data-upgrade-tier]');
      if (!upgradeBtn || plansEl.contains(upgradeBtn) === false) return;
      const tierId = upgradeBtn.getAttribute('data-upgrade-tier');
      if (!tierId || upgradeBtn.disabled) return;

      await withBusy(upgradeBtn, async () => {
        try {
          setStatus(this.i18nMessage('accountStartingCheckout', 'Opening Creem Checkout…'));
          const planId = this._accountBillingByTier[tierId] || 'monthly';
          await startProCheckout({ tierId, planId, billingInterval: planId });
          setStatus(
            this.i18nMessage('accountCheckoutOpened', 'Complete payment in the new tab, then refresh status.'),
            'success'
          );
        } catch (error) {
          if (error?.message === 'NOT_SIGNED_IN') {
            setStatus(getMembershipErrorMessage(error), 'error');
            return;
          }
          console.error('Checkout failed:', error);
          setStatus(getMembershipErrorMessage(error), 'error');
        }
      });
    });

    billingBtn?.addEventListener('click', async () => {
      await withBusy(billingBtn, async () => {
        try {
          await openBillingPortal();
        } catch (error) {
          setStatus(getMembershipErrorMessage(error), 'error');
        }
      });
    });

    refreshBtn?.addEventListener('click', async () => {
      await withBusy(refreshBtn, async () => {
        await Promise.all([
          refreshMembershipStatus({ force: true }),
          fetchProductCatalog({ force: true })
        ]);
        await this.renderAccountSettings({ showSkeleton: false });
        setStatus(this.i18nMessage('accountStatusRefreshed', 'Membership status updated'), 'success');
      });
    });

    const setDialogStatus = (message, type = 'info') => {
      if (!activateDialogStatus) return;
      activateDialogStatus.hidden = !message;
      activateDialogStatus.textContent = message || '';
      activateDialogStatus.classList.remove('is-success', 'is-error');
      if (type === 'success') activateDialogStatus.classList.add('is-success');
      if (type === 'error') activateDialogStatus.classList.add('is-error');
    };

    const closeActivationDialog = () => {
      if (!activationDialog) return;
      activationDialog.style.display = 'none';
      activationDialog.hidden = true;
      setDialogStatus('');
    };

    const openActivationDialog = () => {
      if (!activationDialog) return;
      activationDialog.hidden = false;
      activationDialog.style.display = 'block';
      setDialogStatus('');
      if (activateInput) {
        activateInput.value = '';
        setTimeout(() => activateInput.focus(), 50);
      }
    };

    const runActivate = async () => {
      await withBusy(activateSubmitBtn, async () => {
        try {
          setDialogStatus(this.i18nMessage('accountActivating', 'Activating…'));
          const code = activateInput?.value || '';
          const result = await redeemActivationCode(code);
          if (activateInput) activateInput.value = '';
          await this.renderAccountSettings({ showSkeleton: false });
          const end = result?.data?.periodEnd
            ? new Date(result.data.periodEnd * 1000).toLocaleDateString()
            : '';
          const successMsg = end
            ? this.accountMessage('accountActivateSuccessUntil', 'Activated! Pro until $1', [end])
            : (this.i18nMessage('accountActivateSuccess', 'Activation successful — Pro unlocked') || 'Activation successful — Pro unlocked');
          closeActivationDialog();
          setStatus(successMsg, 'success');
        } catch (error) {
          setDialogStatus(getMembershipErrorMessage(error), 'error');
        }
      });
    };

    openActivationBtn?.addEventListener('click', () => openActivationDialog());
    openActivationSignedOutBtn?.addEventListener('click', async () => {
      await withBusy(openActivationSignedOutBtn, async () => {
        try {
          setStatus(this.i18nMessage('accountSigningIn', 'Signing in…'));
          await signInWithGoogle();
          await this.renderAccountSettings({ showSkeleton: true });
          openActivationDialog();
        } catch (error) {
          console.error('Google sign-in failed:', error);
          setStatus(getMembershipErrorMessage(error), 'error');
        }
      });
    });
    activateSubmitBtn?.addEventListener('click', () => runActivate());
    activateCancelBtn?.addEventListener('click', () => closeActivationDialog());
    activateCloseBtn?.addEventListener('click', () => closeActivationDialog());
    activationDialog?.addEventListener('click', (event) => {
      if (event.target === activationDialog) closeActivationDialog();
    });
    activateInput?.addEventListener('keydown', (event) => {
      if (event.key === 'Enter') {
        event.preventDefault();
        runActivate();
      } else if (event.key === 'Escape') {
        event.preventDefault();
        closeActivationDialog();
      }
    });

    refreshMembershipStatus().then(() => this.renderAccountSettings()).catch(() => {
      this.renderAccountSettings({ showSkeleton: false }).catch(() => {});
    });
  }

  async renderAccountSettings({ showSkeleton } = {}) {
    const useSkeleton = showSkeleton ?? !this._accountSettingsReady;
    if (useSkeleton) this.setAccountSettingsLoading(true);

    try {
      // Prefer a fresh /v1/me so per-feature ai.features used/limit are present
      const status = await refreshMembershipStatus({ force: true }).catch(() => getCachedMembershipStatus().catch(() => null));
      const signedOut = document.getElementById('account-signed-out');
      const signedIn = document.getElementById('account-signed-in');
      if (!signedOut || !signedIn) return;

      const catalog = await fetchProductCatalog({ force: true }).catch(() => null);
      this._accountCatalog = catalog;
      this.renderAccountPricing(catalog, status);

      const isSignedIn = Boolean(status?.signedIn);
      signedOut.hidden = isSignedIn;
      signedIn.hidden = !isSignedIn;

      if (!isSignedIn) return;

      const nameEl = document.getElementById('account-user-name');
      const emailEl = document.getElementById('account-user-email');
      const avatarEl = document.getElementById('account-user-avatar');
      const badgeEl = document.getElementById('account-plan-badge');
      const detailEl = document.getElementById('account-plan-detail');
      const billingBtn = document.getElementById('account-manage-billing-button');

      if (nameEl) nameEl.textContent = status.user?.name || status.user?.email || 'User';
      if (emailEl) emailEl.textContent = status.user?.email || '';
      if (avatarEl) {
        if (status.user?.picture) {
          avatarEl.src = status.user.picture;
          avatarEl.hidden = false;
        } else {
          avatarEl.hidden = true;
        }
      }

      const plan = String(status.plan || 'free').toLowerCase();
      const isPaid = Boolean(status.isPro || status.isPremium || (plan !== 'free'));
      const planLabel = this.accountPlanLabel(status.tier || plan, status.tier?.name);

      if (badgeEl) {
        badgeEl.textContent = planLabel;
        badgeEl.classList.toggle('pro', isPaid);
        badgeEl.classList.toggle('free', !isPaid);
      }

      if (detailEl) {
        detailEl.textContent = this.formatAccountPlanDetail(status, planLabel, isPaid);
      }

      if (billingBtn) billingBtn.hidden = !isPaid;

      const openActivationBtn = document.getElementById('account-open-activation-button');
      // Always available when signed in (codes can upgrade tier or extend access)
      if (openActivationBtn) openActivationBtn.hidden = false;
    } finally {
      this._accountSettingsReady = true;
      this.setAccountSettingsLoading(false);
    }
  }

  accountAiQuotaRows(tier, { ai = null, catalog = null } = {}) {
    const q = tier?.quotas || {};
    const defs = (
      Array.isArray(ai?.featureDefs) && ai.featureDefs.length
        ? ai.featureDefs
        : (Array.isArray(catalog?.featureDefs) ? catalog.featureDefs : [])
    ).filter((d) => d && (d.type === 'quota' || d.quotaKey) && d.metered === 'ai');

    if (defs.length) {
      return defs.map((d) => {
        const quotaKey = d.quotaKey || `${d.id}_quota`;
        const n = Number(q[quotaKey]);
        return {
          id: d.id,
          label: labelFromFeatureDef(d) || d.label || d.id,
          limit: Number.isFinite(n) && n >= 0 ? Math.floor(n) : 0
        };
      });
    }

    // Legacy SuperMark fallback when catalog has no featureDefs yet
    const id = String(tier?.id || '').toLowerCase();
    const rank = Number(tier?.rank) || 0;
    const resolve = (key, legacyFallback = 0) => {
      const n = Number(q[key]);
      if (Number.isFinite(n) && n >= 0) return Math.floor(n);
      return legacyFallback;
    };

    let organize = resolve('ai_organize_quota', Number.isFinite(Number(q.ai_requests))
      ? Math.floor(Number(q.ai_requests))
      : -1);
    let recommend = resolve('ai_recommend_quota', -1);
    let introduce = resolve('ai_introduce_quota', -1);

    if (organize < 0 || recommend < 0 || introduce < 0) {
      if (id === 'premium' || rank >= 20) {
        if (organize < 0) organize = 500;
        if (recommend < 0) recommend = 500;
        if (introduce < 0) introduce = 1000;
      } else if (id === 'pro' || rank >= 10) {
        if (organize < 0) organize = 30;
        if (recommend < 0) recommend = 0;
        if (introduce < 0) introduce = 0;
      } else {
        if (organize < 0) organize = 0;
        if (recommend < 0) recommend = 0;
        if (introduce < 0) introduce = 0;
      }
    }

    return [
      {
        id: 'ai_organize',
        label: this.i18nMessage('accountAiQuotaOrganize', 'AI Organize') || 'AI Organize',
        limit: organize
      },
      {
        id: 'ai_recommend',
        label: this.i18nMessage('accountAiQuotaRecommend', 'AI Recommend') || 'AI Recommend',
        limit: recommend
      },
      {
        id: 'ai_introduce',
        label: this.i18nMessage('accountAiQuotaIntroduce', 'AI Introduce') || 'AI Introduce',
        limit: introduce
      }
    ];
  }

  maxPlanLimitAcrossTiers(def, catalog = null) {
    const key = String(def?.limitKey || '').trim();
    if (!key) return 1;
    const tiers = getCatalogTiers(catalog || this._accountCatalog);
    let max = 0;
    for (const tier of tiers) {
      const n = Number(tier?.quotas?.[key]);
      if (Number.isFinite(n) && n > max) max = Math.floor(n);
    }
    for (const preset of Array.isArray(def?.presets) ? def.presets : []) {
      const n = Math.floor(Number(preset));
      if (Number.isFinite(n) && n > max) max = n;
    }
    return Math.max(1, max);
  }

  planLimitRowLabel(def) {
    if (def?.limitLabel) return String(def.limitLabel);
    if (def?.limitLabels) {
      return labelFromFeatureDef({ labels: def.limitLabels, label: def.limitLabel })
        || def.limitLabels.en
        || def.limitLabels.zh_CN
        || '';
    }
    return labelFromFeatureDef(def) || def?.label || def?.id || '';
  }

  formatPlanLimitMeta(def, value) {
    const n = Math.max(0, Math.floor(Number(value) || 0));
    const unit = String(def?.unit || '').trim();
    if (n <= 0) {
      return unit === '×' || unit === 'x'
        ? (this.i18nMessage('accountAiQuotaNone', '×') || '×')
        : (this.i18nMessage('accountAiQuotaNone', '—') || '—');
    }
    if (unit === '×' || unit === 'x') {
      return this.accountMessage('clear404ScanSpeedSuffix', '×$1', [String(n)]);
    }
    if (unit) return `${n} ${unit}`;
    return String(n);
  }

  appendAccountPlanQuotaRow(wrap, {
    label,
    meta,
    fillPct = 0,
    isNone = false,
    isCapacity = false
  }) {
    const block = document.createElement('div');
    block.className = 'account-plan-ai-quota-row';
    if (isNone) block.classList.add('is-none');
    if (isCapacity) block.classList.add('is-capacity');

    const head = document.createElement('div');
    head.className = 'account-plan-ai-quota-head';
    const labelEl = document.createElement('span');
    labelEl.className = 'account-plan-ai-quota-label';
    labelEl.textContent = label;
    head.appendChild(labelEl);
    const metaEl = document.createElement('span');
    metaEl.className = 'account-plan-ai-quota-meta';
    metaEl.textContent = meta;
    head.appendChild(metaEl);
    block.appendChild(head);

    const track = document.createElement('div');
    track.className = 'account-plan-ai-quota-track';
    const fill = document.createElement('div');
    fill.className = 'account-plan-ai-quota-fill';
    fill.style.width = `${Math.max(0, Math.min(100, fillPct))}%`;
    track.appendChild(fill);
    block.appendChild(track);
    wrap.appendChild(block);
  }

  appendAccountPlanLimitRows(wrap, tier, catalog = null) {
    const defs = (Array.isArray(catalog?.featureDefs) ? catalog.featureDefs : [])
      .filter((d) => d && String(d.limitKey || '').trim());
    if (!defs.length) return;

    const selected = new Set(
      (Array.isArray(tier?.features) ? tier.features : []).map((id) => String(id || '').trim())
    );
    const planId = String(tier?.id || 'free').toLowerCase();

    for (const def of defs) {
      const featureId = String(def.id || '').trim();
      const entitled = def.type === 'limit' || selected.has(featureId);
      const value = entitled
        ? getPlanLimitForFeature(featureId, {
          status: { plan: planId },
          catalog,
          defaultValue: 0
        })
        : 0;
      const max = this.maxPlanLimitAcrossTiers(def, catalog);
      this.appendAccountPlanQuotaRow(wrap, {
        label: this.planLimitRowLabel(def),
        meta: this.formatPlanLimitMeta(def, value),
        fillPct: value > 0 && max > 0 ? Math.round((value / max) * 100) : 0,
        isNone: value <= 0,
        isCapacity: value > 0
      });
    }
  }

  buildAccountPlanAiQuota(tier, { isCurrent = false, signedIn = false, ai = null, catalog = null } = {}) {
    const wrap = document.createElement('div');
    wrap.className = 'account-plan-ai-quota';

    // Display order: Quotas → Limits → Features (features list is appended after this block)
    const rows = this.accountAiQuotaRows(tier, { ai, catalog });

    const isLive = Boolean(isCurrent && signedIn);
    for (const row of rows) {
      const slot = ai?.features?.[row.id];
      let limit = Math.max(0, Number(row.limit) || 0);
      if (isLive && slot && Number.isFinite(Number(slot.limit))) {
        limit = Math.max(0, Number(slot.limit));
      }
      let used = 0;
      if (isLive) {
        if (slot && Number.isFinite(Number(slot.used))) {
          used = Math.max(0, Number(slot.used));
        } else if (row.id === 'ai_organize' && Number.isFinite(Number(ai?.used))) {
          used = Math.max(0, Number(ai.used));
        }
      }
      const pct = limit > 0 ? Math.min(100, Math.round((used / limit) * 100)) : 0;

      const block = document.createElement('div');
      block.className = 'account-plan-ai-quota-row';
      if (limit <= 0) block.classList.add('is-none');

      const head = document.createElement('div');
      head.className = 'account-plan-ai-quota-head';
      const label = document.createElement('span');
      label.className = 'account-plan-ai-quota-label';
      label.textContent = row.label;
      head.appendChild(label);
      const meta = document.createElement('span');
      meta.className = 'account-plan-ai-quota-meta';
      if (limit <= 0) {
        meta.textContent = this.i18nMessage('accountAiQuotaNone', '×') || '×';
      } else if (isLive) {
        // Prefer direct digits — chrome.i18n "$1 / $2" is unreliable without placeholders.
        meta.textContent = `${used} / ${limit}`;
        meta.title = this.accountMessage(
          'accountAiQuotaUsedHint',
          'Used $1 of $2 this month',
          [String(used), String(limit)]
        );
      } else {
        meta.textContent = this.accountMessage(
          'accountAiQuotaMonthly',
          '$1 / month',
          [String(limit)]
        ) || `${limit} / month`;
      }
      head.appendChild(meta);
      block.appendChild(head);

      const track = document.createElement('div');
      track.className = 'account-plan-ai-quota-track';
      const fill = document.createElement('div');
      fill.className = 'account-plan-ai-quota-fill';
      if (limit <= 0) {
        fill.style.width = '0%';
      } else if (isLive) {
        fill.style.width = `${pct}%`;
        if (pct >= 100) block.classList.add('is-exhausted');
        else if (pct >= 85) block.classList.add('is-warning');
      } else {
        fill.style.width = '100%';
        block.classList.add('is-capacity');
      }
      track.appendChild(fill);
      block.appendChild(track);
      wrap.appendChild(block);
    }

    this.appendAccountPlanLimitRows(wrap, tier, catalog);

    return wrap;
  }

  buildAccountPlanUpgradeButton({
    tierId,
    columnCurrent = false,
    isFree = false,
    canCheckout = false,
    offerTrial = false,
    trialDays = 0,
    formatted = null,
    upgradeLabel = 'Upgrade',
    currentLabel = 'Current plan',
    signedIn = false,
    currentRank = null,
    tierRankValue = 0
  } = {}) {
    const button = document.createElement('button');
    button.type = 'button';
    button.className = 'account-primary-button account-plan-upgrade-button';
    button.dataset.upgradeTier = tierId;

    if (isFree) {
      // Free is never purchasable — always show grayed Upgrade (current state uses column badge).
      button.textContent = upgradeLabel;
      button.disabled = true;
      button.classList.add('is-free-upgrade');
    } else if (columnCurrent) {
      button.textContent = currentLabel;
      button.disabled = true;
    } else if (!canCheckout) {
      button.textContent = this.i18nMessage('accountComingSoon', 'Coming soon') || 'Coming soon';
      button.disabled = true;
    } else if (offerTrial) {
      button.disabled = false;
      button.textContent = this.accountMessage(
        'accountStartTrial',
        'Start $1-day free trial',
        [String(trialDays)]
      );
    } else {
      button.disabled = false;
      button.textContent = formatted?.priceText
        ? `${upgradeLabel} · ${formatted.priceText}`
        : upgradeLabel;
    }

    // Higher-tier members: lower paid tiers are included, not "Current plan".
    if (
      signedIn
      && !isFree
      && !columnCurrent
      && Number.isFinite(currentRank)
      && currentRank > tierRankValue
    ) {
      button.textContent = this.i18nMessage('accountPlanIncluded', 'Included') || 'Included';
      button.disabled = true;
    } else if (
      signedIn
      && !isFree
      && !columnCurrent
      && Number.isFinite(currentRank)
      && currentRank === tierRankValue
    ) {
      button.textContent = currentLabel;
      button.disabled = true;
    }

    return button;
  }

  accountPlanLabel(tierOrId, fallbackName = '') {
    const id = typeof tierOrId === 'string' ? tierOrId : tierOrId?.id;
    if (id === 'free') return this.i18nMessage('accountPlanFree', 'Free') || 'Free';
    if (id === 'pro') return this.i18nMessage('accountPlanPro', 'Pro') || 'Pro';
    if (id === 'premium') return this.i18nMessage('accountPlanPremium', 'Premium') || 'Premium';
    const raw = String(fallbackName || tierOrId?.name || id || '')
      .replace(/\s*Plan$/i, '')
      .replace(/方案$/u, '')
      .trim();
    return raw || id || '';
  }

  /**
   * Localized string with optional $1/$2 substitutions.
   * Always pass substitutions here — never call getMessage then .replace('$1', …):
   * Chrome clears placeholders when args are omitted, producing "-day free trial".
   */
  i18nMessage(key, fallback = '', substitutions = null) {
    const fromI18n = (
      typeof window.getLocalizedMessage === 'function'
        ? window.getLocalizedMessage(key, substitutions)
        : chrome.i18n.getMessage(key, substitutions)
    );
    if (fromI18n) return fromI18n;
    if (!fallback) return '';
    if (substitutions == null) return fallback;
    const values = Array.isArray(substitutions) ? substitutions : [substitutions];
    return values.reduce(
      (text, value, index) => text.replace(new RegExp(`\\$${index + 1}`, 'g'), String(value)),
      String(fallback)
    );
  }

  /** @deprecated Prefer i18nMessage — kept as an alias for Account call sites. */
  accountMessage(key, fallback, substitutions = null) {
    return this.i18nMessage(key, fallback, substitutions);
  }

  formatAccountPlanDetail(status, planLabel, isPaid) {
    const planStatus = String(
      status.planStatus || status.user?.planStatus || (isPaid ? 'active' : 'inactive')
    ).toLowerCase();
    const endSec = status.currentPeriodEnd || status.user?.currentPeriodEnd || null;
    const endSecNum = endSec != null ? Number(endSec) : null;
    const end = endSecNum ? new Date(endSecNum * 1000).toLocaleDateString() : '';
    const label = planLabel || this.i18nMessage('accountPlanPro', 'Pro') || 'Pro';
    const farFuture =
      endSecNum != null
      && endSecNum - Math.floor(Date.now() / 1000) > 50 * 365 * 86400;

    if (!isPaid) {
      if (planStatus === 'expired' || planStatus === 'canceled') {
        return this.accountMessage(
          'accountSubscriptionEnded',
          'Subscription ended. You are on the Free plan.'
        );
      }
      return this.accountMessage(
        'accountFreeDetail',
        'You are on the Free plan. Start a free trial to unlock paid features.'
      );
    }

    switch (planStatus) {
      case 'trialing':
        return end
          ? this.accountMessage(
            'accountTrialUntil',
            'Free trial until $1 — then auto-renews',
            [end]
          )
          : this.accountMessage(
            'accountTrialActive',
            'Free trial active — auto-renews after trial'
          );
      case 'past_due':
        return end
          ? this.accountMessage(
            'accountTierPastDueUntil',
            '$1 past due — access until $2',
            [label, end]
          )
          : this.accountMessage(
            'accountTierPastDue',
            '$1 payment is past due',
            [label]
          );
      case 'canceled':
      case 'expired':
        if (farFuture) {
          return this.accountMessage(
            'accountTierCanceledCode',
            '$1 canceled — access kept via activation code',
            [label]
          );
        }
        return end
          ? this.accountMessage(
            'accountTierCanceledUntil',
            '$1 canceled — access until $2',
            [label, end]
          )
          : this.accountMessage(
            planStatus === 'expired' ? 'accountSubscriptionEnded' : 'accountTierCanceled',
            planStatus === 'expired'
              ? 'Subscription ended. You are on the Free plan.'
              : '$1 canceled',
            planStatus === 'expired' ? null : [label]
          );
      case 'active':
      default:
        return end
          ? this.accountMessage(
            'accountTierActiveUntil',
            '$1 active until $2',
            [label, end]
          )
          : this.accountMessage(
            'accountTierActive',
            '$1 is active',
            [label]
          );
    }
  }

  accountFeatureLabel(featureId, { tier = null, included = true } = {}) {
    const fromCatalog = featureLabelFromCatalog(featureId, this._accountCatalog);
    let label = fromCatalog || null;

    if (!label) {
      const map = {
        [PRO_FEATURES.AI_ORGANIZE]: this.i18nMessage('proFeatureAiOrganize', 'AI Organize (+ restore)') || 'AI Organize (+ restore)',
        [PRO_FEATURES.AI_RECOMMEND]: this.i18nMessage('proFeatureAiRecommend', 'AI Recommend bookmarks') || 'AI Recommend bookmarks',
        [PRO_FEATURES.AI_INTRODUCE]: this.i18nMessage('proFeatureAiIntroduce', 'AI Website Intro') || 'AI Website Intro',
        [PRO_FEATURES.PLUGIN_BAR]: this.i18nMessage('proFeaturePluginBar', 'Plugin bar (clock / todo / notes)') || 'Plugin bar (clock / todo / notes)',
        [PRO_FEATURES.BING_WALLPAPER]: this.i18nMessage('proFeatureBingWallpaper', 'Bing wallpapers') || 'Bing wallpapers',
        [PRO_FEATURES.COMPARE_SEARCH]: this.i18nMessage('proFeatureCompareSearch', 'Compare search (Cmd/Ctrl + Enter)') || 'Compare search (Cmd/Ctrl + Enter)',
        [PRO_FEATURES.QUICK_LINKS_DRAG_TO_REORDER]: this.i18nMessage('proFeatureQuickLinksDrag', 'Quick links drag to reorder') || 'Quick links drag to reorder',
        [PRO_FEATURES.CUSTOMIZE_MODEL_PROVIDERS]: this.i18nMessage('proFeatureCustomizeProviders', 'Custom model providers (BYOK)') || 'Custom model providers (BYOK)'
      };
      label = map[featureId] || String(featureId || '')
        .replace(/_/g, ' ')
        .replace(/\b\w/g, (c) => c.toUpperCase());
    }
    return label;
  }

  /**
   * Same ordered feature rows for every plan column (check / cross comparison).
   */
  planComparisonFeatureRows(tier, allTiers = [], catalog = null) {
    const selected = new Set(this.tierFeatureIds(tier));
    const ids = this.schemaFeatureOrder(allTiers, catalog);
    return ids.map((id) => {
      const included = selected.has(id);
      return {
        id,
        included,
        label: this.accountFeatureLabel(id, { tier, included })
      };
    });
  }

  /**
   * Display order = product Plan schema (featureDefs) order.
   * Unknown ids (not in schema) append alphabetically.
   */
  schemaFeatureOrder(allTiers = [], catalog = null) {
    const defsOrder = (Array.isArray(catalog?.featureDefs) ? catalog.featureDefs : [])
      .map((d) => String(d?.id || ''))
      .filter((id) => id && !id.endsWith('_quota') && !isPlanConfigFeatureId(id));
    const allIds = new Set([
      ...defsOrder,
      ...(allTiers || []).flatMap((t) => this.tierFeatureIds(t))
    ]);
    return [
      ...defsOrder.filter((id) => allIds.has(id)),
      ...[...allIds].filter((id) => !defsOrder.includes(id)).sort()
    ];
  }

  /** Features for a tier, in Plan schema order. */
  orderedTierFeatureIds(tier, allTiers = [], catalog = null) {
    const selected = new Set(this.tierFeatureIds(tier));
    return this.schemaFeatureOrder(allTiers, catalog).filter((id) => selected.has(id));
  }

  /** All features explicitly assigned to this tier (no rank inheritance). */
  tierFeatureIds(tier) {
    return (Array.isArray(tier?.features) ? tier.features : [])
      .map((id) => String(id || ''))
      .filter((id) => id && !id.endsWith('_quota') && !isPlanConfigFeatureId(id));
  }

  defaultFreeFeatureLabels() {
    return [
      this.i18nMessage('freeFeatureBookmarks', 'Bookmark folders & search') || 'Bookmark folders & search',
      this.i18nMessage('freeFeatureEngines', 'Multi search engines') || 'Multi search engines',
      this.i18nMessage('freeFeatureWallpapers', 'Preset & local wallpapers') || 'Preset & local wallpapers',
      this.i18nMessage('freeFeatureQuickLinks', 'Quick links & sidebar') || 'Quick links & sidebar'
    ];
  }

  defaultProFeatureLabels() {
    return [
      this.accountFeatureLabel(PRO_FEATURES.COMPARE_SEARCH),
      this.accountFeatureLabel(PRO_FEATURES.QUICK_LINKS_DRAG_TO_REORDER),
      this.accountFeatureLabel(PRO_FEATURES.PLUGIN_BAR),
      this.accountFeatureLabel(PRO_FEATURES.BING_WALLPAPER),
      this.accountFeatureLabel(PRO_FEATURES.AI_ORGANIZE)
    ];
  }

  defaultPremiumFeatureLabels() {
    return [
      ...this.defaultProFeatureLabels(),
      this.accountFeatureLabel(PRO_FEATURES.AI_RECOMMEND),
      this.accountFeatureLabel(PRO_FEATURES.AI_INTRODUCE),
      this.accountFeatureLabel(PRO_FEATURES.CUSTOMIZE_MODEL_PROVIDERS)
    ];
  }

  stopAccountPromoCountdown() {
    if (this._accountPromoTimer) {
      clearInterval(this._accountPromoTimer);
      this._accountPromoTimer = null;
    }
  }

  accountDiscountDurationTip(formatted) {
    if (!formatted?.discountDuration) return null;
    if (formatted.discountDuration === 'once') {
      return this.i18nMessage('accountDiscountOnce', 'Promo applies to first charge only')
        || 'Promo applies to first charge only';
    }
    if (formatted.discountDuration === 'forever') {
      return this.i18nMessage('accountDiscountForever', 'Promo applies to every renewal')
        || 'Promo applies to every renewal';
    }
    if (formatted.discountDuration === 'repeating') {
      const months = Number(formatted.discountDurationInMonths);
      return this.accountMessage(
        'accountDiscountRepeating',
        'Promo applies for $1 months',
        [String(Number.isFinite(months) && months > 0 ? months : '?')]
      );
    }
    return formatDiscountDurationLabel(
      formatted.discountDuration,
      formatted.discountDurationInMonths
    );
  }

  accountBilledLabel(formatted) {
    const period = String(formatted?.billingPeriod || '').toLowerCase();
    const kind = formatted?.kind;
    if (kind === 'one_time' || period === 'once') {
      return this.i18nMessage('accountBillingOneTime', 'One-time payment') || 'One-time payment';
    }
    if (period === 'every-year') {
      return this.i18nMessage('accountBilledAnnually', 'billed annually') || 'billed annually';
    }
    if (period === 'every-three-months') {
      return this.i18nMessage('accountBilledEveryThreeMonths', 'billed every 3 months')
        || 'billed every 3 months';
    }
    if (period === 'every-six-months') {
      return this.i18nMessage('accountBilledEverySixMonths', 'billed every 6 months')
        || 'billed every 6 months';
    }
    if (period === 'every-day') {
      return this.i18nMessage('accountBilledDaily', 'billed daily') || 'billed daily';
    }
    return this.i18nMessage('accountBilledMonthly', 'billed monthly') || 'billed monthly';
  }

  /** Build "Save $60 a year" with the amount emphasized. */
  appendAccountSavingsLabel(host, formatted) {
    if (!host || !formatted?.savingsAmountText) return;
    const amount = formatted.savingsAmountText;
    const months = Number(formatted.monthsPerCharge);
    let template = this.i18nMessage('accountSaveAmount', 'Save $1') || 'Save $1';
    if (months >= 12) {
      template = this.i18nMessage('accountSaveAmountYear', 'Save $1 a year') || 'Save $1 a year';
    } else if (months === 6) {
      template = this.i18nMessage('accountSaveAmountSixMonths', 'Save $1 every 6 months')
        || 'Save $1 every 6 months';
    } else if (months === 3) {
      template = this.i18nMessage('accountSaveAmountThreeMonths', 'Save $1 every 3 months')
        || 'Save $1 every 3 months';
    }
    const parts = String(template).split('$1');
    if (parts[0]) host.appendChild(document.createTextNode(parts[0]));
    const strong = document.createElement('strong');
    strong.textContent = amount;
    host.appendChild(strong);
    if (parts[1]) host.appendChild(document.createTextNode(parts[1]));
  }

  renderAccountPromoBanner(catalog) {
    const banner = document.getElementById('account-promo-banner');
    const labelEl = document.getElementById('account-promo-label');
    const countdownEl = document.getElementById('account-promo-countdown');
    if (!banner || !labelEl || !countdownEl) return;

    this.stopAccountPromoCountdown();
    const promo = getCatalogPromo(catalog);
    if (!promo?.discountLabel) {
      banner.hidden = true;
      labelEl.textContent = '';
      countdownEl.textContent = '';
      return;
    }

    const discountText = promo.title
      || promo.discountLabel
      || (this.i18nMessage('accountPromoSale', 'Limited offer') || 'Limited offer');
    const isScheduled = promo.phase === 'scheduled' && Number.isFinite(promo.startsAtMs);
    const targetMs = isScheduled ? promo.startsAtMs : promo.endsAtMs;
    if (!Number.isFinite(targetMs)) {
      // Live campaign without end: show title only.
      labelEl.textContent = discountText;
      countdownEl.textContent = '';
      banner.hidden = false;
      return;
    }

    const tick = () => {
      const remaining = targetMs - Date.now();
      if (remaining <= 0) {
        this.stopAccountPromoCountdown();
        banner.hidden = true;
        fetchProductCatalog({ force: true })
          .then((fresh) => {
            this._accountCatalog = fresh || catalog;
            this.renderAccountPricing(this._accountCatalog);
          })
          .catch(() => {
            this.renderAccountPricing(catalog);
          });
        return;
      }
      const timeText = formatPromoCountdown(remaining);
      if (isScheduled) {
        labelEl.textContent = this.accountMessage(
          'accountPromoStartsIn',
          '$1 starts in',
          [discountText]
        );
      } else {
        labelEl.textContent = this.accountMessage(
          'accountPromoEndsIn',
          '$1 ends in',
          [discountText]
        );
      }
      countdownEl.textContent = timeText;
      banner.hidden = false;
    };

    tick();
    this._accountPromoTimer = setInterval(tick, 1000);
  }

  renderAccountPricing(catalog, status = null) {
    const plansEl = document.getElementById('account-plans');
    if (!plansEl) return;

    this.renderAccountPromoBanner(catalog);

    const applyPricing = async () => {
      const liveStatus = status || await getCachedMembershipStatus().catch(() => null);
      const signedIn = Boolean(liveStatus?.signedIn);
      const currentPlan = String(liveStatus?.plan || 'free').toLowerCase();
      const currentRank = Number(liveStatus?.tier?.rank);
      const tiers = getCatalogTiers(catalog);
      const campaignOn = isCatalogPromoLive(catalog);
      const promoMeta = getCatalogPromo(catalog);
      const upgradeLabel = this.i18nMessage('accountUpgradePro', 'Upgrade') || 'Upgrade';
      const currentLabel = this.i18nMessage('accountCurrentPlan', 'Current plan') || 'Current plan';
      const foreverLabel = this.i18nMessage('accountFreePricePeriod', 'Forever') || 'Forever';
      const lowestRank = tiers.length
        ? Math.min(...tiers.map((t) => Number(t.rank) || 0))
        : 0;

      if (!this._accountBillingByTier) this._accountBillingByTier = {};

      plansEl.innerHTML = '';
      plansEl.style.setProperty('--account-plan-count', String(Math.max(2, tiers.length)));
      plansEl.classList.toggle('is-promo-live', Boolean(campaignOn));
      plansEl.classList.toggle('is-promo-scheduled', promoMeta?.phase === 'scheduled');

      for (const tier of tiers) {
        const tierRankValue = Number(tier.rank) || 0;
        const isFree = Boolean(
          tier.isFree || tier.id === 'free' || tierRankValue <= lowestRank
        );
        const available = getAvailableBillingIntervals(tier);
        if (!this._accountBillingByTier[tier.id] || !available.includes(this._accountBillingByTier[tier.id])) {
          const preferred = tier.defaultInterval && available.includes(tier.defaultInterval)
            ? tier.defaultInterval
            : (available[0] || 'monthly');
          this._accountBillingByTier[tier.id] = preferred;
        }
        const interval = this._accountBillingByTier[tier.id];
        const formatted = isFree
          ? null
          : formatCatalogPrice(tier, { interval, promoActive: campaignOn });
        const canCheckout = !isFree && planHasCheckout(tier, interval);
        const showPlanPromo = Boolean(campaignOn && formatted?.discountLabel);
        const trialDays = !isFree
          && formatted?.isSubscription
          && Number(formatted?.trialDays) > 0
          ? Number(formatted.trialDays)
          : 0;
        const offerTrial = Boolean(
          trialDays
          && canCheckout
          && (
            !signedIn
            || (
              liveStatus?.trialEligible !== false
              && !liveStatus?.trialUsed
              && String(liveStatus?.plan || 'free').toLowerCase() === 'free'
            )
          )
        );

        const comparisonRows = this.planComparisonFeatureRows(tier, tiers, catalog);
        let featureRows = comparisonRows;
        if (!featureRows.length) {
          const fallback = isFree
            ? this.defaultFreeFeatureLabels()
            : (tier.id === 'pro'
              ? this.defaultProFeatureLabels()
              : (tier.id === 'premium' ? this.defaultPremiumFeatureLabels() : []));
          featureRows = fallback.map((label) => ({ id: null, label, included: true }));
        }

        const exactCurrent = signedIn && currentPlan === tier.id;
        const freeCurrent = signedIn && isFree && (
          currentPlan === 'free' || (!Number.isFinite(currentRank) && !liveStatus?.isPro)
        );
        const rankCurrent = signedIn
          && Number.isFinite(currentRank)
          && currentRank === tierRankValue
          && currentPlan === tier.id;
        const columnCurrent = exactCurrent || freeCurrent || rankCurrent;

        const article = document.createElement('article');
        article.className = 'account-plan-column';
        if (!isFree) article.classList.add(tier.id === 'premium' ? 'is-premium' : 'is-pro');
        if (columnCurrent) article.classList.add('is-current');
        if (showPlanPromo) article.classList.add('is-promo');
        article.dataset.plan = tier.id;
        article.id = `account-plan-${tier.id}`;

        const head = document.createElement('div');
        head.className = 'account-plan-column-head';

        const nameEl = document.createElement('h4');
        nameEl.className = 'account-plan-column-name';
        nameEl.textContent = tier.name
          || (isFree
            ? (this.i18nMessage('accountPlanFree', 'Free') || 'Free')
            : tier.id);
        head.appendChild(nameEl);

        const billingSlot = document.createElement('div');
        billingSlot.className = 'account-plan-billing-slot';
        if (isFree) {
          const freeBilling = document.createElement('span');
          freeBilling.className = 'account-plan-billing-static';
          freeBilling.textContent = this.i18nMessage('accountFreeBillingLabel', 'No billing')
            || 'No billing';
          billingSlot.appendChild(freeBilling);
        } else if (available.length > 1) {
          const toggle = document.createElement('div');
          toggle.className = 'account-billing-toggle';
          toggle.innerHTML = available.map((planId) => {
            const label = formatBillingPlanLabel(planId, tier);
            const active = interval === planId ? ' is-active' : '';
            return `<button type="button" class="account-billing-option${active}" data-interval="${planId}">${label}</button>`;
          }).join('');
          billingSlot.appendChild(toggle);
        } else if (available.length === 1) {
          const staticBilling = document.createElement('span');
          staticBilling.className = 'account-plan-billing-static';
          staticBilling.textContent = formatBillingPlanLabel(available[0], tier);
          billingSlot.appendChild(staticBilling);
        }
        head.appendChild(billingSlot);

        const priceSlot = document.createElement('div');
        priceSlot.className = 'account-plan-price-slot';
        const priceRow = document.createElement('div');
        priceRow.className = 'account-plan-price-row';
        const priceEl = document.createElement('span');
        priceEl.className = 'account-plan-column-price';
        if (isFree) {
          priceEl.textContent = this.i18nMessage('accountFreePrice', '$0') || '$0';
          priceRow.appendChild(priceEl);
        } else {
          // Match admin Prices: strikethrough list → sale amount (/mo) → discount chip.
          if (formatted?.compareAtAmountText) {
            const compareEl = document.createElement('span');
            compareEl.className = 'account-plan-compare-price';
            compareEl.textContent = formatted.compareAtAmountText;
            priceRow.appendChild(compareEl);
          }
          priceEl.textContent = formatted?.amountText
            || formatted?.priceText
            || (canCheckout ? '—' : 'Coming soon');
          priceRow.appendChild(priceEl);
          if (formatted?.perMonthSuffix) {
            const unitEl = document.createElement('span');
            unitEl.className = 'account-plan-price-unit';
            unitEl.textContent = this.i18nMessage('accountPricePerMonth', '/mo') || '/mo';
            priceRow.appendChild(unitEl);
          }
          if (formatted?.discountLabel) {
            const discountEl = document.createElement('span');
            discountEl.className = 'account-plan-discount';
            discountEl.textContent = formatted.discountLabel;
            priceRow.appendChild(discountEl);
          }
        }
        priceSlot.appendChild(priceRow);
        head.appendChild(priceSlot);

        const periodEl = document.createElement('p');
        periodEl.className = 'account-plan-column-period';
        if (isFree) {
          const periodTextEl = document.createElement('span');
          periodTextEl.textContent = foreverLabel;
          periodEl.appendChild(periodTextEl);
          head.appendChild(periodEl);
        } else {
          const billedFallback = formatted?.billedLabel
            || (formatted?.isSubscription ? 'billed monthly' : 'One-time payment');
          let billedText = '';
          if (canCheckout && offerTrial) {
            billedText = this.accountMessage(
              'accountTrialThenBilled',
              '$1-day free trial · $2',
              [String(trialDays), this.accountBilledLabel(formatted) || billedFallback]
            );
          } else if (canCheckout && formatted?.isSubscription) {
            billedText = this.accountBilledLabel(formatted) || billedFallback;
          } else if (canCheckout) {
            billedText = this.i18nMessage('accountBillingOneTime', 'One-time payment')
              || 'One-time payment';
          }

          if (billedText) {
            const periodTextEl = document.createElement('span');
            periodTextEl.className = 'account-plan-billed';
            periodTextEl.textContent = billedText;
            periodEl.appendChild(periodTextEl);
          }

          const durationTip = this.accountDiscountDurationTip(formatted);
          if (canCheckout && durationTip && formatted?.discountLabel) {
            const infoBtn = document.createElement('button');
            infoBtn.type = 'button';
            infoBtn.className = 'account-plan-billing-info';
            infoBtn.setAttribute(
              'aria-label',
              this.i18nMessage('accountDiscountInfoLabel', 'Promo billing details') || 'Promo billing details'
            );
            infoBtn.innerHTML = ICONS.info;
            const tipEl = document.createElement('span');
            tipEl.className = 'account-plan-billing-tooltip';
            tipEl.setAttribute('role', 'tooltip');
            tipEl.textContent = durationTip;
            infoBtn.appendChild(tipEl);
            const host = periodEl.querySelector('.account-plan-billed') || periodEl.lastElementChild;
            if (host && host.tagName === 'SPAN') {
              host.appendChild(document.createTextNode(' '));
              host.appendChild(infoBtn);
            } else {
              periodEl.appendChild(infoBtn);
            }
          }

          head.appendChild(periodEl);

          if (canCheckout && formatted?.savingsText) {
            const savingsEl = document.createElement('p');
            savingsEl.className = 'account-plan-savings';
            const savingsLabel = document.createElement('span');
            this.appendAccountSavingsLabel(savingsLabel, formatted);
            savingsEl.appendChild(savingsLabel);
            if (formatted.savingsTip) {
              const infoBtn = document.createElement('button');
              infoBtn.type = 'button';
              infoBtn.className = 'account-plan-billing-info';
              infoBtn.setAttribute(
                'aria-label',
                this.i18nMessage('accountSavingsInfoLabel', 'Savings details') || 'Savings details'
              );
              infoBtn.innerHTML = ICONS.info;
              const tipEl = document.createElement('span');
              tipEl.className = 'account-plan-billing-tooltip';
              tipEl.setAttribute('role', 'tooltip');
              tipEl.textContent = formatted.savingsTip;
              infoBtn.appendChild(tipEl);
              savingsEl.appendChild(document.createTextNode(' '));
              savingsEl.appendChild(infoBtn);
            }
            head.appendChild(savingsEl);
          }
        }

        const upgradeButtonOpts = {
          tierId: tier.id,
          columnCurrent,
          isFree,
          canCheckout,
          offerTrial,
          trialDays,
          formatted,
          upgradeLabel,
          currentLabel,
          signedIn,
          currentRank,
          tierRankValue
        };

        const top = document.createElement('div');
        top.className = 'account-plan-column-top';
        top.appendChild(head);
        top.appendChild(this.buildAccountPlanUpgradeButton(upgradeButtonOpts));
        article.appendChild(top);

        article.appendChild(this.buildAccountPlanAiQuota(tier, {
          isCurrent: columnCurrent,
          signedIn,
          ai: liveStatus?.ai || null,
          catalog
        }));

        const list = document.createElement('ul');
        list.className = 'account-plan-column-features';
        for (const row of featureRows) {
          const li = document.createElement('li');
          li.className = row.included ? 'is-on' : 'is-off';
          const mark = document.createElement('span');
          mark.className = 'account-plan-feature-mark';
          mark.setAttribute('aria-hidden', 'true');
          mark.textContent = row.included ? '✓' : '✕';
          const text = document.createElement('span');
          text.className = 'account-plan-feature-text';
          text.textContent = row.label;
          li.appendChild(mark);
          li.appendChild(text);
          list.appendChild(li);
        }
        if (!featureRows.length) {
          const emptyLi = document.createElement('li');
          emptyLi.className = 'is-muted';
          emptyLi.textContent = this.i18nMessage('accountNoExtraFeatures', 'No additional features listed')
            || 'No additional features listed';
          list.appendChild(emptyLi);
        }
        article.appendChild(list);

        article.appendChild(this.buildAccountPlanUpgradeButton(upgradeButtonOpts));
        plansEl.appendChild(article);
      }
    };

    applyPricing().catch(() => {});
  }

  initLanguageSettings() {
    const languageSelect = document.getElementById('ui-language-select');
    if (!languageSelect) {
      return;
    }

    const applySelectValue = (language) => {
      const nextValue = language || 'system';
      if ([...languageSelect.options].some((option) => option.value === nextValue)) {
        languageSelect.value = nextValue;
      } else {
        languageSelect.value = 'system';
      }
    };

    const cachedLanguage = localStorage.getItem('uiLanguage') || 'system';
    applySelectValue(cachedLanguage);

    chrome.storage.sync.get({ uiLanguage: cachedLanguage }, (result) => {
      applySelectValue(result.uiLanguage || 'system');
    });

    languageSelect.addEventListener('change', async () => {
      const selectedLanguage = languageSelect.value || 'system';
      if (typeof window.setUILanguage === 'function') {
        await window.setUILanguage(selectedLanguage);
      } else {
        localStorage.setItem('uiLanguage', selectedLanguage);
        chrome.storage.sync.set({ uiLanguage: selectedLanguage });
      }
    });
  }

  initTheme() {
    const themeSelect = document.getElementById('theme-select');
    const applyThemePreference = (savedTheme) => {
      const theme = savedTheme || 'auto';
      if (themeSelect) themeSelect.value = theme;

      if (theme === 'auto') {
        this.setThemeBasedOnSystem();
      } else {
        document.documentElement.setAttribute('data-theme', theme);
        this.updateThemeIcon(theme === 'dark');
      }
    };

    const cachedTheme = localStorage.getItem('theme') || 'auto';
    applyThemePreference(cachedTheme);

    chrome.storage.sync.get(['theme'], (result) => {
      if (result.theme) {
        localStorage.setItem('theme', result.theme);
        applyThemePreference(result.theme);
      } else if (cachedTheme) {
        setSyncedData({ theme: cachedTheme });
      }
    });

    // 监听系统主题变化
    window.matchMedia('(prefers-color-scheme: dark)').addListener((e) => {
      const current = localStorage.getItem('theme') || 'auto';
      if (current === 'auto') {
        const isDark = e.matches;
        document.documentElement.setAttribute('data-theme', isDark ? 'dark' : 'light');
        this.updateThemeIcon(isDark);
      }
    });

    // 监听主题选择变化
    if (themeSelect) {
      themeSelect.addEventListener('change', (e) => {
        const selectedTheme = e.target.value;
        localStorage.setItem('theme', selectedTheme);
        setSyncedData({ theme: selectedTheme });

        if (selectedTheme === 'auto') {
          this.setThemeBasedOnSystem();
        } else {
          document.documentElement.setAttribute('data-theme', selectedTheme);
          this.updateThemeIcon(selectedTheme === 'dark');
        }
      });
    }

    // 保留原有的主题切换按钮功能
    const themeToggleBtn = document.getElementById('theme-toggle-btn');
    if (themeToggleBtn) {
      themeToggleBtn.addEventListener('click', () => {
        const currentTheme = document.documentElement.getAttribute('data-theme');
        const newTheme = currentTheme === 'dark' ? 'light' : 'dark';

        document.documentElement.setAttribute('data-theme', newTheme);
        localStorage.setItem('theme', newTheme);
        setSyncedData({ theme: newTheme });
        if (themeSelect) themeSelect.value = newTheme;

        this.updateThemeIcon(newTheme === 'dark');
      });
    }

    // 跨设备/标签页同步主题
    chrome.storage.onChanged.addListener((changes, area) => {
      if (area !== 'sync' || !changes.theme) return;
      getDataSyncEnabled().then((enabled) => {
        if (!enabled) return;
        const nextTheme = changes.theme.newValue || 'auto';
        localStorage.setItem('theme', nextTheme);
        applyThemePreference(nextTheme);
      });
    });
  }

  initGlassOpacitySettings() {
    const slider = document.getElementById('glass-opacity-slider');
    const valueEl = document.getElementById('glass-opacity-value');
    if (!slider || !valueEl) return;

    const defaultOpacity =
      typeof window.DEFAULT_GLASS_OPACITY === 'number' ? window.DEFAULT_GLASS_OPACITY : 48;

    const applyValue = (raw, { persist = false } = {}) => {
      const percent = window.applyGlassOpacity
        ? window.applyGlassOpacity(raw, { persist })
        : Number(raw) || defaultOpacity;
      slider.value = String(percent);
      valueEl.textContent = String(percent);
      return percent;
    };

    const cached = localStorage.getItem('glassOpacity');
    applyValue(cached != null ? cached : defaultOpacity, { persist: false });

    chrome.storage.sync.get(['glassOpacity'], (result) => {
      if (result.glassOpacity != null) {
        applyValue(result.glassOpacity, { persist: false });
      }
    });

    slider.addEventListener('input', (e) => {
      applyValue(e.target.value, { persist: false });
    });

    const persist = () => applyValue(slider.value, { persist: true });
    slider.addEventListener('change', persist);
    slider.addEventListener('mouseup', persist);
    slider.addEventListener('touchend', persist);

    chrome.storage.onChanged.addListener((changes, area) => {
      if ((area !== 'sync' && area !== 'local') || !changes.glassOpacity) return;
      if (changes.glassOpacity.newValue != null) {
        applyValue(changes.glassOpacity.newValue, { persist: false });
      }
    });
  }

  setThemeBasedOnSystem() {
    const isDarkMode = window.matchMedia('(prefers-color-scheme: dark)').matches;
    const theme = isDarkMode ? 'dark' : 'light';
    document.documentElement.setAttribute('data-theme', theme);
    this.updateThemeIcon(isDarkMode);
  }

  updateThemeIcon(isDark) {
    const themeToggleBtn = document.getElementById('theme-toggle-btn');
    if (!themeToggleBtn) return;
    
    themeToggleBtn.innerHTML = isDark ? ICONS.dark_mode : ICONS.light_mode;
  }

  initQuickLinksSettings() {
    if (!this.enableQuickLinksCheckbox) return;

    // 加载快捷链接设置
    chrome.storage.sync.get(['enableQuickLinks'], (result) => {
      this.enableQuickLinksCheckbox.checked = result.enableQuickLinks !== false;
      this.toggleQuickLinksVisibility(this.enableQuickLinksCheckbox.checked);
    });

    // 监听快捷链接设置变化
    this.enableQuickLinksCheckbox.addEventListener('change', () => {
      const isEnabled = this.enableQuickLinksCheckbox.checked;
      chrome.storage.sync.set({ enableQuickLinks: isEnabled }, () => {
        this.toggleQuickLinksVisibility(isEnabled);
      });
    });
  }

  toggleQuickLinksVisibility(show) {
    const quickLinksWrapper = document.querySelector('.recently-visited-links-wrapper');
    if (quickLinksWrapper) {
      quickLinksWrapper.style.display = show ? 'flex' : 'none';
    }
  }

  initPluginBarSettings() {
    this.enablePluginBarCheckbox = document.getElementById('enable-plugin-bar');
    this.showPluginClockCheckbox = document.getElementById('show-plugin-clock');
    this.showPluginTodoCheckbox = document.getElementById('show-plugin-todo');
    this.showPluginNotesCheckbox = document.getElementById('show-plugin-notes');

    chrome.storage.sync.get(
      ['enablePluginBar', 'showPluginClock', 'showPluginTodo', 'showPluginNotes'],
      (result) => {
        if (this.enablePluginBarCheckbox) {
          this.enablePluginBarCheckbox.checked = result.enablePluginBar !== false;
        }
        if (this.showPluginClockCheckbox) {
          this.showPluginClockCheckbox.checked = result.showPluginClock !== false;
        }
        if (this.showPluginTodoCheckbox) {
          this.showPluginTodoCheckbox.checked = result.showPluginTodo !== false;
        }
        if (this.showPluginNotesCheckbox) {
          this.showPluginNotesCheckbox.checked = result.showPluginNotes !== false;
        }
      }
    );

    const applyPluginBarUpdate = (partial) => {
      chrome.storage.sync.set(partial);
      if (window.pluginBarManager) {
        window.pluginBarManager.updateFromSettings(partial);
      }
    };

    if (this.enablePluginBarCheckbox) {
      this.enablePluginBarCheckbox.addEventListener('change', async () => {
        if (this.enablePluginBarCheckbox.checked) {
          const entitled = await isFeatureEntitled(PRO_FEATURES.PLUGIN_BAR);
          if (!entitled) {
            this.enablePluginBarCheckbox.checked = false;
            const status = await getCachedMembershipStatus();
            if (typeof window.settingsManager?.openAccountSettings === 'function') {
              window.settingsManager.openAccountSettings({ highlightUpgrade: true });
            }
            window.dispatchEvent(new CustomEvent('supermark:pro-required', {
              detail: {
                featureId: PRO_FEATURES.PLUGIN_BAR,
                signedIn: Boolean(status?.signedIn)
              }
            }));
            return;
          }
        }
        applyPluginBarUpdate({ enablePluginBar: this.enablePluginBarCheckbox.checked });
      });
    }
    if (this.showPluginClockCheckbox) {
      this.showPluginClockCheckbox.addEventListener('change', () => {
        applyPluginBarUpdate({ showPluginClock: this.showPluginClockCheckbox.checked });
      });
    }
    if (this.showPluginTodoCheckbox) {
      this.showPluginTodoCheckbox.addEventListener('change', () => {
        applyPluginBarUpdate({ showPluginTodo: this.showPluginTodoCheckbox.checked });
      });
    }
    if (this.showPluginNotesCheckbox) {
      this.showPluginNotesCheckbox.addEventListener('change', () => {
        applyPluginBarUpdate({ showPluginNotes: this.showPluginNotesCheckbox.checked });
      });
    }
  }

  initLinkOpeningSettings() {
    // 检查元素是否存在
    if (!this.openInNewTabCheckbox) {
      console.log('openInNewTabCheckbox not found, skipping settings initialization');
      return;
    }
    
    // 加载链接打开方式设置
    chrome.storage.sync.get(['openInNewTab'], (result) => {
      this.openInNewTabCheckbox.checked = result.openInNewTab === true;
    });

    // 监听设置变化
    this.openInNewTabCheckbox.addEventListener('change', () => {
      const isEnabled = this.openInNewTabCheckbox.checked;
      chrome.storage.sync.set({ openInNewTab: isEnabled });
    });
  }

  initWheelSwitchingTab() {
    // 加载保存的设置
    chrome.storage.sync.get({ enableWheelSwitching: false }, (result) => {
      if (this.enableWheelSwitchingCheckbox) {
        this.enableWheelSwitchingCheckbox.checked = result.enableWheelSwitching;
        
        // 添加事件监听器
        this.enableWheelSwitchingCheckbox.addEventListener('change', () => {
          const isEnabled = this.enableWheelSwitchingCheckbox.checked;
          chrome.storage.sync.set({ enableWheelSwitching: isEnabled });
          
          // 触发自定义事件，通知滚轮切换状态变化
          document.dispatchEvent(new CustomEvent('wheelSwitchingChanged', {
            detail: { enabled: isEnabled }
          }));
        });
      }
    });
  }

  // 添加 debounce 方法来优化性能
  debounce(func, wait) {
    let timeout;
    return function executedFunction(...args) {
      const later = () => {
        clearTimeout(timeout);
        func(...args);
      };
      clearTimeout(timeout);
      timeout = setTimeout(later, wait);
    };
  }

  initBookmarkWidthSettings() {
    // 获取元素引用
    this.widthSlider = document.getElementById('width-slider');
    this.widthValue = document.getElementById('width-value');
    this.widthPreviewCount = document.getElementById('width-preview-count');
    
    if (!this.widthSlider || !this.widthValue) {
      console.log('Width slider elements not found, skipping bookmark width settings initialization');
      return;
    }
    
    // 从存储中获取保存的宽度值
    chrome.storage.sync.get(['bookmarkWidth'], (result) => {
      const savedWidth = result.bookmarkWidth || 150; // 默认150px
      this.widthSlider.value = savedWidth;
      this.widthValue.textContent = savedWidth;
      this.updatePreviewCount(savedWidth);
      this.updateBookmarkWidth(savedWidth);
    });
    
    // 监听滑块的变化
    this.widthSlider.addEventListener('input', (e) => {
      const width = e.target.value;
      this.widthValue.textContent = width;
      this.updatePreviewCount(width);
      this.updateBookmarkWidth(width);
    });
      
    // 监听滑块的鼠标释放事件
    this.widthSlider.addEventListener('mouseup', () => {
      // 保存设置
      chrome.storage.sync.set({ bookmarkWidth: this.widthSlider.value });
    });
        
    // 添加窗口大小改变的监听
    const debouncedUpdate = this.debounce(() => {
      this.updatePreviewCount(this.widthSlider.value);
    }, 250);
    window.addEventListener('resize', debouncedUpdate);
  }
  
  // 新增书签卡片高度设置函数
  initCardHeightSettings() {
    // 获取滑块和显示元素
    this.heightSlider = document.getElementById('height-slider');
    this.heightValue = document.getElementById('height-value');
    
    if (!this.heightSlider || !this.heightValue) {
      console.log('Height slider elements not found, skipping card height settings initialization');
      return;
    }
    
    // 从存储中获取保存的高度值
    chrome.storage.sync.get('bookmarkCardHeight', (result) => {
      const savedHeight = result.bookmarkCardHeight || 36; // 默认值为36px
      
      // 设置滑块和显示值
      this.heightSlider.value = savedHeight;
      this.heightValue.textContent = savedHeight;
      
      // 应用高度设置
      this.updateCardHeight(savedHeight);
    });
    
    // 监听滑块的变化
    this.heightSlider.addEventListener('input', (e) => {
      const height = e.target.value;
      this.heightValue.textContent = height;
      this.updateCardHeight(height);
    });
    
    // 监听滑块的鼠标释放事件
    this.heightSlider.addEventListener('mouseup', () => {
      // 保存设置
      chrome.storage.sync.set({ bookmarkCardHeight: this.heightSlider.value });
    });
  }
  
  // 更新书签卡片高度
  updateCardHeight(height) {
    document.documentElement.style.setProperty('--bookmark-card-height', `${height}px`);

    // 创建或更新自定义样式
    let styleElement = document.getElementById('custom-card-height');
    if (!styleElement) {
      styleElement = document.createElement('style');
      styleElement.id = 'custom-card-height';
      document.head.appendChild(styleElement);
    }
    
    // 设置卡片高度（含新建链接 / 新建文件夹按钮）
    styleElement.textContent = `
      .card,
      .bookmark-add-card,
      .bookmark-tree-action-btn {
        height: ${height}px !important;
      }
    `;
  }

  updatePreviewCount(width) {
    // 获取书签列表容器
    const bookmarksList = document.getElementById('bookmarks-list');
    if (!bookmarksList) return;

    // 确保容器可见
    const originalDisplay = bookmarksList.style.display;
    if (getComputedStyle(bookmarksList).display === 'none') {
      bookmarksList.style.display = 'grid';
    }

    // 获取容器的实际可用宽度
    const containerStyle = getComputedStyle(bookmarksList);
    const containerWidth = bookmarksList.offsetWidth 
      - parseFloat(containerStyle.paddingLeft) 
      - parseFloat(containerStyle.paddingRight);

    // 还原容器显示状态
    bookmarksList.style.display = originalDisplay;

    // 使用与 CSS Grid 相同的计算逻辑
    const gap = 16; // gap: 1rem
    const minWidth = parseInt(width);
    
    // 计算一行能容纳的最大数量
    // 使用 Math.floor 确保不会超出容器宽度
    const count = Math.floor((containerWidth + gap) / (minWidth + gap));
    
    // 更新显示 - 使用本地化文本
    const previewText = chrome.i18n.getMessage("bookmarksPerRow", [count]) || `${count} 个/行`;
    this.widthPreviewCount.textContent = previewText;
  }

  updateBookmarkWidth(width) {
    document.documentElement.style.setProperty('--bookmark-width', width + 'px');
    const columns = `repeat(auto-fill, minmax(${width}px, 1fr))`;
    document.querySelectorAll('.bookmark-section-items').forEach(el => {
      el.style.gridTemplateColumns = columns;
    });
  }

  initContainerWidthSettings() {
    // 获取元素引用
    this.containerWidthSlider = document.getElementById('container-width-slider');
    this.containerWidthValue = document.getElementById('container-width-value');
    
    if (!this.containerWidthSlider || !this.containerWidthValue) {
      console.log('Container width slider elements not found, skipping container width settings initialization');
      return;
    }
    
    // 从存储中获取保存的宽度值
    chrome.storage.sync.get(['bookmarkContainerWidth'], (result) => {
      const savedWidth = result.bookmarkContainerWidth || 100; // 默认100%
      this.containerWidthSlider.value = savedWidth;
      this.containerWidthValue.textContent = savedWidth;
      this.updateContainerWidth(savedWidth);
    });
    
    // 监听滑块的变化
    this.containerWidthSlider.addEventListener('input', (e) => {
      const width = e.target.value;
      this.containerWidthValue.textContent = width;
      this.updateContainerWidth(width);
    });
    
    // 监听滑块的鼠标释放事件，保存设置
    this.containerWidthSlider.addEventListener('mouseup', () => {
      // 保存设置
      chrome.storage.sync.set({ bookmarkContainerWidth: this.containerWidthSlider.value });
    });
  }

  // 更新书签容器宽度的方法
  updateContainerWidth(widthPercent) {
    const bookmarksContainer = document.querySelector('.bookmarks-container');
    if (bookmarksContainer) {
      bookmarksContainer.style.width = `${widthPercent}%`;
    }
  }

  initLayoutSettings() {
    // 获取元素引用
    this.showSearchBoxCheckbox = document.getElementById('show-search-box');
    this.showWelcomeMessageCheckbox = document.getElementById('show-welcome-message');
    this.showFooterCheckbox = document.getElementById('show-footer');
    this.enableSidebarOpenAnimationCheckbox = document.getElementById('enable-sidebar-open-animation');

    // 添加快捷链接图标的设置
    this.showHistoryLinkCheckbox = document.getElementById('show-history-link');
    this.showDownloadsLinkCheckbox = document.getElementById('show-downloads-link');
    this.showPasswordsLinkCheckbox = document.getElementById('show-passwords-link');
    this.showExtensionsLinkCheckbox = document.getElementById('show-extensions-link');
    this.showChromeWebStoreLinkCheckbox = document.getElementById('show-chrome-web-store-link');
    this.showCustomizeChromeLinkCheckbox = document.getElementById('show-customize-chrome-link');
    this.enableLinksIconsReorderCheckbox = document.getElementById('enable-chrome-quick-icons-reorder');

    // 加载保存的设置
    chrome.storage.sync.get(
      [
        'showSearchBox', 
        'showWelcomeMessage', 
        'showFooter',
        'enableSidebarOpenAnimation',
        'showHistoryLink',
        'showDownloadsLink',
        'showPasswordsLink',
        'showExtensionsLink',
        'showChromeWebStoreLink',
        'showCustomizeChromeLink',
        'enableLinksIconsReorder'
      ],
      (result) => {
        // 设置复选框状态 - 搜索框默认为开启
        if (this.showSearchBoxCheckbox) {
          this.showSearchBoxCheckbox.checked = result.showSearchBox !== false;
        }
        if (this.showWelcomeMessageCheckbox) {
          this.showWelcomeMessageCheckbox.checked = result.showWelcomeMessage !== false;
        }
        if (this.showFooterCheckbox) {
          this.showFooterCheckbox.checked = result.showFooter !== false;
        }
        if (this.enableSidebarOpenAnimationCheckbox) {
          this.enableSidebarOpenAnimationCheckbox.checked = result.enableSidebarOpenAnimation !== false;
        }
        
        // 设置快捷链接图标的状态
        if (this.showHistoryLinkCheckbox) {
          this.showHistoryLinkCheckbox.checked = result.showHistoryLink !== false;
        }
        if (this.showDownloadsLinkCheckbox) {
          this.showDownloadsLinkCheckbox.checked = result.showDownloadsLink !== false;
        }
        if (this.showPasswordsLinkCheckbox) {
          this.showPasswordsLinkCheckbox.checked = result.showPasswordsLink !== false;
        }
        if (this.showExtensionsLinkCheckbox) {
          this.showExtensionsLinkCheckbox.checked = result.showExtensionsLink !== false;
        }
        if (this.showChromeWebStoreLinkCheckbox) {
          this.showChromeWebStoreLinkCheckbox.checked = result.showChromeWebStoreLink !== false;
        }
        if (this.showCustomizeChromeLinkCheckbox) {
          this.showCustomizeChromeLinkCheckbox.checked = result.showCustomizeChromeLink !== false;
        }
        if (this.enableLinksIconsReorderCheckbox) {
          this.enableLinksIconsReorderCheckbox.checked = result.enableLinksIconsReorder !== false;
        }
        
        // 应用设置到界面
        this.toggleElementVisibility('#history-link', result.showHistoryLink !== false);
        this.toggleElementVisibility('#downloads-link', result.showDownloadsLink !== false);
        this.toggleElementVisibility('#passwords-link', result.showPasswordsLink !== false);
        this.toggleElementVisibility('#extensions-link', result.showExtensionsLink !== false);
        this.toggleElementVisibility('#chrome-web-store-link', result.showChromeWebStoreLink !== false);
        this.toggleElementVisibility('#customize-chrome-link', result.showCustomizeChromeLink !== false);

        // 检查是否所有链接都被隐藏
        const linksContainer = document.querySelector('.chrome-quick-icons');
        if (linksContainer) {
          const allLinksHidden = 
            result.showHistoryLink === false && 
            result.showDownloadsLink === false && 
            result.showPasswordsLink === false && 
            result.showExtensionsLink === false &&
            result.showChromeWebStoreLink === false &&
            result.showCustomizeChromeLink === false;
          
          linksContainer.style.display = allLinksHidden ? 'none' : '';
        }
      }
    );

    // 监听设置变化
    this.showSearchBoxCheckbox?.addEventListener('change', () => {
      const isVisible = this.showSearchBoxCheckbox.checked;
      chrome.storage.sync.set({ showSearchBox: isVisible });
      
      // 立即应用设置
      const searchContainer = document.querySelector('.search-container');
      if (searchContainer) {
        searchContainer.style.display = isVisible ? '' : 'none';
      }
      
      // 立即更新欢迎语显示
      if (window.WelcomeManager) {
        window.WelcomeManager.updateWelcomeMessage();
      }
    });

    this.showWelcomeMessageCheckbox?.addEventListener('change', async () => {
      const isVisible = this.showWelcomeMessageCheckbox.checked;

      // 开启欢迎语需要先登录，名称取自登录账号
      if (isVisible) {
        const status = await getCachedMembershipStatus();
        if (!status?.signedIn) {
          this.showWelcomeMessageCheckbox.checked = false;
          const message = getMembershipErrorMessage({ message: 'NOT_SIGNED_IN' });
          if (typeof window.Utilities?.showToast === 'function') {
            window.Utilities.showToast(message, 2800);
          }
          this.openAccountSettings();
          return;
        }
      }

      chrome.storage.sync.set({ showWelcomeMessage: isVisible });

      if (window.WelcomeManager) {
        window.WelcomeManager.updateWelcomeMessage();
      }
    });

    if (this.enableSidebarOpenAnimationCheckbox) {
      this.enableSidebarOpenAnimationCheckbox.addEventListener('change', () => {
        const enabled = this.enableSidebarOpenAnimationCheckbox.checked;
        chrome.storage.sync.set({ enableSidebarOpenAnimation: enabled });
      });
    }

    this.showFooterCheckbox?.addEventListener('change', () => {
      const isVisible = this.showFooterCheckbox.checked;
      chrome.storage.sync.set({ showFooter: isVisible });
      
      // 立即应用设置
      const footer = document.querySelector('footer');
      if (footer) {
        footer.style.display = isVisible ? '' : 'none';
      }
    });

    // 添加事件监听器
    this.showHistoryLinkCheckbox?.addEventListener('change', () => {
      const isVisible = this.showHistoryLinkCheckbox.checked;
      chrome.storage.sync.set({ showHistoryLink: isVisible });
      this.toggleElementVisibility('#history-link', isVisible);
    });

    this.showDownloadsLinkCheckbox?.addEventListener('change', () => {
      const isVisible = this.showDownloadsLinkCheckbox.checked;
      chrome.storage.sync.set({ showDownloadsLink: isVisible });
      this.toggleElementVisibility('#downloads-link', isVisible);
    });

    this.showPasswordsLinkCheckbox?.addEventListener('change', () => {
      const isVisible = this.showPasswordsLinkCheckbox.checked;
      chrome.storage.sync.set({ showPasswordsLink: isVisible });
      this.toggleElementVisibility('#passwords-link', isVisible);
    });

    this.showExtensionsLinkCheckbox?.addEventListener('change', () => {
      const isVisible = this.showExtensionsLinkCheckbox.checked;
      chrome.storage.sync.set({ showExtensionsLink: isVisible });
      this.toggleElementVisibility('#extensions-link', isVisible);
    });

    if (this.showChromeWebStoreLinkCheckbox) {
      this.showChromeWebStoreLinkCheckbox.addEventListener('change', () => {
        const isVisible = this.showChromeWebStoreLinkCheckbox.checked;
        chrome.storage.sync.set({ showChromeWebStoreLink: isVisible });
        this.toggleElementVisibility('#chrome-web-store-link', isVisible);
      });
    }

    if (this.showCustomizeChromeLinkCheckbox) {
      this.showCustomizeChromeLinkCheckbox.addEventListener('change', () => {
        const isVisible = this.showCustomizeChromeLinkCheckbox.checked;
        chrome.storage.sync.set({ showCustomizeChromeLink: isVisible });
        this.toggleElementVisibility('#customize-chrome-link', isVisible);
      });
    }

    if (this.enableLinksIconsReorderCheckbox) {
      this.enableLinksIconsReorderCheckbox.addEventListener('change', async () => {
        const enabled = this.enableLinksIconsReorderCheckbox.checked;
        if (enabled) {
          const allowed = await requireProFeature(PRO_FEATURES.QUICK_LINKS_DRAG_TO_REORDER);
          if (!allowed) {
            this.enableLinksIconsReorderCheckbox.checked = false;
            chrome.storage.sync.set({ enableLinksIconsReorder: false });
            return;
          }
        }
        chrome.storage.sync.set({ enableLinksIconsReorder: enabled });
      });
    }
  }

  // 辅助方法：切换元素可见性
  toggleElementVisibility(selector, isVisible) {
    const element = document.querySelector(selector);
    if (element) {
      element.style.display = isVisible ? '' : 'none';
      
      // 特殊处理 chrome-quick-icons 容器
      if (selector.includes('link')) {
        const linksContainer = document.querySelector('.chrome-quick-icons');
        if (linksContainer) {
          // 检查是否所有链接都被隐藏
          const visibleLinks = Array.from(linksContainer.querySelectorAll('a')).filter(
            link => link.style.display !== 'none'
          ).length;
          
          linksContainer.style.display = visibleLinks === 0 ? 'none' : '';
        }
      }
    }
  }

  initSearchSuggestionsSettings() {
    // 获取元素引用
    this.showHistorySuggestionsCheckbox = document.getElementById('show-history-suggestions');
    this.showBookmarkSuggestionsCheckbox = document.getElementById('show-bookmark-suggestions');
    this.openSearchInNewTabCheckbox = document.getElementById('open-search-in-new-tab');
    
    // 加载搜索建议设置
    chrome.storage.sync.get(
      ['showHistorySuggestions', 'showBookmarkSuggestions', 'showSearchBox', 'openSearchInNewTab'], 
      (result) => {
        // 如果设置不存在(undefined)或者没有明确设置为 false,则默认为 true
        this.showHistorySuggestionsCheckbox.checked = result.showHistorySuggestions !== false;
        this.showBookmarkSuggestionsCheckbox.checked = result.showBookmarkSuggestions !== false;
        this.openSearchInNewTabCheckbox.checked = result.openSearchInNewTab !== false;

        // 初始化时如果是新用户(设置不存在),则保存默认值
        if (!('showHistorySuggestions' in result)) {
          chrome.storage.sync.set({ showHistorySuggestions: true });
        }
        if (!('showBookmarkSuggestions' in result)) {
          chrome.storage.sync.set({ showBookmarkSuggestions: true });
        }
        if (!('showSearchBox' in result)) {
          chrome.storage.sync.set({ showSearchBox: true });
        }
        if (!('openSearchInNewTab' in result)) {
          chrome.storage.sync.set({ openSearchInNewTab: true });
        }
      }
    );

    // 监听设置变化
    this.showHistorySuggestionsCheckbox.addEventListener('change', () => {
      const isEnabled = this.showHistorySuggestionsCheckbox.checked;
      chrome.storage.sync.set({ showHistorySuggestions: isEnabled });
    });

    this.showBookmarkSuggestionsCheckbox.addEventListener('change', () => {
      const isEnabled = this.showBookmarkSuggestionsCheckbox.checked;
      chrome.storage.sync.set({ showBookmarkSuggestions: isEnabled });
    });
    
    this.openSearchInNewTabCheckbox.addEventListener('change', () => {
      const isEnabled = this.openSearchInNewTabCheckbox.checked;
      chrome.storage.sync.set({ openSearchInNewTab: isEnabled });
    });
  }

  initUpdateCheck() {
    this.updateStatusEl = document.getElementById('update-check-status');
    this.updateCheckBtn = document.getElementById('check-update-button');
    this.updateDownloadBtn = document.getElementById('download-update-button');
    this.updateActionsEl = document.getElementById('update-check-actions');
    this.autoCheckUpdatesCheckbox = document.getElementById('auto-check-updates');
    this.aboutTabButton = document.querySelector('.settings-tab-button[data-tab="about"]');
    this.updateDialog = document.getElementById('update-available-dialog');
    this.updateDialogMessage = document.getElementById('update-available-dialog-message');
    this.updateDialogLaterBtn = document.getElementById('update-available-later');
    this.updateDialogViewBtn = document.getElementById('update-available-view');
    this.updateDialogCloseBtn = document.getElementById('update-available-close');
    this._updateCheckInFlight = null;
    this._lastUpdateResult = null;
    this._autoCheckUpdatesEnabled = true;
    this._updateDialogBound = false;

    this.bindUpdateAvailableDialog();

    if (this.autoCheckUpdatesCheckbox) {
      chrome.storage.sync.get({ autoCheckUpdates: true }, (result) => {
        this._autoCheckUpdatesEnabled = result.autoCheckUpdates !== false;
        this.autoCheckUpdatesCheckbox.checked = this._autoCheckUpdatesEnabled;
        this.refreshUpdateIdleMessage();
        if (this._autoCheckUpdatesEnabled) {
          this.maybeAutoCheckUpdates({ silent: true, promptIfAvailable: true });
        }
      });
      this.autoCheckUpdatesCheckbox.addEventListener('change', () => {
        this._autoCheckUpdatesEnabled = Boolean(this.autoCheckUpdatesCheckbox.checked);
        chrome.storage.sync.set({ autoCheckUpdates: this._autoCheckUpdatesEnabled });
        this.refreshUpdateIdleMessage();
        if (this._autoCheckUpdatesEnabled) {
          this.runUpdateCheck({ force: true, silent: false, promptIfAvailable: true });
        }
      });
    } else {
      this.refreshUpdateIdleMessage();
      this.maybeAutoCheckUpdates({ silent: true, promptIfAvailable: true });
    }

    if (this.updateCheckBtn) {
      this.updateCheckBtn.addEventListener('click', () => {
        this.runUpdateCheck({
          force: true,
          silent: false,
          promptIfAvailable: true,
          forcePrompt: true
        });
      });
    }

    if (this.updateDownloadBtn) {
      this.updateDownloadBtn.addEventListener('click', () => {
        this.openUpdateDownloadPage(this._lastUpdateResult);
      });
    }

    // 语言切换后刷新文案
    document.addEventListener('uilanguagechange', () => {
      if (this._lastUpdateResult) {
        this.renderUpdateStatus(this._lastUpdateResult, { checking: false });
      } else {
        this.refreshUpdateIdleMessage();
      }
      if (this._accountCatalog) {
        this.renderAccountPricing(this._accountCatalog);
      }
      if (this.updateDialog && !this.updateDialog.hidden && this._lastUpdateResult) {
        this.fillUpdateAvailableDialog(this._lastUpdateResult);
      }
    });
  }

  isAutoCheckUpdatesEnabled() {
    if (this.autoCheckUpdatesCheckbox) {
      return Boolean(this.autoCheckUpdatesCheckbox.checked);
    }
    return this._autoCheckUpdatesEnabled !== false;
  }

  refreshUpdateIdleMessage() {
    if (!this.updateStatusEl || this._lastUpdateResult) return;
    const autoOn = this.isAutoCheckUpdatesEnabled();
    const key = autoOn ? 'updateCheckIdleMessage' : 'updateCheckIdleManualMessage';
    const fallback = autoOn
      ? 'Updates are checked automatically when Settings opens'
      : 'Auto-check is off. Click the button to check manually.';
    this.updateStatusEl.classList.remove('is-checking', 'is-latest', 'is-available', 'is-error');
    this.updateStatusEl.textContent = this.i18nMessage(key, fallback);
  }

  maybeAutoCheckUpdates({ silent = true, promptIfAvailable = true } = {}) {
    if (!this.isAutoCheckUpdatesEnabled()) return Promise.resolve(null);
    return this.runUpdateCheck({ force: false, silent, promptIfAvailable });
  }

  openUpdateDownloadPage(result = this._lastUpdateResult) {
    const url = result?.htmlUrl || result?.zipUrl || result?.crxUrl;
    if (url) {
      chrome.tabs.create({ url });
    }
  }

  bindUpdateAvailableDialog() {
    if (this._updateDialogBound || !this.updateDialog) return;
    this._updateDialogBound = true;

    const dismiss = () => {
      this.dismissUpdateAvailableDialog();
    };

    this.updateDialogLaterBtn?.addEventListener('click', dismiss);
    this.updateDialogCloseBtn?.addEventListener('click', dismiss);
    this.updateDialogViewBtn?.addEventListener('click', () => {
      this.openUpdateDownloadPage(this._lastUpdateResult);
      this.dismissUpdateAvailableDialog({ remember: true });
    });
    this.updateDialog.addEventListener('click', (event) => {
      if (event.target === this.updateDialog) {
        dismiss();
      }
    });
  }

  fillUpdateAvailableDialog(result) {
    if (!this.updateDialogMessage || !result) return;
    const latest = result.latestVersion || '';
    const local = result.localVersion || chrome.runtime.getManifest()?.version || '';
    this.updateDialogMessage.textContent = this.accountMessage(
      'updateAvailableDialogMessage',
      'SuperMark $1 is available. You are currently on $2.',
      [String(latest), String(local)]
    ) || `SuperMark ${latest} is available. You are currently on ${local}.`;
  }

  async shouldPromptForUpdate(result) {
    if (!result || result.status !== 'updateAvailable' || !result.latestVersion) {
      return false;
    }
    const latest = String(result.latestVersion);
    const stored = await new Promise((resolve) => {
      chrome.storage.local.get(['updatePromptDismissedVersion'], (data) => resolve(data || {}));
    });
    const dismissed = String(stored.updatePromptDismissedVersion || '');
    if (!dismissed) return true;
    // Only skip if user already dismissed this exact (or newer) version prompt
    return compareVersions(latest, dismissed) > 0;
  }

  async showUpdateAvailableDialog(result, { forcePrompt = false } = {}) {
    if (!this.updateDialog || !result || result.status !== 'updateAvailable') return;
    if (!forcePrompt) {
      const shouldPrompt = await this.shouldPromptForUpdate(result);
      if (!shouldPrompt) return;
    }
    this._lastUpdateResult = result;
    this.fillUpdateAvailableDialog(result);
    this.updateDialog.hidden = false;
    this.updateDialog.style.display = 'block';
  }

  dismissUpdateAvailableDialog({ remember = true } = {}) {
    if (!this.updateDialog) return;
    if (remember && this._lastUpdateResult?.latestVersion) {
      chrome.storage.local.set({
        updatePromptDismissedVersion: String(this._lastUpdateResult.latestVersion)
      });
    }
    this.updateDialog.hidden = true;
    this.updateDialog.style.display = 'none';
  }

  setAboutTabBadge(hasUpdate) {
    if (!this.aboutTabButton) return;
    this.aboutTabButton.classList.toggle('has-update', !!hasUpdate);
  }

  renderUpdateStatus(result, { checking = false } = {}) {
    if (!this.updateStatusEl) return;

    this.updateStatusEl.classList.remove(
      'is-checking',
      'is-latest',
      'is-available',
      'is-error'
    );

    if (checking) {
      this.updateStatusEl.classList.add('is-checking');
      this.updateStatusEl.textContent =
        (typeof window.getLocalizedMessage === 'function'
          ? window.getLocalizedMessage('updateCheckingMessage')
          : null) ||
        chrome.i18n.getMessage('updateCheckingMessage') ||
        'Checking for updates...';
      if (this.updateActionsEl) this.updateActionsEl.hidden = true;
      if (this.updateCheckBtn) this.updateCheckBtn.disabled = true;
      return;
    }

    if (this.updateCheckBtn) this.updateCheckBtn.disabled = false;
    this._lastUpdateResult = result;

    const statusText = formatUpdateStatus(result);
    if (result?.status === 'updateAvailable') {
      this.updateStatusEl.classList.add('is-available');
      this.updateStatusEl.textContent = statusText;
      if (this.updateActionsEl) this.updateActionsEl.hidden = false;
      if (this.updateDownloadBtn) this.updateDownloadBtn.hidden = false;
      this.setAboutTabBadge(true);
    } else if (result?.status === 'upToDate') {
      this.updateStatusEl.classList.add('is-latest');
      this.updateStatusEl.textContent = statusText;
      if (this.updateActionsEl) this.updateActionsEl.hidden = true;
      this.setAboutTabBadge(false);
    } else {
      this.updateStatusEl.classList.add('is-error');
      this.updateStatusEl.textContent = statusText;
      if (this.updateActionsEl) this.updateActionsEl.hidden = true;
      this.setAboutTabBadge(false);
    }
  }

  async runUpdateCheck({
    force = false,
    silent = false,
    promptIfAvailable = false,
    forcePrompt = false
  } = {}) {
    if (!this.updateStatusEl && !this.aboutTabButton && !this.updateDialog) return;

    if (this._updateCheckInFlight) {
      return this._updateCheckInFlight;
    }

    if (!silent) {
      this.renderUpdateStatus(null, { checking: true });
    }

    this._updateCheckInFlight = checkForUpdate({ force })
      .then(async (result) => {
        this.renderUpdateStatus(result, { checking: false });
        if (promptIfAvailable && result?.status === 'updateAvailable') {
          await this.showUpdateAvailableDialog(result, { forcePrompt });
        }
        return result;
      })
      .catch((err) => {
        const result = {
          status: 'error',
          localVersion: chrome.runtime.getManifest()?.version || '0',
          checkedAt: Date.now(),
          message: err?.message || 'Update check failed'
        };
        this.renderUpdateStatus(result, { checking: false });
        return result;
      })
      .finally(() => {
        this._updateCheckInFlight = null;
      });

    return this._updateCheckInFlight;
  }

  initClearCache() {
    this.clearCacheBtn = document.getElementById('clear-favicon-cache-button');
    this.clearCacheStatusEl = document.getElementById('clear-cache-status');
    if (!this.clearCacheBtn) return;

    this.clearCacheBtn.addEventListener('click', async () => {
      const confirmed = window.confirm(
        this.i18nMessage(
          'clearCacheConfirm',
          'Clear cached bookmark icons? Icons will be fetched again afterward.'
        )
      );
      if (!confirmed) return;

      this.clearCacheBtn.disabled = true;
      if (this.clearCacheStatusEl) {
        this.clearCacheStatusEl.hidden = false;
        this.clearCacheStatusEl.classList.remove('is-success', 'is-error');
        this.clearCacheStatusEl.textContent = this.i18nMessage(
          'clearCacheClearing',
          'Clearing cache...'
        );
      }

      try {
        await clearFaviconCache();
        this.reloadVisibleFavicons();
        document.dispatchEvent(new CustomEvent('faviconcachecleared'));

        if (this.clearCacheStatusEl) {
          this.clearCacheStatusEl.classList.add('is-success');
          this.clearCacheStatusEl.textContent = this.i18nMessage(
            'clearCacheSuccess',
            'Icon cache cleared.'
          );
        }
      } catch (error) {
        console.error('Failed to clear favicon cache:', error);
        if (this.clearCacheStatusEl) {
          this.clearCacheStatusEl.classList.add('is-error');
          this.clearCacheStatusEl.textContent = this.i18nMessage(
            'clearCacheFailed',
            'Failed to clear cache. Please try again.'
          );
        }
      } finally {
        this.clearCacheBtn.disabled = false;
      }
    });
  }

  reloadVisibleFavicons() {
    document.querySelectorAll('img.bookmark-favicon').forEach((img) => {
      const card = img.closest('a.bookmark-card');
      const pageUrl = card?.href;
      if (pageUrl) {
        applyFaviconWithFallback(img, pageUrl);
      }
    });

    document.querySelectorAll('.quick-link-item-container').forEach((item) => {
      const pageUrl = item.dataset.url;
      const img = item.querySelector('img');
      if (pageUrl && img) {
        applyFaviconWithFallback(img, pageUrl);
      }
    });
  }
}

// 导出设置管理器实例
export const settingsManager = new SettingsManager();
window.settingsManager = settingsManager;