const _ = require('lodash')
const { SearchService, QueryType } = require('azure-search-client')
const request = require('request-promise')
const stream = require('stream')
const Promise = require('bluebird')
const pipeline = Promise.promisify(stream.pipeline)

/* global WIKI */

module.exports = {
  async activate() {
    // not used
  },
  async deactivate() {
    // not used
  },
  /**
   * INIT
   */
  async init() {
    WIKI.logger.info(`(SEARCH/AZURE) Initializing...`)
    this.client = new SearchService(this.config.serviceName, this.config.adminKey)

    // -> Create Search Index
    const indexes = await this.client.indexes.list()
    if (!_.find(_.get(indexes, 'result.value', []), ['name', this.config.indexName])) {
      WIKI.logger.info(`(SEARCH/AZURE) Creating index...`)
      await this.client.indexes.create({
        name: this.config.indexName,
        fields: [
          {
            name: 'id',
            type: 'Edm.String',
            key: true,
            searchable: false
          },
          {
            name: 'locale',
            type: 'Edm.String',
            searchable: false
          },
          {
            name: 'path',
            type: 'Edm.String',
            searchable: false
          },
          {
            name: 'title',
            type: 'Edm.String',
            searchable: true
          },
          {
            name: 'description',
            type: 'Edm.String',
            searchable: true
          },
          {
            name: 'content',
            type: 'Edm.String',
            searchable: true
          }
        ],
        scoringProfiles: [
          {
            name: 'fieldWeights',
            text: {
              weights: {
                title: 4,
                description: 3,
                content: 1
              }
            }
          }
        ],
        suggesters: [
          {
            name: 'suggestions',
            searchMode: 'analyzingInfixMatching',
            sourceFields: ['title', 'description', 'content']
          }
        ]
      })
    }
    WIKI.logger.info(`(SEARCH/AZURE) Initialization completed.`)
  },
  /**
   * QUERY
   *
   * @param {String} q Query
   * @param {Object} opts Additional options
   */
  async query(q, opts) {
    try {
      let suggestions = []
      const results = await this.client.indexes.use(this.config.indexName).search({
        count: true,
        scoringProfile: 'fieldWeights',
        search: q,
        select: 'id, locale, path, title, description',
        queryType: QueryType.simple,
        top: 50
      })
      if (results.result.value.length < 5) {
        // Using plain request, not yet available in library...
        try {
          const suggestResults = await request({
            uri: `https://${this.config.serviceName}.search.windows.net/indexes/${this.config.indexName}/docs/autocomplete`,
            method: 'post',
            qs: {
              'api-version': '2017-11-11-Preview'
            },
            headers: {
              'api-key': this.config.adminKey,
              'Content-Type': 'application/json'
            },
            json: true,
            body: {
              autocompleteMode: 'oneTermWithContext',
              search: q,
              suggesterName: 'suggestions'
            }
          })
          suggestions = suggestResults.value.map(s => s.queryPlusText)
        } catch (err) {
          WIKI.logger.warn('Search Engine suggestion failure: ', err)
        }
      }
      return {
        results: results.result.value,
        suggestions,
        totalHits: results.result['@odata.count']
      }
    } catch (err) {
      WIKI.logger.warn('Search Engine Error:')
      WIKI.logger.warn(err)
    }
  },
  /**
   * CREATE
   *
   * @param {Object} page Page to create
   */
  async created(page) {
    await this.client.indexes.use(this.config.indexName).index([
      {
        id: page.hash,
        locale: page.localeCode,
        path: page.path,
        title: page.title,
        description: page.description,
        content: page.safeContent
      }
    ])
  },
  /**
   * UPDATE
   *
   * @param {Object} page Page to update
   */
  async updated(page) {
    await this.client.indexes.use(this.config.indexName).index([
      {
        id: page.hash,
        locale: page.localeCode,
        path: page.path,
        title: page.title,
        description: page.description,
        content: page.safeContent
      }
    ])
  },
  /**
   * DELETE
   *
   * @param {Object} page Page to delete
   */
  async deleted(page) {
    await this.client.indexes.use(this.config.indexName).index([
      {
        '@search.action': 'delete',
        id: page.hash
      }
    ])
  },
  /**
   * RENAME
   *
   * @param {Object} page Page to rename
   */
  async renamed(page) {
    await this.client.indexes.use(this.config.indexName).index([
      {
        '@search.action': 'delete',
        id: page.sourceHash
      }
    ])
    await this.client.indexes.use(this.config.indexName).index([
      {
        id: page.destinationHash,
        locale: page.localeCode,
        path: page.destinationPath,
        title: page.title,
        description: page.description,
        content: page.safeContent
      }
    ])
  },
  /**
   * REBUILD INDEX
   */
  async rebuild() {
    WIKI.logger.info(`(SEARCH/AZURE) Rebuilding Index...`)
    await pipeline(
      WIKI.models.knex.column({ id: 'hash' }, 'path', { locale: 'localeCode' }, 'title', 'description', 'render').select().from('pages').where({
        isPublished: true,
        isPrivate: false
      }).stream(),
      new stream.Transform({
        objectMode: true,
        transform: (chunk, enc, cb) => {
          cb(null, {
            id: chunk.id,
            path: chunk.path,
            locale: chunk.locale,
            title: chunk.title,
            description: chunk.description,
            content: WIKI.models.pages.cleanHTML(chunk.render)
          })
        }
      }),
      this.client.indexes.use(this.config.indexName).createIndexingStream()
    )
    WIKI.logger.info(`(SEARCH/AZURE) Index rebuilt successfully.`)
  }
}