//= require lunr.min //= require _vendor/jquery.mark.js (function ($, Modules) {

'use strict'

Modules.Search = function Search () {
  var s = this
  var $html = $('html')
  var $tocNav
  var $searchForm
  var $searchLabel
  var $searchInput
  var $searchResults
  var $searchResultsTitle
  var $searchResultsWrapper
  var $searchResultsClose
  var results
  var query
  var queryTimer
  var maxSearchEntries = 20

  this.start = function start ($element) {
    $searchForm = $element.find('form')
    $searchInput = $element.find('#search')
    $searchLabel = $element.find('.search__label')
    $searchResultsWrapper = $element.find('.search-results')
    $searchResults = $searchResultsWrapper.find('.search-results__content')
    $searchResultsTitle = $searchResultsWrapper.find('.search-results__title')
    $searchResultsClose = $searchResultsWrapper.find('.search-results__close')
    $tocNav = $('#toc')
    s.downloadSearchIndex()
    attach()
    changeSearchLabel()
  }

  this.downloadSearchIndex = function downloadSearchIndex () {
    updateTitle('Loading search index')
    $.ajax({
      url: '/search.json',
      cache: true,
      method: 'GET',
      success: function (data) {
        s.lunrData = data
        s.lunrIndex = lunr.Index.load(s.lunrData.index)
        replaceStopWordFilter()
        $(document).trigger('lunrIndexLoaded')
      }
    })
  }

  function attach () {
    // Search functionality on search text input
    $searchInput.on('input', function (e) {
      e.preventDefault()
      query = $(this).val()
      s.search(query, function (r) {
        results = r
        renderResults(query)
        updateTitle()
      })
      if (window.ga) {
        window.clearTimeout(queryTimer)
        queryTimer = window.setTimeout(sendQueryToAnalytics, 1000)
      }
    })

    // Set focus on the first search result instead of submiting the search
    // form to Google
    $searchForm.on('submit', function (e) {
      e.preventDefault()
      showResults()
      var $firstResult = $searchResults.find('.search-result__title a').first()
      if ($firstResult.length) {
        $firstResult.focus()
      } else {
        // if there are no results, show the "0 results" state
        results = []
        updateTitle()
      }
    })

    // Closing the search results, move focus back to the search input
    $searchResultsClose.on('click', function (e) {
      e.preventDefault()
      $searchInput.focus()
      hideResults()
    })

    // Attach analytics events to search result clicks
    if (window.ga) {
      $searchResults.on('click', '.search-result__title a', function () {
        var href = $(this).attr('href')
        ga('send', {
          hitType: 'event',
          eventCategory: 'Search result',
          eventAction: 'click',
          eventLabel: href,
          transport: 'beacon'
        })
      })
    }

    // When selecting navigation link, close the search results.
    $('#toc').on('click', 'a', function (e) {
      hideResults()
    })
  }

  function changeSearchLabel () {
    $searchLabel.text('Search this documentation')
  }

  function getResults (query) {
    var results = []
    s.lunrIndex.search(query).forEach(function (item, index) {
      if (index < maxSearchEntries) {
        results.push(s.lunrData.docs[item.ref])
      }
    })
    return results
  }

  this.search = function search (query, callback) {
    if (query === '') {
      hideResults()
      return
    }
    showResults()
    // The index has not been downloaded yet, exit early and wait.
    if (!s.lunrIndex) {
      $(document).on('lunrIndexLoaded', function () {
        s.search(query, callback)
      })
      return
    }
    callback(getResults(query))
  }

  function renderResults (query) {
    var output = ''
    if (results.length === 0) {
      output += '<p>Nothing found</p>'
    }
    output += '<ul>'
    for (var index in results) {
      var result = results[index]
      var content = s.processContent(result.content, query)
      output += '<li class="search-result">'
      output += '<h3 class="search-result__title">'
      output += '<a href="' + result.url + '">'
      output += result.title
      output += '</a>'
      output += '</h3>'
      if (typeof content !== 'undefined') {
        output += '<p>' + content + '</p>'
      }
      output += '</li>'
    }
    output += '</ul>'

    $searchResults.html(output)
  }

  this.processContent = function processContent (content, query) {
    var output
    content = '<div>' + content + '</div>'
    content = $(content).mark(query)

    // Split content by sentence.
    var sentences = content.html().replace(/(\.+|:|!|\?|\r|\n)("*|'*|\)*|}*|]*)/gm, '|').split('|')

    // Select the first five sentences that contain a <mark>
    var selectedSentences = []
    for (var i = 0; i < sentences.length; i++) {
      if (selectedSentences.length === 5) {
        break
      }

      var sentence = sentences[i].trim()
      var containsMark = sentence.includes('mark>')
      if (containsMark && (selectedSentences.indexOf(sentence) === -1)) {
        selectedSentences.push(sentence)
      }
    }
    if (selectedSentences.length > 0) {
      output = ' … ' + selectedSentences.join(' … ') + ' … '
    }
    return output
  }

  // Default text is to display the number of search results
  function updateTitle (text) {
    if (typeof text === 'undefined') {
      var count = results.length
      var resultsText = count + ' results'
    }
    $searchResultsTitle.text(text || resultsText)
  }

  function showResults () {
    $searchResultsWrapper.addClass('is-open')
      .attr('aria-hidden', 'false')
    $html.addClass('has-search-results-open')
    $tocNav.addClass('search-results-open')
  }

  function hideResults () {
    $searchResultsWrapper.removeClass('is-open')
      .attr('aria-hidden', 'true')
    $html.removeClass('has-search-results-open')
    $tocNav.removeClass('search-results-open')
  }

  function sendQueryToAnalytics () {
    if (query === '') {
      return
    }
    var stripped = window.stripPIIFromString(query)
    ga('send', {
      hitType: 'event',
      eventCategory: 'Search query',
      eventAction: 'type',
      eventLabel: stripped,
      transport: 'beacon'
    })
  }

  function replaceStopWordFilter () {
    // Replace the default stopWordFilter as it excludes useful words like
    // 'get'
    // See: https://lunrjs.com/docs/stop_word_filter.js.html#line43
    s.lunrIndex.pipeline.remove(lunr.stopWordFilter)
    s.lunrIndex.pipeline.add(s.govukStopWorldFilter)
  }

  this.govukStopWorldFilter = lunr.generateStopWordFilter([
    'a',
    'able',
    'about',
    'across',
    'after',
    'all',
    'almost',
    'also',
    'am',
    'among',
    'an',
    'and',
    'any',
    'are',
    'as',
    'at',
    'be',
    'because',
    'been',
    'but',
    'by',
    'can',
    'cannot',
    'could',
    'dear',
    'did',
    'do',
    'does',
    'either',
    'else',
    'ever',
    'every',
    'for',
    'from',
    'got',
    'had',
    'has',
    'have',
    'he',
    'her',
    'hers',
    'him',
    'his',
    'how',
    'however',
    'i',
    'if',
    'in',
    'into',
    'is',
    'it',
    'its',
    'just',
    'least',
    'let',
    'like',
    'likely',
    'may',
    'me',
    'might',
    'most',
    'must',
    'my',
    'neither',
    'no',
    'nor',
    'not',
    'of',
    'off',
    'often',
    'on',
    'only',
    'or',
    'other',
    'our',
    'own',
    'rather',
    'said',
    'say',
    'says',
    'she',
    'should',
    'since',
    'so',
    'some',
    'than',
    'that',
    'the',
    'their',
    'them',
    'then',
    'there',
    'these',
    'they',
    'this',
    'tis',
    'to',
    'too',
    'twas',
    'us',
    'wants',
    'was',
    'we',
    'were',
    'what',
    'when',
    'where',
    'which',
    'while',
    'who',
    'whom',
    'why',
    'will',
    'with',
    'would',
    'yet',
    'you',
    'your'
  ])
}

// Polyfill includes
if (!String.prototype.includes) {
  String.prototype.includes = function (search, start) { // eslint-disable-line no-extend-native
    'use strict'
    if (typeof start !== 'number') {
      start = 0
    }

    if (start + search.length > this.length) {
      return false
    } else {
      return this.indexOf(search, start) !== -1
    }
  }
}

})(jQuery, window.GOVUK.Modules)