const {
  inspect
} = require('util')

const {
  IgnoredError
} = require('../../errors/')

/**
 * A base handler containing handler utilities
 */
class BaseHandler {
  /**
   * Construct a Handler
   * @class
   * @param {Object}      data                The handler data
   * @prop  {Agent}       [data.agent]        The agent managing the bot
   * @prop  {Eris.Client} data.client         The Eris client
   * @prop  {Object}      [data.options={}]   Additional options for the command handler
   * @prop  {Object}      [data.options._app] The Discord bot app info (If not supplied, the app is gotten automatically)
   */
  constructor ({ agent, client, options = {} }) {
    const {
      _app
    } = options

    /**
     * The agent managing the bot
     * @private
     * @type    {Agent}
     */
    this._agent = agent

    /**
     * The Eris client
     * @private
     * @type    {Eris}
     */
    this._client = client

    /**
     * The Discord app of the bot
     * @private
     * @type    {Object}
     */
    this._app = _app

    if (!_app) this._getApp()
  }

  /**
   * Get the bot's app data and store it in `this.app`
   * @private
   * @returns {Promise<Object>} The app data
   */
  _getApp () {
    return this._client.getOAuthApplication().then((app) => {
      this._app = app

      return app
    })
  }

  /**
   * Send a response to Discord
   * @private
   * @async
   * @param   {Eris.TextChannel}      channel             The channel to send the response to
   * @param   {Object}                msgData             The data for the response message
   * @prop    {String}                [msgData.content]   The response content
   * @prop    {Object}                [msgData.embed]     The response embed
   * @prop    {Object}                [msgData.file]      The response file
   * @prop    {String}                [msgData.file.name] The name of the file
   * @prop    {Buffer}                [msgData.file.file] The file content
   * @returns {Promise<Eris.Message>}                     The resulting response or a returned error of minor reasons why the response failed
   */
  async _sendResponse (channel, { content, embed, file }) {
    if (!this.validateChannel(channel)) throw new IgnoredError('Invalid Channel', 'The provided channel was not valid to send a message to', 400)

    if (content || embed || file) {
      if (file && !(file.file instanceof Buffer)) throw TypeError('Supplied file is not a Buffer instance:\n' + (file.file || file))

      return channel.createMessage({ content, embed }, file)
        .catch((err) => {
          if (err.code === 50035 && (err.message.includes('length') || err.message.includes('size'))) {
            return channel.createMessage('Text was too long, sent as a file instead.', {
              name: 'Command Result.txt',
              file: Buffer.from(`${content || 'undefined'}\n\n${inspect(embed)}`)
            })
          }

          throw err
        })
    }
  }

  /**
   * Check that a channel is valid to send a message to (Minimizes bad requests and reduces ratelimiting)
   * @param   {Eris.Channel} channel The channel to verify
   * @returns {Boolean}              Whether a message can be sent to the channel or not
   */
  validateChannel (channel) {
    if (channel && this._client.getChannel(channel.id) && [0, 1].includes(channel.type) && (!channel.type ? channel.permissionsOf(this._client.user.id).has('sendMessages') : true)) return true
    else return false
  }
}

module.exports = BaseHandler