createAnalyticsBundle = ({
  countlyUrl = 'https://countly.ipfs.tech',
  appVersion = process.env.REACT_APP_VERSION,
  // @ts-ignore - declared but never used
  appGitRevision = process.env.REACT_APP_GIT_REV,
  debug = false
}) => {
  return {
    name: 'analytics',

    persistActions: [
      ACTIONS.ANALYTICS_ENABLED,
      ACTIONS.ANALYTICS_DISABLED,
      ACTIONS.ANALYTICS_DISABLED,
      ACTIONS.ANALYTICS_ADD_CONSENT,
      ACTIONS.ANALYTICS_REMOVE_CONSENT,
      ACTIONS.SET_SHOW_ANALYTICS_BANNER
    ],

    /**
     * @param {Store} store
     */
    init: async (store) => {
      // LogRocket.init('sfqf1k/ipfs-webui')
      // test code sets a mock Counly instance on the global.
      if (!root.Countly) {
        root.Countly = {}
        root.Countly.q = []
        // @ts-ignore
        await import('countly-sdk-web')
      }
      const Countly = root.Countly

      Countly.require_consent = true
      Countly.url = countlyUrl
      const countlyAppKeyPromise = pickAppKey()
      countlyAppKeyPromise.then((appKey) => {
        Countly.app_key = appKey
      })
      Countly.app_version = appVersion
      Countly.debug = debug

      if (store.selectIsIpfsDesktop()) {
        Countly.app_version = store.selectDesktopVersion()
        Countly.q.push(['change_id', store.selectDesktopCountlyDeviceId(), true])
      }

      // Configure what to track. Nothing is sent without user consent.
      Countly.q.push(['track_sessions'])
      Countly.q.push(['track_errors'])

      // Don't track clicks or links as it can include full url.
      // Countly.q.push(['track_clicks'])
      // Countly.q.push(['track_links'])
      if (store.selectAnalyticsEnabled()) {
        const consent = store.selectAnalyticsConsent()
        addConsent(consent, store)
      } else if (!store.selectAnalyticsOptedOut()) {
        if (store.selectAnalyticsOptedOutPriorToDefaultOptIn()) {
          store.doToggleShowAnalyticsBanner(true)
        }
        // add consent/opt in by default
        store.doEnableAnalytics()
      }

      store.subscribeToSelectors(['selectRouteInfo'], ({ routeInfo }) => {
        // skip routes with no hash, as we'll be immediately redirected to `/#`
        if (!root.location || !root.location.hash) return
        /*
        By tracking the pattern rather than the window.location, we limit the info
        we collect to just the app sections that are viewed, and avoid recording
        specific CIDs or local repo paths that would contain personal information.
        */
        root.Countly.q.push(['track_pageview', routeInfo.pattern])
      })

      // Fix for storybook error 'Countly.init is not a function'
      if (typeof Countly.init === 'function') {
        await countlyAppKeyPromise
        Countly.init()
      }
    },

    // Listen to redux actions
    getMiddleware: () => () => {
      /**
       * @param {function(Message):void} next
       * @returns {function(Message):void}
       */
      const middleware = next => action => {
        const payload = parseTask(action)
        if (payload) {
          const { id, duration, error } = payload
          addEventWrapped({ id, duration })

          // Record errors. Only from explicitly selected actions.
          if (error) {
            root.Countly.q.push(['add_log', action.type])
            root.Countly.q.push(['log_error', error])
            // LogRocket.error(error)
            // logger.error('Error in action', action.type, error)
          }
        }

        return next(action)
      }

      return middleware
    },

    /**
     * @param {Model|void} state
     * @param {Message} action
     * @returns {Model}
     */
    reducer: (state, action) => {
      state = state || {
        lastEnabledAt: 0,
        lastDisabledAt: 0,
        showAnalyticsComponents: !DISABLE_ALL_ANALYTICS, // hide related UI  for now, see https://github.com/ipfs/ipfs-webui/issues/2198
        showAnalyticsBanner: false,
        optedOut: DISABLE_ALL_ANALYTICS, // disable analytics by default for now, see https://github.com/ipfs/ipfs-webui/issues/2198
        consent: []
      }

      switch (action.type) {
        case ACTIONS.ANALYTICS_ENABLED:
          return { ...state, lastEnabledAt: Date.now(), consent: action.payload.consent, optedOut: false }
        case ACTIONS.ANALYTICS_DISABLED:
          return { ...state, lastDisabledAt: Date.now(), consent: action.payload.consent, optedOut: true, showAnalyticsBanner: false }
        case ACTIONS.ANALYTICS_ADD_CONSENT: {
          const consent = state.consent.filter(item => item !== action.payload.name).concat(action.payload.name)
          return { ...state, lastEnabledAt: Date.now(), consent, optedOut: false }
        }
        case ACTIONS.ANALYTICS_REMOVE_CONSENT: {
          const consent = state.consent.filter(item => item !== action.payload.name)
          const lastDisabledAt = (consent.length === 0) ? Date.now() : state.lastDisabledAt
          const didOptOutCompletely = consent.length === 0
          return { ...state, lastDisabledAt, consent, optedOut: didOptOutCompletely, showAnalyticsBanner: false }
        }
        case ACTIONS.SET_SHOW_ANALYTICS_BANNER: {
          const shouldShowAnalyticsBanner = action.payload?.shouldShow || false
          return { ...state, showAnalyticsBanner: shouldShowAnalyticsBanner }
        }
        default: {
          // deal with missing consent state from 2.4.0 release.
          if (!state.consent) {
            if (state.lastEnabledAt > state.lastDisabledAt) {
              return { ...state, consent: consentGroups.safe }
            } else {
              return { ...state, consent: [] }
            }
          }

          return state
        }
      }
    },

    ...selectors,

    ...actions
  }
}
@param {boolean?} shouldShow
@returns {function(Context):void}