const UB = require('@unitybase/ub-pub')
const { webDavSchemas } = require('@unitybase/cs-shared')

/**
 * WebDav utils. Expect `@ub-e/webdav-model` to be in domain  - it inject `uiSettings.webdav` into appInfo based
 * on `customSettings.webdav` server side config. Otherwise, `isWebDavEnabled` returns `false`
 *
 * @example
const adminuiVue = require('@unitybase/adminui-vue')
if (
  adminuiVue.webDav.isEntitySupportWebDav(this.entityName) &&
  adminuiVue.webDav.canBeOpenedInApp(this.fileName)
) {
  adminuiVue.webDav.openInApp({
    entity: this.entityName,
    attribute: this.attributeName,
    ID: this.fileId
  }, this.fileName)
}
 * @module webDav
 * @memberOf module:@unitybase/adminui-vue
 */
module.exports = {
  isWebDavEnabled,
  isEntitySupportWebDav,
  canBeOpenedInApp,
  canGetPermanentURI,
  getPermanentURI,
  getURI,
  openInApp
}

/**
 * Returns `true` in case WebDav is enabled on server-side
 *
 * @returns {boolean}
 */
function isWebDavEnabled () {
  // uiSettings.webdav is injected into appInfo by `@ub-e/webdav-model` based on customSettings.webdav server side config
  const davCfg = UB.connection.appConfig.uiSettings.adminUI.webdav
  return !!(davCfg && davCfg.enabled)
}

let davEntitiesSet
/**
 * Returns true in case WebDav is enabled and entity supports WebDaw
 *
 * @param {string} entityCode
 * @returns {boolean}
 */
function isEntitySupportWebDav (entityCode) {
  if (!isWebDavEnabled()) return false
  if (!davEntitiesSet) {
    const entitiesCollection = UB.connection.appConfig.uiSettings.adminUI.webdav.entities
    davEntitiesSet = new Set(entitiesCollection.map(eCfg => eCfg.name))
  }
  return davEntitiesSet.has(entityCode)
}

/**
 * Return `true` if file can be opened on application. For actual opening, application what handle depp links should be installed
 *
 * @param {string} fileName
 * @returns {boolean}
 */
function canBeOpenedInApp (fileName) {
  return isWebDavEnabled() && !!getSchemaByFileName(fileName)
}

/**
 * Return `true` if permanent (without auth token) WebDav URI can be generated (only in case Kerberos is used for authentication).
 *   - if `fileName` is empty - returns `true` if authorization support permalinks
 *   - if `fileName` is passed - returns `true` if authorization support permalinks and file type support opening in app
 *
 * @param {string} [fileName]
 * @returns {boolean}
 */
function canGetPermanentURI (fileName) {
  if (!isWebDavEnabled()) return false
  const lastAuthType = window.localStorage.getItem(UB.LDS_KEYS.LAST_AUTH_SCHEMA) || ''
  return (lastAuthType === 'Negotiate') && (!fileName || canBeOpenedInApp(fileName))
}

/**
 * Synchronously return permanent WebDav URI (http.....) for specified document
 *  - in case `canGetPermanentURI` returns `true` for this file name - returns URI
 *  - otherwise - returns empty string ''
 *
 * @param {object} instanceInfo           Instance information
 * @param {string} instanceInfo.entity    Code of entity to retrieve from
 * @param {string} instanceInfo.attribute Code of `document` type attribute for specified entity
 * @param {string} instanceInfo.ID        Instance ID
 * @param {string} [fileName]   Name of file in store (usually origName || fName)
 * @returns {string}
 */
function getPermanentURI (instanceInfo, fileName) {
  if (!canGetPermanentURI(fileName)) return ''
  return _buildURI(instanceInfo, fileName)
}

/**
 * Return WebDav URI (http.....) for specified document.
 * If user authorised using Negotiate (domain) authentication - generate a permanent link, otherwise link contains auth token
 * and valid until current user session is active.
 *
 * @param {object} instanceInfo           Instance information
 * @param {string} instanceInfo.entity    Code of entity to retrieve from
 * @param {string} instanceInfo.attribute Code of `document` type attribute for specified entity
 * @param {string} instanceInfo.ID        Instance ID
 * @param {string} [fileName]   Name of file in store (usually origName || fName)
 * @param {boolean} [forceAuthToken=false] Force adding an Authorization token into URI path for non-Kerberos authorization.
 *   Recommended way is to use Kerberos (domain) authorization and create semantic URL without AuthToken.
 *   By default, if user authorised using Kerberos - semantically correct URL without token is generated and application (Office)
 *   will authorise request using Kerberos
 * @param {boolean} [readOnly=true] Add /ro/ part into URL - UnityBase server handle such URI as readOnly request
 * @returns {Promise<{uri: string, withAuthToken: boolean}>}
 */
async function getURI (instanceInfo, fileName, forceAuthToken = false, readOnly = true) {
  const lastAuthType = window.localStorage.getItem(UB.LDS_KEYS.LAST_AUTH_SCHEMA) || ''
  const withAuthToken = forceAuthToken || (lastAuthType !== 'Negotiate')
  let OTP
  if (withAuthToken) {
    const session = await UB.connection.authorize()
    OTP = 'otp' + session.signature()
  }
  return {
    uri: _buildURI(instanceInfo, fileName, OTP, readOnly),
    withAuthToken
  }
}

/**
 * Build WebDav URI with optional OTP
 *
 * @param {object} instanceInfo           Instance information
 * @param {string} instanceInfo.entity    Code of entity to retrieve from
 * @param {string} instanceInfo.attribute Code of `document` type attribute for specified entity
 * @param {string} instanceInfo.ID        Instance ID
 * @param {string} [fileName]   Name of file in store (usually origName || fName)
 * @param {string|undefined} [OTP] If passed - wil be added to URI after provider name
 * @param {boolean} [readOnly=true] Add /ro/ part into URL - UnityBase server handle such URI as readOnly request
 * @return {string}
 * @private
 */
function _buildURI (instanceInfo, fileName, OTP, readOnly) {
  const davCfg = UB.connection.appConfig.uiSettings.adminUI.webdav
  let davURI = `${window.location.origin}/${davCfg.endpoint}/`
  if (OTP) {
    davURI += (OTP + '/')
  }
  let shortFn = fileName
  const MAX_FN_LEN = 99
  if (shortFn.length > MAX_FN_LEN) {
    // MS Office limitation is 219 characters in file path - see https://support.microsoft.com/en-us/topic/error-message-when-you-open-or-save-a-file-in-microsoft-excel-filename-is-not-valid-951229f3-dc14-980f-765e-224e4fdc7331
    // URL before file name is up to 120 (in case of otp) chars, like https://server.url/folders/otp23815cdd65bccc9ff36bc9b2/doc/doc_attachment/document/3000119001487/,
    // so we have ~100 characters for file name. UnityBase server limit a file name length to 100 char max.
    // Since file name is "virtual" and passed to URl only for correct displaying of file name on client, we reduce length here without losing the extension
    let extPos = shortFn.lastIndexOf('.')
    if (extPos === -1) extPos = shortFn.length
    const ext = shortFn.substring(extPos)
    shortFn = shortFn.substring(0, MAX_FN_LEN - ext.length - 2) + '~1' + ext // fileNameOf100Chars.docx -> fileNameOf100C~1.docx
  }
  shortFn = encodeURIComponent(shortFn) // file name should be encoded to produce valid URL
  const uriParts = [davCfg.provider]
  if (readOnly) uriParts.push('ro')
  uriParts.push(instanceInfo.entity)
  uriParts.push(instanceInfo.attribute)
  uriParts.push(instanceInfo.ID)
  uriParts.push(shortFn)
  davURI += uriParts.join('/')
  return davURI
}

/**
 * Open specified blobItem in desktop application, associated with file extension by redirecting
 * browser to [office URI](https://docs.microsoft.com/office/client-developer/office-uri-schemes).
 *
 * If user authorised using Negotiate (domain) authentication - permanent is user, otherwise link contains auth token
 * and valid until current user session is active.
 *
 * @param {object} instanceInfo           Instance information
 * @param {string} instanceInfo.entity    Code of entity to retrieve from
 * @param {string} instanceInfo.attribute Code of `document` type attribute for specified entity
 * @param {string} instanceInfo.ID        Instance ID
 * @param {string} [fileName]   Name of file in store (usually origName || fName)
 * @param {object} [options={}]
 * @param {boolean} [options.forEdit=false] Force app to open file in edit mode
 * @returns {Promise<boolean>} Return true if permanent link is used
 */
async function openInApp (instanceInfo, fileName, options = { }) {
  const schema = getSchemaByFileName(fileName)
  const { uri, withAuthToken } = await getURI(instanceInfo, fileName, false, !options.forEdit)
  const url = schema + (options.forEdit ? 'ofe|u|' : 'ofv|u|') + uri
  window.localStorage.setItem(UB.LDS_KEYS.PREVENT_CALL_LOGOUT_ON_UNLOAD, 'true')

  if (!UB.isGecko) {
    document.location.href = url
  } else {
    // A simple "document.location.href" change would close WS connections on FF browser
    // So, we need to use a trick with iframe

    // Add iframe, if not exists
    if (!document.querySelector('iframe.ws_keep_conn')) {
      const iframe = document.createElement('iframe')
      iframe.name = 'ws_keep_conn'
      iframe.className = 'ws_keep_conn'
      iframe.style.display = 'none'
      document.body.appendChild(iframe)
    }

    // Add a link to the DOM and click it programmatically, and remove the link instantly
    const link = document.createElement('a')
    link.href = url
    link.target = 'ws_keep_conn'
    document.body.appendChild(link)
    link.click()
    document.body.removeChild(link)
  }

  return !withAuthToken
}

/**
 * Return URI schema name for specified file, or '' if schema not known for file extension
 *
 * @param {string} fileName
 * @returns {string}
 */
function getSchemaByFileName (fileName) {
  if (!fileName) return ''
  const dotIdx = fileName.lastIndexOf('.')
  if (dotIdx === -1) return ''
  const ext = fileName.substring(dotIdx).toLowerCase()
  return webDavSchemas.FILE_EXT2SCHEMA[ext] || ''
}