import * as util from '../util'
import _axios from 'axios'
import flatten from 'array-flatten'

const fields = ['title', 'description_text', 'body_text', 'tags', 'meta_title', 'meta_description', 'meta_keywords'] // the fields that will be indexed

/**
 * The lunr.js builder plugin.
 * Meant to be used like `builder.use(plugin)`
 *
 * @memberof plugin.freshdesk
 * @return {void}
 */
function plugin () {
  this.ref('id')
  fields.forEach(f => this.field(f))
}

/**
 * Fetches all the solution articles and forum posts from the designated source.
 *
 * @memberof plugin.freshdesk
 * @param  {string} domain          the domain of the API to make the request to
 * @param  {string} user            the username for the API
 * @param  {string} pass            the password for the API
 * @return {object[]}               a list of processed solution articles, forum topics, and forum comments
 */
async function fetch (
  domain,
  user,
  pass
) {
  const solutions = _axios.create({
    baseURL: `https://${domain}.freshdesk.com/api/v2/solutions/`,
    auth: {
      username: user,
      password: pass
    }
  })

  // gets a list of all the articles
  const categories = await get(solutions, 'categories')
  const folders = await getAllItemsIn(categories, solutions, id => `categories/${id}/folders`)
  const articles = await getAllItemsIn(folders, solutions, id => `folders/${id}/articles`)
  const processedArticles = await getAllItemsIn(articles, solutions, id => `articles/${id}`).then(arts => arts.map(a => process(a, 'solution'))) // process each article after fetching it

  const posts = _axios.create({
    baseURL: `https://${domain}.freshdesk.com/api/v2/discussions/`,
    auth: {
      username: user,
      password: pass
    }
  })

  // gets a list of all the articles
  const discussionCategories = await get(posts, 'categories')
  const forums = await getAllItemsIn(discussionCategories, posts, id => `categories/${id}/forums`).then(ary => ary.filter(t => hasComments(t)))
  const topics = await getAllItemsIn(forums, posts, id => `forums/${id}/topics`).then(ary => ary.filter(t => hasComments(t)))
  const comments = await getAllItemsIn(topics, posts, id => `topics/${id}/comments`).then(cmts => cmts.map(comment => process(comment, 'forum')))

  return [...comments, ...processedArticles, ...topics.map(e => process(e, 'forum'))]
}

function hasComments (topic) {
  return topic.comments_count > 0
}

/**
 * Helper function to get all the items from a list of parent resources.
 *
 * @private
 * @param  {object[]} ary             list of parent resources
 * @param  {axios.Instance} axios     the preconfigured axios instance
 * @param  {Function} buildURL        function to build the endpoint URL relative to the baseURL in the axios instance
 * @return {object[]}                 a list of child resources
 */
async function getAllItemsIn (ary, axios, buildURL) {
  return flatten.depth(await Promise.all(ary.map(({id}) => get(axios, buildURL(id)))), 1)
}

/**
 * Helper function to make axios get request.
 *
 * @private
 * @param  {axios.Instance} axios the preconfigured axios instance
 * @param  {string} url     the url to access relative to the baseURL in the axios instance
 * @return {object}         data of axios response
 */
async function get (axios, url) {
  return axios.get(url).then(({data}) => {
    let ret
    const baseURL = axios.defaults.baseURL

    if (data.constructor.name === 'Array') {
      ret = data.map(doc => { return {...doc, documentSource: {url: baseURL + url}} })
    } else if (data.constructor.name === 'Object') {
      ret = {...data, documentSource: {url: baseURL + url}}
    }

    return ret
  })
}

/**
 * Modify the documents so that they can be indexed correctly.
 *
 * @memberof plugin.freshdesk
 * @param  {object} doc the document to be processed
 * @param  {?string} source the source of the document
 * @return {object}     the processed document
 */
function process (doc, source = '') {
  const obj = {...doc} // dup the document

  obj.tags = obj.tags ? obj.tags.join(' ') : '' // lunr can't index an array of strings so join the tags into 1 string

  if (obj.seo_data) Object.keys(obj.seo_data).forEach(k => { obj[k] = obj.seo_data[k] }) // map seo_data to root field so that it can indexed (lunr can't index nested fields)

  if (obj.documentSource) obj.documentSource = {...obj.documentSource, type: source}

  fields.forEach(f => { obj[f] = obj[f] || '' }) // if any fields are null then it will impact lunr search results

  return obj
}

/**
 * Checks to see if certain functions are defined.
 *
 * @return {Boolean}
 * @memberOf plugin.freshdesk
 */
function isSupported () {
  return util.definedFunction(Object.keys) && util.definedFunction([].forEach)
}

/**
 * Contains functions for interacting with the freshdesk API and processing those responses.
 *
 * @namespace freshdesk
 * @memberOf plugin
 */
export {fields, plugin, process, fetch, isSupported}