const UB = require('@unitybase/ub')
/* global ubs_settings */

ubs_settings.on('update:after', flushCacheAfter.bind(ubs_settings, 'selectAfterUpdate'))
ubs_settings.on('insert:after', flushCacheAfter.bind(ubs_settings, 'selectAfterInsert'))
ubs_settings.on('delete:after', flushCacheAfter.bind(ubs_settings, 'selectBeforeDelete'))

/**
 * Flush a cache for key after insert/update/delete
 * @private
 * @param {string} storeDataName
 * @param {ubMethodParams} ctx
 */
function flushCacheAfter (storeDataName, ctx) {
  let settingKey
  const oldCurrentDataName = ctx.dataStore.currentDataName
  ctx.dataStore.currentDataName = storeDataName
  try {
    if (!ctx.dataStore.eof) { // dataLoader or migration can skip select afterXXX
      settingKey = ctx.dataStore.get('settingKey')
    }
  } finally {
    ctx.dataStore.currentDataName = oldCurrentDataName
  }

  if (!settingKey) {
    settingKey = UB.Repository('ubs_settings')
      .attrs(['settingKey'])
      .where('ID', '=', ctx.mParams.execParams.ID)
      .selectScalar()
  }

  if (settingKey) {
    const supportLanguages = ubs_settings.entity.connectionConfig.supportLang
    for (const lang of supportLanguages) {
      UB.App.globalCachePut(computeCacheKey(settingKey, lang), null)
    }
    console.debug('ubs_setting: flush cache for', settingKey)
  }
}

/**
 * Load a single configuration value
 *
 * @method loadKey
 * @memberOf ubs_settings_ns.prototype
 * @memberOfModule @unitybase/ubs
 * @param {string} settingKey
 * @param {string|boolean|number|null} [defaultValue=null] The value returned if setting key not found
 * @returns {null|number|string|boolean}
 */
ubs_settings.loadKey = function loadKey (settingKey, defaultValue = null) {
  const cached = UB.App.globalCacheGet(computeCacheKey(settingKey))
  let kType, kVal
  if (cached) {
    const parsed = JSON.parse(cached)
    kType = parsed.kType
    kVal = parsed.kVal
    console.debug('ubs_setting: cached value is used for', settingKey)
  } else {
    const store = UB.Repository('ubs_settings')
      .attrs(['ID', 'type', 'settingKey', 'settingValue'])
      .where('[settingKey]', '=', settingKey)
      .select()
    if (store.eof) {
      console.warn('There is no setting with key:' + settingKey)
      kType = (typeof defaultValue === 'boolean')
        ? 'BOOLEAN'
        : (typeof defaultValue === 'number')
            ? 'NUMBER'
            : 'STRING'
      kVal = kType === 'STRING' ? defaultValue : JSON.stringify(defaultValue)
    } else {
      kType = store.get('type')
      kVal = store.get('settingValue')
      store.next()
      if (!store.eof) {
        console.error('There is more than one settings with key: ' + settingKey)
      }
    }
    UB.App.globalCachePut(computeCacheKey(settingKey), JSON.stringify({ kType, kVal }))
  }

  const res = convert(kType, kVal, settingKey)
  return res
}

/**
 * Load a configuration object for number of keys
 *
 * @deprecated Consider to use `ubs_settings.loadKey` - it's cached in globalCache
 * @method loadKeys
 * @memberOf ubs_settings_ns.prototype
 * @memberOfModule @unitybase/ubs
 * @param {Array<string>|string} settingKeys   A mask or array of keys.
 * @returns {object}
 */
ubs_settings.loadKeys = function loadKeys (settingKeys) {
  const store = UB.Repository('ubs_settings')
    .attrs(['ID', 'type', 'settingKey', 'settingValue'])
    .where('[settingKey]', typeof settingKeys === 'string' ? 'startsWith' : 'in', settingKeys)
    .select()

  const configObject = {}
  for (; !store.eof; store.next()) {
    const settingKey = store.get('settingKey')
    configObject[settingKey] = convert(store.get('type'), store.get('settingValue'), settingKey)
  }

  return configObject
}

let _settingsStore
/**
 * @private
 * @returns {TubDataStore}
 */
function getSettingsStore () {
  if (!_settingsStore) {
    _settingsStore = UB.DataStore('ubs_settings')
  }
  return _settingsStore
}
/**
 * Create a new Key or set value for existing key
 *
 * @method addOrUpdateKey
 * @memberOf ubs_settings_ns.prototype
 * @memberOfModule @unitybase/ubs
 * @param {ubs_settings_ns} keyData
 */
ubs_settings.addOrUpdateKey = function (keyData) {
  const existedKeyData = UB.Repository('ubs_settings')
    .attrs(['ID', 'mi_modifyDate'])
    .where('[settingKey]', '=', keyData.settingKey)
    .selectSingle()
  if (existedKeyData) {
    getSettingsStore().run('update', {
      execParams: {
        ID: existedKeyData.ID,
        settingValue: keyData.settingValue,
        mi_modifyDate: existedKeyData.mi_modifyDate
      }
    })
  } else {
    getSettingsStore().run('insert', { execParams: keyData })
  }
}

/**
 * Alias for a loadKey
 *
 * @deprecated Use ubs_setting.loadKey instead
 * @method getSettingValue
 * @memberOf ubs_settings_ns.prototype
 * @memberOfModule @unitybase/ubs
 * @param {string} settingKey
 * @param {string|boolean|number|null} [defaultValue=null] The value returned if setting key not found
 * @returns {string|boolean|number|null}
 */
ubs_settings.getSettingValue = ubs_settings.loadKey

/**
 * Compute cache key for setting key (
 * @param {string} settingKey
 * @param {string} lang
 */
function computeCacheKey (settingKey, lang) {
  return 'ubs_setting:' + (lang || Session.userLang) + ':' + settingKey
}
function convertBoolean (value, settingKey) {
  if ((value === 'true') || (value === '1')) return true
  if ((value === 'false') || (value === '0')) return false
  console.error('Unknown value: ', value, 'for boolean key ', settingKey)
  return value
}

function convert (type, value, settingKey) {
  switch (type.toUpperCase ? type.toUpperCase() : type) {
    case 'BOOLEAN':
      return convertBoolean(value, settingKey)
    case 'STRING':
      return value
    case 'INT':
    case 'NUMBER':
      return parseInt(value, 10)
    default :
      console.error('Unknown type:', type, 'for key:', settingKey)
      return value
  }
}