const dllName = process.platform === 'win32' ? 'ubmail.dll' : 'libubmail.so'
const archPath = process.arch === 'x32' ? './bin/x32' : './bin/x86_64'
const path = require('path')
const fs = require('fs')
const moduleName = path.join(__dirname, archPath, dllName)
let binding
if (!fs.existsSync(moduleName)) {
  console.warn('UBMail is not compiled')
  binding = {}
} else {
  binding = require(moduleName)
}
const UBMail = module.exports
/**
 * The module for sending and receiving mail
 *
 * @module @unitybase/mailer
 */

/**
 * constructor for TubMailReceiver
 *
 * @function TubMailReceiver
 * @returns {TubMailReceiver}
 */
UBMail.TubMailReceiver = binding.TubMailReceiver

/**
 * constructor for TubMailSender
 *
 * @function TubMailSender
 * @returns {TubMailSenderBind}
 */
UBMail.TubMailSender = binding.TubMailSender

/**
 * Mail server connection using IMAP protocol
 *
 * @example
   try {
      const folders = imap.listFolders()
      console.debug('Available folders are:', folders)

      console.log('select INBOX')
      imap.selectFolder('INBOX')

      // Messages that have the \Recent flag set but not the \Seen flag.
      // This is functionally equivalent to "(RECENT UNSEEN)"
      const newSNs = imap.search('NEW')
      console.log(`INBOX contains ${newSNs.length} new messages`)
      if (newSNs.length) {
        let msg = imap.receive(newSNs[0])
        try {
          inspectMessage(msg)
        } finally {
          msg.freeNative()
        }
      }
    } finally {
      imap.freeNative()
    }
 */
class UBMailImap {
  /**
   * @param {object}  paramsObj                 Mail server connection parameters
   * @param {string}  paramsObj.host            mail server host
   * @param {string}  paramsObj.port mail       mail server port
   * @param {string}  [paramsObj.user = '']     mail server login
   * @param {string}  [paramsObj.password = ''] mail server password
   * @param {boolean} [paramsObj.tls=false]     use tls on server request
   * @param {boolean} [paramsObj.fullSSL=false] Setup TLS before any IMAP command
   * @param {boolean} [paramsObj.auth = false]  authentication required
   */
  constructor (paramsObj) {
    this._nativeImap = new binding.TubMailImap(paramsObj)
  }

  /**
   * Select a folder by it's name. Returns true on success or throw on error
   *
   * @param {string} folderName
   * @returns {boolean}
   */
  selectFolder (folderName) {
    if (!this._nativeImap.selectFolder(folderName)) {
      throw new Error(`IMAP - '${this._nativeImap.getResultString()}'`)
    }
    return true
  }

  /**
   * Close a selected folder. (end of Selected state)
   *
   * @returns {boolean}
   */
  closeFolder () {
    if (!this._nativeImap.closeFolder()) {
      throw new Error(`IMAP - '${this._nativeImap.getResultString()}'`)
    }
    return true
  }

  /**
   * Search for message sequence numbers in the selected folder
   *
   * @param {string} criteria A search criteria - see [rfc3501 section-6.4.4](https://tools.ietf.org/html/rfc3501#section-6.4.4)
   * @returns {Array<number>}
   */
  search (criteria) {
    const str = this._nativeImap.searchMess(criteria)
    if (!str) {
      const resp = this._nativeImap.getResultString()
      if (resp.indexOf('OK ') === -1) { // SXX OK SEARCH completed
        throw new Error(`IMAP - '${resp}'`)
      } else {
        return []
      }
    }
    return str.split(',').map(sID => parseInt(sID, 10))
  }

  /**
   * Get a message count from a specified folder what match specified criteria
   *
   * @param {string} [folderName='INBOX']
   * @param {string} [criteria='ALL']
   */
  getMessagesCount (folderName = 'INBOX', criteria = 'ALL') {
    return this._nativeImap.statusFolder(folderName, criteria)
  }

  /**
   * Receive a full message from current folder by it's sequence numbers.
   * To get s message sequence numbers use a `search()` method.
   *
   * Messages indexes are in the order they're received
   *
   * @throws Throws on error
   * @param {number} msgSeqNum
   * @returns {TubMimeMessBind}
   */
  receive (msgSeqNum) {
    return this._nativeImap.receive(msgSeqNum)
  }

  /**
   * Mark the message with specified sequence number as `Deleted`.
   *
   * Real deleting will be done after successful `closeFolder` or `expungeFolder`.
   *
   * @throws Throws on error
   * @param {number} msgSeqNum
   * @returns {TubMimeMessBind}
   */
  deleteMessage (msgSeqNum) {
    return this._nativeImap.deleteMessage(msgSeqNum)
  }

  /**
   * Returns size of the message with specified sequence number
   *
   * @param {number} msgSeqNum
   */
  getMessageSize (msgSeqNum) {
    const s = this._nativeImap.getMessageSize(msgSeqNum)
    if (s === -1) {
      throw new Error(`IMAP - '${this._nativeImap.getResultString()}'`)
    }
    return s
  }

  /**
   * List sub-folders of initialFolder. If initial folder is '' then list all available folders
   *
   * @param {string} [initialFolderName='']
   * @returns {Array<string>}
   */
  listFolders (initialFolderName = '') {
    const str = this._nativeImap.listFolders(initialFolderName)
    if (!str) throw new Error(`IMAP - '${this._nativeImap.getResultString()}`)
    return str.split(',')
  }

  /**
   * Close IMAP connection and release all resources ASAP
   */
  freeNative () {
    if (this._nativeImap) {
      this.closeFolder()
      this._nativeImap.freeNative()
      this._nativeImap = undefined
    }
  }
}
UBMail.UBMailImap = UBMailImap

/**
 * IMAP based mail receiver. API is compatible with TubMailReceiver, so can be used as direct replacement.
 * For LEGACY code only. In new code UBMailImap should be used
 */
class TubMailReceiverImap extends UBMailImap {
  /**
   * @param {object}  paramsObj                 Mail server connection parameters
   * @param {string}  paramsObj.host            mail server host
   * @param {string}  paramsObj.port mail       mail server port
   * @param {string}  [paramsObj.user = '']     mail server login
   * @param {string}  [paramsObj.password = ''] mail server password
   * @param {boolean} [paramsObj.tls=false]     use tls on server request
   * @param {boolean} [paramsObj.fullSSL=false] Setup TLS before any IMAP command
   * @param {boolean} [paramsObj.auth = false]  authentication required
   */
  constructor (paramsObj) {
    super(paramsObj)
    /**
     * Message IDs for receive iteration
     *
     * @type {undefined}
     * @private
     */
    this._messagesIDs = undefined
  }

  /**
   * Method for TubMailReceiver POP3 compatibility.
   *
   * Receive a full message from ALL 'INBOX' messages by it's index.
   *
   * Messages indexes are in the order they're received
   *
   * @throws Throws on error
   * @param {number} msgIndex
   * @returns {TubMimeMessBind}
   */
  receive (msgIndex) {
    this._checkIDsLoaded(msgIndex)
    return this._nativeImap.receive(this._messagesIDs[msgIndex])
  }

  /**
   * Method for TubMailReceiver POP3 compatibility.
   *
   * Mark message from ALL 'INBOX' messages by it's index as `Deleted`.
   *
   * Real deleting will be done after successful `closeFolder` or `expungeFolder`.
   * For compatibility with TubMailReceiver UBMailImap.expungeFolder is called inside freeNative() method.
   *
   * @throws Throws on error
   * @param {number} msgIndex
   * @returns {TubMimeMessBind}
   */
  deleteMessage (msgIndex) {
    this._checkIDsLoaded(msgIndex)
    return this._nativeImap.deleteMessage(this._messagesIDs[msgIndex])
  }

  /**
   * Method for TubMailReceiver POP3 compatibility.
   *
   * Returns size of ALL message from 'INBOX' by it's index
   *
   * @param {number} msgIndex
   */
  getMessageSize (msgIndex) {
    this._checkIDsLoaded(msgIndex)
    const s = this._nativeImap.getMessageSize(this._messagesIDs[msgIndex])
    if (s === -1) {
      throw new Error(`IMAP - '${this._nativeImap.getResultString()}'`)
    }
    return s
  }

  /**
   * Method for TubMailReceiver POP3 compatibility. Do nothing.
   *
   * @returns {boolean}
   */
  reconnect () {
    return true
  }

  /**
   * Load ALL message IDs from INBOX for POP3 compatibility functions
   *
   * @param msgIndex
   * @private
   */
  _checkIDsLoaded (msgIndex) {
    if (!this._messagesIDs) { // initialize message IDs array on first call to receive
      this.selectFolder('INBOX')
      this._messagesIDs = this.search('ALL')
    }
    if (msgIndex >= this._messagesIDs.length) throw new Error(`IMAP - message index ${msgIndex} is out of bounds [0..${this._messagesIDs.length}]`)
  }

  /**
   * Close IMAP connection and release all resources ASAP
   */
  freeNative () {
    this._messagesIDs = undefined
    super.freeNative()
  }
}
UBMail.TubMailReceiverImap = TubMailReceiverImap

const _bt = binding.TubSendMailBodyType || {}
/**
 * Mail body type
 *
 * @enum
 */
UBMail.TubSendMailBodyType = {
  Text: _bt.Text,
  HTML: _bt.HTML,
  Calendar: _bt.Calendar
}

const _ac = binding.TubSendMailAttachKind || {}
/**
 * Mail attach kind
 *
 * @enum
 */
UBMail.TubSendMailAttachKind = {
  File: _ac.File,
  Text: _ac.Text,
  Buffer: _ac.Buffer
}

/**
 * Get body from message
 *
 * @deprecated Use UBMail.getBodyPart(mimeMsg).read() instead
 */
UBMail.getBodyFromMessage = function () {
  throw new Error('UBMail.getBodyFromMessage is obsolete. Use UBMail.getBodyPart(mimeMsg).read() instead')
}

/**
 * Return a mime part what represents the e-mail body
 *
 * @param {TubMimeMessBind} message
 * @returns {TMimePartBind}
 */
UBMail.getBodyPart = function (message) {
  /**
   *
   * @param part
   */
  function bodyPartDeep (part) {
    const subPart = part.subPart
    const L = subPart.length
    if (L === 0) {
      return part
    } else {
      for (let i = 0; i < L; i++) {
        const pi = subPart[i]
        if (pi.disposition !== 'ATTACHMENT') {
          return bodyPartDeep(pi)
        }
      }
    }
  }
  return bodyPartDeep(message.messagePart)
}