/* istanbul ignore file */

import React from 'react';
import PropTypes from 'prop-types';
import { connectHits, connectStateResults, Snippet } from 'react-instantsearch-dom';

// Components
import Pagination from '../Pagination';
import Title from '@ui/components/Title';

// Style
let classes = {};
/* istanbul ignore next */
if (typeof window !== 'undefined' || typeof _COMPILED_ === 'undefined') classes = require('./style.module.scss');

// custom results
const Results = connectStateResults(({ searchState, searchResults, children }) => {
  // welcome state
  if (searchState && !searchState.query) {
    return (
      <div className={[classes.SearchResults, classes.SearchResults_empty].join(' ')}>
        <i className="icon icon-search" />
        <Title level={6}>Start typing to search…</Title>
      </div>
    );
  }

  // empty state (no results)
  if (searchResults && searchResults.nbHits === 0) {
    return (
      <div className={[classes.SearchResults, classes.SearchResults_empty].join(' ')}>
        <Title level={6}>Try a different search.</Title>
        <div>No results for ‘{searchState.query}’.</div>
      </div>
    );
  }

  return children;
});

const determineResultDisplay = hit => {
  const { _snippetResult: snippet } = hit;
  const fullMatch = Object.entries(snippet).sort((a, b) => {
    const [, aVal] = a;
    const [, bVal] = b;
    if (aVal.matchLevel === 'full' && (bVal.matchLevel === 'partial' || bVal.matchLevel === 'none')) return -1;
    else if (aVal.matchLevel === bVal.matchLevel) return 0;
    return 1;
  });
  return fullMatch[0][0];
};

const handleArrowFocus = e => {
  const { key, target } = e;
  const hitList = target.querySelector('.SearchResults-list') || target.parentElement;
  const focusAt = document.querySelector('a:focus');

  let focusTo;
  if (!focusAt) focusTo = hitList.firstElementChild.focus();
  else if (key && (key === 'ArrowUp' || key === 'ArrowDown')) {
    if (key === 'ArrowUp') focusTo = focusAt.previousElementSibling || hitList.lastElementChild;
    if (key === 'ArrowDown') focusTo = focusAt.nextElementSibling || hitList.firstElementChild;
    e.preventDefault();
  }

  if (focusTo) focusTo.focus();
};

const createCustomHitsComponent = (subdomains, onReload, UrlManager) => {
  const Hits = ({ hits }) => {
    // if there are no results within the current filters
    if (hits.length === 0) {
      return (
        <div className={[classes.SearchResults, classes.SearchResults_empty].join(' ')}>
          <Title level={6}>No results.</Title>
          <div>Try a different filter or tab.</div>
        </div>
      );
    }

    // if there are results
    if (typeof onReload === 'function') onReload(); // callback
    const results = hits.map(hit => {
      // Build hitUrl
      let hitUrl;
      if (typeof window !== 'undefined' && window.location.hash) {
        hitUrl = UrlManager.remove('hash', window.location.href);
      }
      hitUrl = UrlManager.upsert('slugs', hit.internalLink.split('/'), hitUrl);

      // Enterprise only: Populate child if project has parent & hit is from a different child project
      if (UrlManager.options.parent && subdomains[hit.project] !== UrlManager.options.project.subdomain) {
        hitUrl = UrlManager.upsert('child', subdomains[hit.project], hitUrl);
        // Remove version if hit is from a different child project
        if (UrlManager.options.version) {
          hitUrl = UrlManager.remove('version', hitUrl);
        }
      }

      const attribute = determineResultDisplay(hit);
      const title = hit.isReference && hit.method ? `(${hit.method.toUpperCase()}) ${hit.title}` : hit.title;

      return (
        <a key={hit.objectID} className={classes['SearchResults-Result']} href={hitUrl} tabIndex={0} target="_self">
          <header className={classes['SearchResults-Result-Header']} title={title}>
            {hit.isReference && (
              <span
                className={[classes['SearchResults-Method'], classes[`SearchResults-Method_${hit.method}`]].join(' ')}
              >
                {hit.method}
              </span>
            )}
            <span className={classes['SearchResults-Result-Title']}>
              {hit.title}
              {Object.keys(subdomains).length <= 1 || (
                <span className={classes['SearchResults-Result-Project']}>{hit.subdomain}</span>
              )}
            </span>
            {hit.link_url && (
              <small className={classes['SearchResults-Result-Link']}>
                <i className="fa fa-external-link-alt" />
              </small>
            )}
          </header>
          <div className={classes['SearchResults-Result-Excerpt']} title={title}>
            <Snippet attribute={attribute} hit={hit} />
          </div>
        </a>
      );
    });

    return (
      <div className={[classes.SearchResults, classes.SearchResults_default].join(' ')}>
        <div className={classes['SearchResults-ScrollTo']} />
        <div
          className="SearchResults-list"
          onKeyDownCapture={() => {
            if (typeof window !== 'undefined') handleArrowFocus();
          }}
        >
          {results}
        </div>
        <Pagination />
      </div>
    );
  };
  Hits.propTypes = {
    hits: PropTypes.array,
  };
  return Hits;
};

const SearchResults = ({ subdomains, onReload, UrlManager }) => {
  const Hits = createCustomHitsComponent(subdomains, onReload, UrlManager);
  const CustomHits = connectHits(Hits);
  return (
    <Results>
      <CustomHits hitComponent={Hits} />
    </Results>
  );
};

SearchResults.propTypes = {
  onReload: PropTypes.func,
  subdomains: PropTypes.object,
  tab: PropTypes.string,
  UrlManager: PropTypes.shape({
    options: PropTypes.object,
    remove: PropTypes.func.isRequired,
    upsert: PropTypes.func.isRequired,
  }).isRequired,
};

SearchResults.defaultProps = {
  subdomains: {},
};

export default SearchResults;
