/**
 * Modal dialogs (message boxes) for showing errors, information and confirmation
 *
 * @example
 // valid usage
 const uDialogs = require('@unitybase/adminui-vue').uDialogs
 // WRONG usage
 // const uDialogs = require('@unitybase/adminui-vue/utils/uDialogs')
  const answer = await uDialogs.dialogDeleteRecord(entityName, item)
 * @module uDialogs
 * @memberOf module:@unitybase/adminui-vue
 */

const Vue = require('vue')
const UB = require('@unitybase/ub-pub')
const UDialog = require('../components/UDialog.vue').default
const { Notification } = require('element-ui')
const UDialogConstructor = Vue.extend(UDialog)

const USER_MESSAGE_RE = /^<<<([\S|\s]+)>>>$/

/**
 * Show modal dialog with 3 optional button and text/html content
 *
 * @example
const resp = await $App.uDialogs.dialog({
  title: 'scan',
  msg: 'noPaperInScanner',
  type: 'warning',
  buttons: {yes: 'tryAgain', cancel: 'stopScan'}
})
// here `resp` is either 'yes' or 'cancel'
 * @param {object} options
 * @param {string} options.title Dialog title (will be translated using UB.i18n)
 * @param {string} options.msg Dialog message. Text of HTML string. Will be translated using UB.i18n. Support "magic" hyperlink - see {@link module:magicLinks magicLinks}
 * @param {object} options.buttons
 * @param {string} [options.buttons.yes] "yes" action text (will be translated). If not passed or empty "yes" button not displayed
 * @param {string} [options.buttons.no] "no" action text (will be translated). If not passed or empty "no" button not displayed
 * @param {string} [options.buttons.cancel] "cancel" action text (will be translated). If not passed or empty "cancel" button not displayed
 * @param {string} options.type Type of icon. Can be any available el-icon-[type]. For example `error` will show `el-icon-error`. Recommended types are: `error`, `info`, `question`
 * @param {boolean} [options.isDevInfo = false] If true adds "Copy to clipboard" button
 * @returns {Promise<string>} Promise resolved to one of 'yes', 'no' 'cancel' depending on button clicked.
 *   If dialog is closed using Esc key or by pressing window "close" button result is `cancel`
 */
function dialog (options) {
  let vm
  return new Promise(resolve => {
    const instance = new UDialogConstructor({ data: options, resolver: resolve })
    vm = instance.$mount()
    document.body.appendChild(vm.$el)
    vm.$refs.modal.open()
  }).then(res => {
    const el = vm.$el
    vm.$destroy()
    vm = null
    document.body.removeChild(el)
    return res
  })
}

/**
 * Confirmation dialog. Title & message are translated using {@link module:@unitybase/ub-pub~i18n UB.i18n}
 *
 * @example
const choice = await $App.dialogYesNo('makeChangesSuccessfulTitle', 'makeChangesSuccessfulBody')
if (choice){
  // do something on Yes answer
} else {
  // do something on No answer
 }
 *
 * @param {string} title
 * @param {string} msg
 * @returns {Promise<boolean>} user choice true or false
 */
function dialogYesNo (title, msg) {
  return dialog({
    title,
    msg,
    type: 'question',
    buttons: {
      yes: 'Yes',
      cancel: 'No'
    }
  }).then(r => r === 'yes')
}

/**
 * Show information dialog. Title & message are translated using {@link module:@unitybase/ub-pub~i18n UB.i18n}
 * Injected into Vue prototype as `$dialogInfo`
 *
 * @param {string} msg
 * @param {string} [title='info'] title
 * @returns {Promise<boolean>} resolved to true then user click OK in other case - false
 */
function dialogInfo (msg, title = 'info') {
  return dialog({
    title,
    msg,
    buttons: {
      yes: 'ok'
    }
  }).then(r => r === 'yes')
}

/**
 * Show error dialog. Title & message are translated using {@link module:@unitybase/ub-pub~i18n UB.i18n}
 *
 * @param {string} msg
 * @param {string} [title='error'] title
 * @param {boolean} [isDevInfo=false] If true adds "Copy to clipboard" button
 * @returns {Promise<boolean>} resolved to true when user press OK button, in other case (Esc) - false
 */
function dialogError (msg, title = 'error', isDevInfo = false) {
  msg = msg.replace(USER_MESSAGE_RE, '$1')
  return dialog({
    title,
    msg,
    type: 'error',
    isDevInfo,
    buttons: {
      yes: 'ok'
    }
  })
}

/**
 * Vue based error reported. To be used by ub-pub.setErrorReporter
 *
 * @param {String} errMsg
 * @param errCode
 * @param entityCode
 * @param {string} detail
 */
function errorReporter ({ errMsg, errCode, entityCode, detail }) {
  var msgToDisplay = USER_MESSAGE_RE.test(errMsg)
    ? UB.i18n(errMsg.replace(USER_MESSAGE_RE, '$1'))
    : errMsg

  function buildSupportMailURL () {
    const baseUrl = UB.connection && UB.connection.appConfig.uiSettings.adminUI.supportMailTo
    if (!baseUrl) return ''
    const isQuery = typeof baseUrl === 'string' ? baseUrl.includes('?') : false
    // build mail body
    const userLogin = UB.connection.userLogin()
    const activeTabElems = document.querySelectorAll('.x-panel.x-tabpanel-child.x-panel-default.x-closable.x-panel-closable.x-panel-default-closable')
    let activeTabID
    activeTabElems.forEach((elem) => {
      if (window.getComputedStyle(elem).display === 'block') activeTabID = elem.id
    })
    const obj = {
      login: userLogin,
      uitag: activeTabID,
      details: detail,
      message: msgToDisplay
    }
    const bodyQuery = `body=${encodeURIComponent(JSON.stringify(obj, null, ' '))}`
    let result = isQuery ? baseUrl + `&${bodyQuery}` : baseUrl + `?${bodyQuery}`
    result = 'mailto:' + result.trim()
    const maxSizeResult = 2035 // problem with max size of message in Win plathorm
    if (window?.navigator.platform.includes('Win') && result.length > maxSizeResult) {
      result = result.substring(0, maxSizeResult) + '...'
    }
    return result
  }
  // all styles placed in ./template.vue
  const devBtnID = 'ub-notification__error__dev-btn'
  const showMessBtnID = 'ub-notification__error__show-mess-btn'
  const devBtn = `<i title="${UB.i18n('showDeveloperDetail')}" class="u-icon-wrench" data-id="${devBtnID}"></i>`
  const showMessBtn = `<i title="${UB.i18n('showFullScreen')}" class="u-icon-window-top" data-id="${showMessBtnID}"></i>`
  const mailToHref = buildSupportMailURL()
  const milToBtn = mailToHref ? `<a title="${UB.i18n('supportMailToTitle')}" href="${mailToHref}"  class="fas fa-mail-bulk" target="_blank"></a>` : ''
  const footer = `<div class="ub-notification__error__btn-group">${showMessBtn + devBtn + milToBtn}</div>`
  const message = `<div class="ub-notification__error__content">${msgToDisplay}</div>${footer}`
  const instance = Notification.error({
    title: UB.i18n('error'),
    message,
    dangerouslyUseHTMLString: true,
    customClass: 'ub-notification__error',
    duration: 30000,
    onClose () {
      devBtnEl.removeEventListener('click', devBtnListener)
      showMessBtnEl.removeEventListener('click', showMessBtnListener)
    }
  })

  const devBtnEl = instance.$el.querySelector(`[data-id=${devBtnID}]`)
  const showMessBtnEl = instance.$el.querySelector(`[data-id=${showMessBtnID}]`)
  const devBtnListener = () => {
    return dialogError(detail, 'error', true, { errMsg, errCode, entityCode })
  }
  const showMessBtnListener = () => {
    dialogError(errMsg, 'error')
    instance.close()
  }
  devBtnEl.addEventListener('click', devBtnListener)
  showMessBtnEl.addEventListener('click', showMessBtnListener)
}

/**
 * Shows deletion confirmation message.
 *
 * @param {string} entity Entity code
 * @param {object} [instanceData] Instance data needed to determine description attribute value
 * @returns {Promise<boolean>}
 */
function dialogDeleteRecord (entity, instanceData = {}) {
  const descriptionAttr = UB.connection.domain.get(entity).descriptionAttribute
  const hasDescriptionAttr = descriptionAttr && descriptionAttr in instanceData
  const defaultMess = hasDescriptionAttr
    ? UB.i18n('deleteConfirmationWithCaption', UB.i18n(entity), instanceData[descriptionAttr])
    : UB.i18n('deleteConfirmation', UB.i18n(entity))
  const customMessCode = `${entity}:deleteInquiry`
  const customMess = UB.i18n(customMessCode, UB.i18n(entity), instanceData[descriptionAttr])
  const hasCustomMess = customMessCode !== customMess

  if (hasCustomMess) {
    return dialogYesNo('deletionDialogConfirmCaption', customMess)
  } else {
    return dialogYesNo('deletionDialogConfirmCaption', defaultMess)
  }
}

/**
 *  Inject $dialog into Vue prototype. Called in `adminui-vue` model initialisation.
 *  Injects:
 *   - $dialog
 *   - $dialogError
 *   - $dialogInfo
 *   - $dialogYesNo
 *   - $dialogDeleteRecord
 *   - $errorReporter
 *
 * @param {Vue} Vue
 */
function install (Vue) {
  Vue.prototype.$dialog = dialog
  Vue.prototype.$dialogError = dialogError
  Vue.prototype.$dialogInfo = dialogInfo
  Vue.prototype.$dialogYesNo = dialogYesNo
  Vue.prototype.$dialogDeleteRecord = dialogDeleteRecord
  Vue.prototype.$errorReporter = errorReporter
}

module.exports = {
  dialog,
  dialogError,
  dialogInfo,
  dialogYesNo,
  dialogDeleteRecord,
  errorReporter,
  install
}