/*!
 * Copyright (C) 2019  Josh Habdas <jhabdas@protonmail.com>
 *
 * This file is part of After Dark.
 *
 * After Dark is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published
 * by the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * After Dark is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
 */

fetchInject([
  "/js/vue.min.js",
  "/js/lodash.custom.min.js",
  "/js/fuse.min.js",
  "/js/mark.min.js"
]).then(() => {
  (function (window, document, undefined) {
    'use strict';

    const getQueryByParam = param => decodeURIComponent(
      (location.search.split(param + '=')[1] || '').split('&')[0]
    ).replace(/\+/g, ' ');

    const queryParam = 's';
    const hotkeys = ["/","s"];
    const selectors = {
      appContainer: '#search-app',
      resultContainer: '#search-results',
      searchInput: '#query'
    };

    const fuseOpts = {
      shouldSort: true,
      tokenize: true,
      matchAllTokens: true,
      includeScore: true,
      includeMatches: true,
      keys: [
        { name: "title", weight: 0.8 },
        { name: "contents", weight: 0.5 },
        { name: "tags", weight: 0.3 },
        { name: "categories", weight: 0.3 }
      ]
    };

    const getSearchInput = () => document.querySelector(selectors.searchInput);
    const focusSearchInput = () => getSearchInput().focus();
    const searchQuery = getSearchInput().value = getQueryByParam(queryParam);

    const fuse = new Fuse([], fuseOpts);
    window.fetch('/index.json').then(response => {
      response.text().then(searchData => {
        fuse.setCollection(JSON.parse(searchData));
        searchQuery && search(searchQuery);
      });
    });

    const getUrl = (query) => {
      const encodedQuery = encodeURIComponent(query);
      const url = "/search/";
      return (encodedQuery)
        ? `${url}?${queryParam}=${encodedQuery}`
        : url;
    };

    let mark = new Mark(
      document.querySelector(
        selectors.resultContainer
      )
    );

    const app = new Vue({
      delimiters: ['{', '}'],
      el: selectors.appContainer,
      data: {
        fuse: null,
        results: [],
        query: getQueryByParam(queryParam),
        resultsForSearch: getQueryByParam(queryParam)
      },
      mounted () {
        this.fuse = fuse;
        window.onpopstate = (evt) => {
          this.query = evt.state.query;
        };
        const searchInput = getSearchInput();
        document.onkeydown = function (evt) {
          if (evt.target === searchInput) return;
          if (hotkeys.includes(evt.key)) {
            evt.preventDefault();
            focusSearchInput();
            getSearchInput().select();
          };
        }
        focusSearchInput();
      },
      watch: {
        query () {
          this.executeSearch();
          window.history.replaceState(
            {query: this.query},
            null,
            getUrl(this.query)
          );
        }
      },
      beforeUpdate: function () {
        mark.unmark();
      },
      updated: function () {
        this.$nextTick(function () {
          mark = new Mark(
            document.querySelector(
              selectors.resultContainer
            )
          )
          mark.mark(this.query.trim());
        })
      },
      methods: {
        executeSearch: _.debounce(function () {
          const trimmedQuery = this.query.trim();
          this.resultsForSearch = trimmedQuery;
          this.results = (trimmedQuery)
            ? this.fuse.search(trimmedQuery)
            : [];
        }, 250)
      }
    });

    const search = query => {
      app.results = fuse.search(query);
    };

  })(window, document);
});
