// Copyright 2015 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
/* eslint-disable @devtools/no-imperative-dom-api */

import * as Common from '../../core/common/common.js';
import * as i18n from '../../core/i18n/i18n.js';
import * as Platform from '../../core/platform/platform.js';
import * as SDK from '../../core/sdk/sdk.js';
import * as Protocol from '../../generated/protocol.js';
import * as SettingsUI from '../../ui/legacy/components/settings_ui/settings_ui.js';
import * as UI from '../../ui/legacy/legacy.js';
import * as VisualLogging from '../../ui/visual_logging/visual_logging.js';
import * as MobileThrottling from '../mobile_throttling/mobile_throttling.js';
import * as EmulationComponents from '../settings/emulation/components/components.js';

import networkConfigViewStyles from './networkConfigView.css.js';

const UIStrings = {
  /**
   * @description Text in the Network conditions panel shown in the dropdown where the user chooses the user agent.
   */
  custom: 'Custom…',
  /**
   * @description Placeholder text shown in the input box where a user is expected to add a custom user agent.
   */
  enterACustomUserAgent: 'Enter a custom user agent',
  /**
   * @description Error message when the custom user agent field is empty.
   */
  customUserAgentFieldIsRequired: 'Custom user agent field is required',
  /**
   * @description Header for the caching settings within the network conditions panel.
   */
  caching: 'Caching',
  /**
   * @description Option in the network conditions panel to disable the cache.
   */
  disableCache: 'Disable cache',
  /**
   * @description Header in Network conditions panel for the network throttling and emulation settings.
   */
  networkThrottling: 'Network',
  /**
   * @description Header in the network conditions panel for the user agent settings.
   */
  userAgent: 'User agent',
  /**
   * @description User agent setting in the network conditions panel to use the browser's default value.
   */
  selectAutomatically: 'Use browser default',
  /**
   * @description Title of a section in the Network conditions panel that includes
   * a set of checkboxes to override the content encodings supported by the browser.
   */
  acceptedEncoding: 'Accepted `Content-Encoding`s',
  /**
   * @description Status text displayed after updating user agent client hints.
   */
  clientHintsStatusText: 'User agent updated.',
  /**
   * @description The aria alert message when the Network conditions panel is shown.
   */
  networkConditionsPanelShown: 'Network conditions shown.',
} as const;
const str_ = i18n.i18n.registerUIStrings('panels/network/NetworkConfigView.ts', UIStrings);
const i18nString = i18n.i18n.getLocalizedString.bind(undefined, str_);

let networkConfigViewInstance: NetworkConfigView;

export class NetworkConfigView extends UI.Widget.VBox {
  constructor() {
    super({
      jslog: `${VisualLogging.panel('network-conditions').track({resize: true})}`,
      useShadowDom: true,
    });
    this.registerRequiredCSS(networkConfigViewStyles);

    this.contentElement.classList.add('network-config');

    this.createCacheSection();
    this.contentElement.createChild('div').classList.add('panel-section-separator');
    this.createNetworkThrottlingSection();
    this.contentElement.createChild('div').classList.add('panel-section-separator');
    this.createUserAgentSection();
    this.contentElement.createChild('div').classList.add('panel-section-separator');
    this.createAcceptedEncodingSection();
  }

  static instance(opts: {
    forceNew: boolean|null,
  } = {forceNew: null}): NetworkConfigView {
    const {forceNew} = opts;
    if (!networkConfigViewInstance || forceNew) {
      networkConfigViewInstance = new NetworkConfigView();
    }
    return networkConfigViewInstance;
  }

  static createUserAgentSelectAndInput(title: string): {
    select: HTMLSelectElement,
    input: HTMLInputElement,
    error: HTMLElement,
  } {
    const userAgentSetting = Common.Settings.Settings.instance().createSetting('custom-user-agent', '');
    const userAgentMetadataSetting =
        Common.Settings.Settings.instance().createSetting<Protocol.Emulation.UserAgentMetadata|null>(
            'custom-user-agent-metadata', null);
    const userAgentSelectElement = document.createElement('select');
    userAgentSelectElement.setAttribute(
        'jslog', `${VisualLogging.dropDown().track({change: true}).context(userAgentSetting.name)}`);
    UI.ARIAUtils.setLabel(userAgentSelectElement, title);

    const customOverride = {title: i18nString(UIStrings.custom), value: 'custom'};
    userAgentSelectElement.appendChild(UI.UIUtils.createOption(customOverride.title, customOverride.value, 'custom'));

    for (const userAgentDescriptor of userAgentGroups) {
      const groupElement = userAgentSelectElement.createChild('optgroup');
      groupElement.label = userAgentDescriptor.title;
      for (const userAgentVersion of userAgentDescriptor.values) {
        const userAgentValue =
            SDK.NetworkManager.MultitargetNetworkManager.patchUserAgentWithChromeVersion(userAgentVersion.value);
        groupElement.appendChild(UI.UIUtils.createOption(
            userAgentVersion.title, userAgentValue, Platform.StringUtilities.toKebabCase(userAgentVersion.title)));
      }
    }

    userAgentSelectElement.selectedIndex = 0;

    const otherUserAgentElement = UI.UIUtils.createInput('', 'text');
    otherUserAgentElement.setAttribute(
        'jslog', `${VisualLogging.textField().track({change: true}).context(userAgentSetting.name)}`);
    otherUserAgentElement.value = userAgentSetting.get();
    UI.Tooltip.Tooltip.install(otherUserAgentElement, userAgentSetting.get());
    otherUserAgentElement.placeholder = i18nString(UIStrings.enterACustomUserAgent);
    otherUserAgentElement.required = true;
    UI.ARIAUtils.setLabel(otherUserAgentElement, otherUserAgentElement.placeholder);

    const errorElement = document.createElement('div');
    errorElement.classList.add('network-config-input-validation-error');
    UI.ARIAUtils.markAsAlert(errorElement);
    if (!otherUserAgentElement.value) {
      errorElement.textContent = i18nString(UIStrings.customUserAgentFieldIsRequired);
    }

    settingChanged();
    userAgentSelectElement.addEventListener('change', userAgentSelected, false);
    otherUserAgentElement.addEventListener('input', applyOtherUserAgent, false);

    function userAgentSelected(): void {
      const value = userAgentSelectElement.options[userAgentSelectElement.selectedIndex].value;
      if (value !== customOverride.value) {
        userAgentSetting.set(value);
        otherUserAgentElement.value = value;
        UI.Tooltip.Tooltip.install(otherUserAgentElement, value);
        const userAgentMetadata = getUserAgentMetadata(value);
        userAgentMetadataSetting.set(userAgentMetadata);
        SDK.NetworkManager.MultitargetNetworkManager.instance().setCustomUserAgentOverride(value, userAgentMetadata);
      } else {
        userAgentMetadataSetting.set(null);
        otherUserAgentElement.select();
      }
      errorElement.textContent = '';
      const userAgentChangeEvent = new CustomEvent('user-agent-change', {detail: {value}});
      userAgentSelectElement.dispatchEvent(userAgentChangeEvent);
    }

    function settingChanged(): void {
      const value = userAgentSetting.get();
      const options = userAgentSelectElement.options;
      let selectionRestored = false;
      for (let i = 0; i < options.length; ++i) {
        if (options[i].value === value) {
          userAgentSelectElement.selectedIndex = i;
          selectionRestored = true;
          break;
        }
      }

      if (!selectionRestored) {
        userAgentSelectElement.selectedIndex = 0;
      }
    }

    function applyOtherUserAgent(): void {
      if (userAgentSetting.get() !== otherUserAgentElement.value) {
        if (!otherUserAgentElement.value) {
          errorElement.textContent = i18nString(UIStrings.customUserAgentFieldIsRequired);
        } else {
          errorElement.textContent = '';
        }
        userAgentSetting.set(otherUserAgentElement.value);
        UI.Tooltip.Tooltip.install(otherUserAgentElement, otherUserAgentElement.value);
        settingChanged();
      }
    }

    return {select: userAgentSelectElement, input: otherUserAgentElement, error: errorElement};
  }

  private createSection(title: string, className?: string): HTMLElement {
    const section = this.contentElement.createChild('section', 'network-config-group');
    if (className) {
      section.classList.add(className);
    }
    section.createChild('div', 'network-config-title').textContent = title;
    return section.createChild('div', 'network-config-fields');
  }

  private createCacheSection(): void {
    const section = this.createSection(i18nString(UIStrings.caching), 'network-config-disable-cache');
    section.appendChild(SettingsUI.SettingsUI.createSettingCheckbox(
        i18nString(UIStrings.disableCache), Common.Settings.Settings.instance().moduleSetting('cache-disabled')));
  }

  private createNetworkThrottlingSection(): void {
    const title = i18nString(UIStrings.networkThrottling);
    const section = this.createSection(title, 'network-config-throttling');
    MobileThrottling.NetworkThrottlingSelector.NetworkThrottlingSelect.createForGlobalConditions(section, title);
    const saveDataSelect =
        MobileThrottling.ThrottlingManager.throttlingManager().createSaveDataOverrideSelector('chrome-select').element;
    section.appendChild(saveDataSelect);
  }

  private createUserAgentSection(): void {
    const userAgentMetadataSetting =
        Common.Settings.Settings.instance().createSetting<Protocol.Emulation.UserAgentMetadata|null>(
            'custom-user-agent-metadata', null);
    const customUserAgentSetting = Common.Settings.Settings.instance().createSetting('custom-user-agent', '');

    const title = i18nString(UIStrings.userAgent);
    const section = this.createSection(title, 'network-config-ua');
    const autoCheckbox = UI.UIUtils.CheckboxLabel.create(
        i18nString(UIStrings.selectAutomatically), true, undefined, customUserAgentSetting.name);
    section.appendChild(autoCheckbox);

    customUserAgentSetting.addChangeListener(() => {
      if (autoCheckbox.checked) {
        return;
      }
      const customUA = customUserAgentSetting.get();
      const userAgentMetadata = getUserAgentMetadata(customUA);
      SDK.NetworkManager.MultitargetNetworkManager.instance().setCustomUserAgentOverride(customUA, userAgentMetadata);
    });
    const customUserAgentSelectBox = section.createChild('div', 'network-config-ua-custom');
    autoCheckbox.addEventListener('change', userAgentSelectBoxChanged);
    const customSelectAndInput = NetworkConfigView.createUserAgentSelectAndInput(title);
    customUserAgentSelectBox.appendChild(customSelectAndInput.select);
    customUserAgentSelectBox.appendChild(customSelectAndInput.input);
    customUserAgentSelectBox.appendChild(customSelectAndInput.error);

    const clientHints = new EmulationComponents.UserAgentClientHintsForm.UserAgentClientHintsForm();
    const userAgentMetaDataSetting = userAgentMetadataSetting.get();
    const initialUserAgentMetaData = getUserAgentMetadata(customSelectAndInput.select.value);
    clientHints.value = {
      showMobileCheckbox: true,
      showSubmitButton: true,
      metaData: userAgentMetaDataSetting || initialUserAgentMetaData || undefined,
    };
    customUserAgentSelectBox.appendChild(clientHints);

    customSelectAndInput.select.addEventListener('user-agent-change', (event: Event) => {
      const userStringValue = (event as CustomEvent).detail.value;
      const userAgentMetadata = userStringValue ? getUserAgentMetadata(userStringValue) : null;
      clientHints.value = {
        metaData: userAgentMetadata || undefined,
        showMobileCheckbox: true,
        showSubmitButton: true,
      };
      userAgentUpdateButtonStatusText.textContent = '';
    });

    clientHints.addEventListener('clienthintschange', () => {
      customSelectAndInput.select.value = 'custom';
      userAgentUpdateButtonStatusText.textContent = '';
    });

    clientHints.addEventListener('clienthintssubmit', event => {
      const metaData: Protocol.Emulation.UserAgentMetadata = (event as CustomEvent).detail.value;
      const customUA = customUserAgentSetting.get();
      userAgentMetadataSetting.set(metaData);
      SDK.NetworkManager.MultitargetNetworkManager.instance().setCustomUserAgentOverride(customUA, metaData);
      userAgentUpdateButtonStatusText.textContent = i18nString(UIStrings.clientHintsStatusText);
    });

    const userAgentUpdateButtonStatusText = section.createChild('span', 'status-text');
    userAgentUpdateButtonStatusText.textContent = '';

    userAgentSelectBoxChanged();

    function userAgentSelectBoxChanged(): void {
      const useCustomUA = !autoCheckbox.checked;
      customUserAgentSelectBox.classList.toggle('checked', useCustomUA);
      customSelectAndInput.select.disabled = !useCustomUA;
      customSelectAndInput.input.disabled = !useCustomUA;
      customSelectAndInput.error.hidden = !useCustomUA;
      clientHints.disabled = !useCustomUA;
      const customUA = useCustomUA ? customUserAgentSetting.get() : '';
      const userAgentMetadata = useCustomUA ? getUserAgentMetadata(customUA) : null;
      SDK.NetworkManager.MultitargetNetworkManager.instance().setCustomUserAgentOverride(customUA, userAgentMetadata);
    }
  }

  private createAcceptedEncodingSection(): void {
    const useCustomAcceptedEncodingSetting =
        Common.Settings.Settings.instance().createSetting('use-custom-accepted-encodings', false);
    const customAcceptedEncodingSetting = Common.Settings.Settings.instance().createSetting(
        'custom-accepted-encodings',
        `${Protocol.Network.ContentEncoding.Gzip},${Protocol.Network.ContentEncoding.Br},${
            Protocol.Network.ContentEncoding.Deflate}`);

    const title = i18nString(UIStrings.acceptedEncoding);
    const section = this.createSection(title, 'network-config-accepted-encoding');
    const autoCheckbox = UI.UIUtils.CheckboxLabel.create(
        i18nString(UIStrings.selectAutomatically), true, undefined, useCustomAcceptedEncodingSetting.name);
    section.appendChild(autoCheckbox);

    function onSettingChange(): void {
      if (!useCustomAcceptedEncodingSetting.get()) {
        SDK.NetworkManager.MultitargetNetworkManager.instance().clearCustomAcceptedEncodingsOverride();
      } else {
        SDK.NetworkManager.MultitargetNetworkManager.instance().setCustomAcceptedEncodingsOverride(
            customAcceptedEncodingSetting.get() === '' ?
                [] :
                customAcceptedEncodingSetting.get().split(',') as Protocol.Network.ContentEncoding[]);
      }
    }

    customAcceptedEncodingSetting.addChangeListener(onSettingChange);
    useCustomAcceptedEncodingSetting.addChangeListener(onSettingChange);

    const encodingsSection = section.createChild('div', 'network-config-accepted-encoding-custom');
    encodingsSection.setAttribute('jslog', `${VisualLogging.section().context(customAcceptedEncodingSetting.name)}`);
    autoCheckbox.checked = !useCustomAcceptedEncodingSetting.get();
    autoCheckbox.addEventListener('change', acceptedEncodingsChanged);
    const checkboxes = new Map<Protocol.Network.ContentEncoding, UI.UIUtils.CheckboxLabel>();
    const contentEncodings: Protocol.EnumerableEnum<typeof Protocol.Network.ContentEncoding> = {
      Deflate: Protocol.Network.ContentEncoding.Deflate,
      Gzip: Protocol.Network.ContentEncoding.Gzip,
      Br: Protocol.Network.ContentEncoding.Br,
      Zstd: Protocol.Network.ContentEncoding.Zstd,
    };
    for (const encoding of Object.values(contentEncodings)) {
      const checkbox = UI.UIUtils.CheckboxLabel.createWithStringLiteral(encoding, true, encoding);
      encodingsSection.appendChild(checkbox);
      checkboxes.set(encoding, checkbox);
    }
    for (const [encoding, checkbox] of checkboxes) {
      checkbox.checked = customAcceptedEncodingSetting.get().includes(encoding);
      checkbox.addEventListener('change', acceptedEncodingsChanged);
    }

    acceptedEncodingsChanged();

    function acceptedEncodingsChanged(): void {
      useCustomAcceptedEncodingSetting.set(!autoCheckbox.checked);
      const encodings = [];
      for (const [encoding, checkbox] of checkboxes) {
        checkbox.disabled = autoCheckbox.checked;
        if (checkbox.checked) {
          encodings.push(encoding);
        }
      }
      customAcceptedEncodingSetting.set(encodings.join(','));
    }
  }
  override wasShown(): void {
    super.wasShown();
    UI.ARIAUtils.LiveAnnouncer.alert(i18nString(UIStrings.networkConditionsPanelShown));
  }
}

function getUserAgentMetadata(userAgent: string): Protocol.Emulation.UserAgentMetadata|null {
  for (const userAgentDescriptor of userAgentGroups) {
    for (const userAgentVersion of userAgentDescriptor.values) {
      if (userAgent ===
          SDK.NetworkManager.MultitargetNetworkManager.patchUserAgentWithChromeVersion(userAgentVersion.value)) {
        if (!userAgentVersion.metadata) {
          return null;
        }
        SDK.NetworkManager.MultitargetNetworkManager.patchUserAgentMetadataWithChromeVersion(userAgentVersion.metadata);
        return userAgentVersion.metadata;
      }
    }
  }
  return null;
}

interface UserAgentGroup {
  title: string;
  values: Array<{
    title: string,
    value: string,
    metadata: Protocol.Emulation.UserAgentMetadata|null,
  }>;
}

export const userAgentGroups: UserAgentGroup[] = [
  {
    title: 'Android',
    values: [
      {
        title: 'Android (4.0.2) Browser \u2014 Galaxy Nexus',
        value:
            'Mozilla/5.0 (Linux; U; Android 4.0.2; en-us; Galaxy Nexus Build/ICL53F) AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 Mobile Safari/534.30',
        metadata: {
          brands: [
            {brand: 'Not A;Brand', version: '99'},
            {brand: 'Chromium', version: '%s'},
            {brand: 'Google Chrome', version: '%s'},
          ],
          fullVersion: '%s',
          platform: 'Android',
          platformVersion: '4.0.2',
          architecture: '',
          model: 'Galaxy Nexus',
          mobile: true,
        },
      },
      {
        title: 'Android (2.3) Browser \u2014 Nexus S',
        value:
            'Mozilla/5.0 (Linux; U; Android 2.3.6; en-us; Nexus S Build/GRK39F) AppleWebKit/533.1 (KHTML, like Gecko) Version/4.0 Mobile Safari/533.1',
        metadata: {
          brands: [
            {brand: 'Not A;Brand', version: '99'},
            {brand: 'Chromium', version: '%s'},
            {brand: 'Google Chrome', version: '%s'},
          ],
          fullVersion: '%s',
          platform: 'Android',
          platformVersion: '2.3.6',
          architecture: '',
          model: 'Nexus S',
          mobile: true,
        },
      },
    ],
  },
  {
    title: 'BlackBerry',
    values: [
      {
        title: 'BlackBerry \u2014 BB10',
        value:
            'Mozilla/5.0 (BB10; Touch) AppleWebKit/537.1+ (KHTML, like Gecko) Version/10.0.0.1337 Mobile Safari/537.1+',
        metadata: null,
      },
      {
        title: 'BlackBerry \u2014 PlayBook 2.1',
        value:
            'Mozilla/5.0 (PlayBook; U; RIM Tablet OS 2.1.0; en-US) AppleWebKit/536.2+ (KHTML, like Gecko) Version/7.2.1.0 Safari/536.2+',
        metadata: null,
      },
      {
        title: 'BlackBerry \u2014 9900',
        value:
            'Mozilla/5.0 (BlackBerry; U; BlackBerry 9900; en-US) AppleWebKit/534.11+ (KHTML, like Gecko) Version/7.0.0.187 Mobile Safari/534.11+',
        metadata: null,
      },
    ],
  },
  {
    title: 'Chrome',
    values: [
      {
        title: 'Chrome \u2014 Android Mobile',
        value:
            'Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/%s Mobile Safari/537.36',
        metadata: {
          brands: [
            {brand: 'Not A;Brand', version: '99'},
            {brand: 'Chromium', version: '%s'},
            {brand: 'Google Chrome', version: '%s'},
          ],
          fullVersion: '%s',
          platform: 'Android',
          platformVersion: '6.0',
          architecture: '',
          model: 'Nexus 5',
          mobile: true,
        },
      },
      {
        title: 'Chrome \u2014 Android Mobile (high-end)',
        value:
            'Mozilla/5.0 (Linux; Android 10; Pixel 4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/%s Mobile Safari/537.36',
        metadata: {
          brands: [
            {brand: 'Not A;Brand', version: '99'},
            {brand: 'Chromium', version: '%s'},
            {brand: 'Google Chrome', version: '%s'},
          ],
          fullVersion: '%s',
          platform: 'Android',
          platformVersion: '10',
          architecture: '',
          model: 'Pixel 4',
          mobile: true,
        },
      },
      {
        title: 'Chrome \u2014 Android Tablet',
        value:
            'Mozilla/5.0 (Linux; Android 4.3; Nexus 7 Build/JSS15Q) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/%s Safari/537.36',
        metadata: {
          brands: [
            {brand: 'Not A;Brand', version: '99'},
            {brand: 'Chromium', version: '%s'},
            {brand: 'Google Chrome', version: '%s'},
          ],
          fullVersion: '%s',
          platform: 'Android',
          platformVersion: '4.3',
          architecture: '',
          model: 'Nexus 7',
          mobile: true,
        },
      },
      {
        title: 'Chrome \u2014 iPhone',
        value:
            'Mozilla/5.0 (iPhone; CPU iPhone OS 13_2 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) CriOS/%s Mobile/15E148 Safari/604.1',
        metadata: null,
      },
      {
        title: 'Chrome \u2014 iPad',
        value:
            'Mozilla/5.0 (iPad; CPU OS 13_2 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) CriOS/%s Mobile/15E148 Safari/604.1',
        metadata: null,
      },
      {
        title: 'Chrome \u2014 Chrome OS',
        value:
            'Mozilla/5.0 (X11; CrOS x86_64 10066.0.0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/%s Safari/537.36',
        metadata: {
          brands: [
            {brand: 'Not A;Brand', version: '99'},
            {brand: 'Chromium', version: '%s'},
            {brand: 'Google Chrome', version: '%s'},
          ],
          fullVersion: '%s',
          platform: 'Chrome OS',
          platformVersion: '10066.0.0',
          architecture: 'x86',
          model: '',
          mobile: false,
        },
      },
      {
        title: 'Chrome \u2014 Mac',
        value:
            'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/%s Safari/537.36',
        metadata: {
          brands: [
            {brand: 'Not A;Brand', version: '99'},
            {brand: 'Chromium', version: '%s'},
            {brand: 'Google Chrome', version: '%s'},
          ],
          fullVersion: '%s',
          platform: 'macOS',
          platformVersion: '10_14_6',
          architecture: 'x86',
          model: '',
          mobile: false,
        },
      },
      {
        title: 'Chrome \u2014 Windows',
        value: 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/%s Safari/537.36',
        metadata: {
          brands: [
            {brand: 'Not A;Brand', version: '99'},
            {brand: 'Chromium', version: '%s'},
            {brand: 'Google Chrome', version: '%s'},
          ],
          fullVersion: '%s',
          platform: 'Windows',
          platformVersion: '10.0',
          architecture: 'x86',
          model: '',
          mobile: false,
        },
      },
    ],
  },
  {
    title: 'Firefox',
    values: [
      {
        title: 'Firefox \u2014 Android Mobile',
        value: 'Mozilla/5.0 (Android 4.4; Mobile; rv:70.0) Gecko/70.0 Firefox/70.0',
        metadata: null,
      },
      {
        title: 'Firefox \u2014 Android Tablet',
        value: 'Mozilla/5.0 (Android 4.4; Tablet; rv:70.0) Gecko/70.0 Firefox/70.0',
        metadata: null,
      },
      {
        title: 'Firefox \u2014 iPhone',
        value:
            'Mozilla/5.0 (iPhone; CPU iPhone OS 8_3 like Mac OS X) AppleWebKit/600.1.4 (KHTML, like Gecko) FxiOS/1.0 Mobile/12F69 Safari/600.1.4',
        metadata: null,
      },
      {
        title: 'Firefox \u2014 iPad',
        value:
            'Mozilla/5.0 (iPad; CPU iPhone OS 8_3 like Mac OS X) AppleWebKit/600.1.4 (KHTML, like Gecko) FxiOS/1.0 Mobile/12F69 Safari/600.1.4',
        metadata: null,
      },
      {
        title: 'Firefox \u2014 Mac',
        value: 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10.14; rv:70.0) Gecko/20100101 Firefox/70.0',
        metadata: null,
      },
      {
        title: 'Firefox \u2014 Windows',
        value: 'Mozilla/5.0 (Windows NT 10.0; WOW64; rv:70.0) Gecko/20100101 Firefox/70.0',
        metadata: null,
      },
    ],
  },
  {
    title: 'Googlebot',
    values: [
      {
        title: 'Googlebot',
        value: 'Mozilla/5.0 (compatible; Googlebot/2.1; +http://www.google.com/bot.html)',
        metadata: null,
      },
      {
        title: 'Googlebot Desktop',
        value:
            'Mozilla/5.0 AppleWebKit/537.36 (KHTML, like Gecko; compatible; Googlebot/2.1; +http://www.google.com/bot.html) Chrome/%s Safari/537.36',
        metadata: null,
      },
      {
        title: 'Googlebot Smartphone',
        value:
            'Mozilla/5.0 (Linux; Android 6.0.1; Nexus 5X Build/MMB29P) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/%s Mobile Safari/537.36 (compatible; Googlebot/2.1; +http://www.google.com/bot.html)',
        metadata: null,
      },
    ],
  },
  {
    title: 'Internet Explorer',
    values: [
      {
        title: 'Internet Explorer 11',
        value: 'Mozilla/5.0 (Windows NT 10.0; WOW64; Trident/7.0; rv:11.0) like Gecko',
        metadata: null,
      },
      {
        title: 'Internet Explorer 10',
        value: 'Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 6.1; WOW64; Trident/6.0)',
        metadata: null,
      },
      {
        title: 'Internet Explorer 9',
        value: 'Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Trident/5.0)',
        metadata: null,
      },
      {
        title: 'Internet Explorer 8',
        value: 'Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.0; Trident/4.0)',
        metadata: null,
      },
      {title: 'Internet Explorer 7', value: 'Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.0)', metadata: null},
    ],
  },
  {
    title: 'Microsoft Edge',
    values: [
      {
        title: 'Microsoft Edge (Chromium) \u2014 Windows',
        value:
            'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/%s Safari/537.36 Edg/%s',
        metadata: {
          brands: [
            {brand: 'Not A;Brand', version: '99'},
            {brand: 'Chromium', version: '%s'},
            {brand: 'Microsoft Edge', version: '%s'},
          ],
          fullVersion: '%s',
          platform: 'Windows',
          platformVersion: '10.0',
          architecture: 'x86',
          model: '',
          mobile: false,
        },
      },
      {
        title: 'Microsoft Edge (Chromium) \u2014 Mac',
        value:
            'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/605.1.15 (KHTML, like Gecko) Chrome/%s Safari/604.1 Edg/%s',
        metadata: {
          brands: [
            {brand: 'Not A;Brand', version: '99'},
            {brand: 'Chromium', version: '%s'},
            {brand: 'Microsoft Edge', version: '%s'},
          ],
          fullVersion: '%s',
          platform: 'macOS',
          platformVersion: '10_14_6',
          architecture: 'x86',
          model: '',
          mobile: false,
        },
      },
      {
        title: 'Microsoft Edge \u2014 iPhone',
        value:
            'Mozilla/5.0 (iPhone; CPU iPhone OS 12_3_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/12.1.1 EdgiOS/44.5.0.10 Mobile/15E148 Safari/604.1',
        metadata: null,
      },
      {
        title: 'Microsoft Edge \u2014 iPad',
        value:
            'Mozilla/5.0 (iPad; CPU OS 12_3_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/12.0 EdgiOS/44.5.2 Mobile/15E148 Safari/605.1.15',
        metadata: null,
      },
      {
        title: 'Microsoft Edge \u2014 Android Mobile',
        value:
            'Mozilla/5.0 (Linux; Android 8.1.0; Pixel Build/OPM4.171019.021.D1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/%s Mobile Safari/537.36 EdgA/42.0.0.2057',
        metadata: {
          brands: [
            {brand: 'Not A;Brand', version: '99'},
            {brand: 'Chromium', version: '%s'},
            {brand: 'Microsoft Edge', version: '%s'},
          ],
          fullVersion: '%s',
          platform: 'Android',
          platformVersion: '8.1.0',
          architecture: '',
          model: 'Pixel',
          mobile: true,
        },
      },
      {
        title: 'Microsoft Edge \u2014 Android Tablet',
        value:
            'Mozilla/5.0 (Linux; Android 6.0.1; Nexus 7 Build/MOB30X) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/%s Safari/537.36 EdgA/42.0.0.2057',
        metadata: {
          brands: [
            {brand: 'Not A;Brand', version: '99'},
            {brand: 'Chromium', version: '%s'},
            {brand: 'Microsoft Edge', version: '%s'},
          ],
          fullVersion: '%s',
          platform: 'Android',
          platformVersion: '6.0.1',
          architecture: '',
          model: 'Nexus 7',
          mobile: true,
        },
      },
      {
        title: 'Microsoft Edge (EdgeHTML) \u2014 Windows',
        value:
            'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/%s Safari/537.36 Edge/18.19042',
        metadata: null,
      },
      {
        title: 'Microsoft Edge (EdgeHTML) \u2014 XBox',
        value:
            'Mozilla/5.0 (Windows NT 10.0; Win64; x64; Xbox; Xbox One) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/%s Safari/537.36 Edge/18.19041',
        metadata: null,
      },
    ],
  },
  {
    title: 'Opera',
    values: [
      {
        title: 'Opera \u2014 Mac',
        value:
            'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/%s Safari/537.36 OPR/65.0.3467.48',
        metadata: null,
      },
      {
        title: 'Opera \u2014 Windows',
        value:
            'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/%s Safari/537.36 OPR/65.0.3467.48',
        metadata: null,
      },
      {
        title: 'Opera (Presto) \u2014 Mac',
        value: 'Opera/9.80 (Macintosh; Intel Mac OS X 10.9.1) Presto/2.12.388 Version/12.16',
        metadata: null,
      },
      {
        title: 'Opera (Presto) \u2014 Windows',
        value: 'Opera/9.80 (Windows NT 6.1) Presto/2.12.388 Version/12.16',
        metadata: null,
      },
      {
        title: 'Opera Mobile \u2014 Android Mobile',
        value: 'Opera/12.02 (Android 4.1; Linux; Opera Mobi/ADR-1111101157; U; en-US) Presto/2.9.201 Version/12.02',
        metadata: null,
      },
      {
        title: 'Opera Mini \u2014 iOS',
        value: 'Opera/9.80 (iPhone; Opera Mini/8.0.0/34.2336; U; en) Presto/2.8.119 Version/11.10',
        metadata: null,
      },
    ],
  },
  {
    title: 'Safari',
    values: [
      {
        title: 'Safari \u2014 iPad iOS 13.2',
        value:
            'Mozilla/5.0 (iPad; CPU iPhone OS 13_2_3 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.0.3 Mobile/15E148 Safari/604.1',
        metadata: null,
      },
      {
        title: 'Safari \u2014 iPhone iOS 13.2',
        value:
            'Mozilla/5.0 (iPhone; CPU iPhone OS 13_2_3 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.0.3 Mobile/15E148 Safari/604.1',
        metadata: null,
      },
      {
        title: 'Safari \u2014 Mac',
        value:
            'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.0.3 Safari/605.1.15',
        metadata: null,
      },
    ],
  },
  {
    title: 'UC Browser',
    values: [
      {
        title: 'UC Browser \u2014 Android Mobile',
        value:
            'Mozilla/5.0 (Linux; U; Android 8.1.0; en-US; Nexus 6P Build/OPM7.181205.001) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/%s UCBrowser/12.11.1.1197 Mobile Safari/537.36',
        metadata: null,
      },
      {
        title: 'UC Browser \u2014 iOS',
        value:
            'Mozilla/5.0 (iPhone; CPU iPhone OS 12_1 like Mac OS X; zh-CN) AppleWebKit/537.51.1 (KHTML, like Gecko) Mobile/16B92 UCBrowser/12.1.7.1109 Mobile AliApp(TUnionSDK/0.1.20.3)',
        metadata: null,
      },
      {
        title: 'UC Browser \u2014 Windows Phone',
        value:
            'Mozilla/5.0 (compatible; MSIE 10.0; Windows Phone 8.0; Trident/6.0; IEMobile/10.0; ARM; Touch; NOKIA; Lumia 920) UCBrowser/10.1.0.563 Mobile',
        metadata: null,
      },
    ],
  },
];
