content: // @flow
  import React from 'react';
  import T from 'prop-types';
  import { customLocationSelector } from 'reducers/custom-location';
  
  import { connect } from 'react-redux';
  import { createSelector } from 'reselect';
  
  import Link from 'components/generic/Link';
  
  import { foundationPartial } from 'styles/foundation';
  import fonts from 'EBI-Icon-fonts/fonts.css';
  import styles from './style.css';
  import config from 'config';
  
  const f = foundationPartial(fonts, styles);
  
  /*::
    type Endpoint ={
      detail: string,
      db: string,
      accession: string,
      isFilter: boolean,
      order: number,
    }
    type SearchLocation = {
      type: string,
      value: string,
      accession: string,
      isFilter: boolean,
      order: number,
    }
    type Location = {
      main: {
        key: string,
        isFilter: boolean,
        order: number,
      },
      search: SearchLocation,
      result: SearchLocation,
      [string]: Endpoint,
      other: Array<string>,
    }
  */
  
  const locationType = T.shape({
    main: T.shape({ key: T.string }),
    entry: T.shape({
      detail: T.string,
      db: T.string,
      accession: T.string,
      isFilter: T.bool,
      order: T.number,
    }),
    other: T.arrayOf(T.string),
  });
  const BreadCrumb = (
    {
      to,
      children,
    } /*: {
      children: any,
      to?: function | {description: Object}} */,
  ) => {
    const childrenToPass =
      typeof children === 'string' ? children.replace(/_/g, ' ') : children;
    return (
      <span>
        {' / '}
        {to ? (
          <Link to={to} className={f('breadcrumb-link')}>
            {childrenToPass}
          </Link>
        ) : (
          childrenToPass
        )}{' '}
      </span>
    );
  };
  BreadCrumb.propTypes = {
    to: T.oneOfType([T.func, T.object]),
    children: T.any,
  };
  
  const BreadCrumbsForOther = ({ other } /*: { other: Array<string>} */) => {
    if (!other.length) return null;
    return (
      <section className={f('main')}>
        {other.map((part, i) => (
          <BreadCrumb
            key={i}
            to={{
              description: { other: other.slice(0, i + 1) },
            }}
          >
            {part}
          </BreadCrumb>
        ))}
      </section>
    );
  };
  BreadCrumbsForOther.propTypes = {
    other: T.arrayOf(T.string),
  };
  
  const BreadCrumbsForSearchOrResult = (
    { location } /*: {location: Location} */,
  ) => {
    if (!['search', 'result'].includes(location?.main?.key)) return null;
    const key /*: 'search' | 'result' */ = (location.main.key /*: any */);
    const { type, value, accession } = location[key];
    return (
      <>
        <section className={f('main')}>
          <BreadCrumb to={{ description: { main: location.main } }}>
            {location.main.key}
          </BreadCrumb>
          <BreadCrumb
            to={{
              description: { main: location.main, [location.main.key]: { type } },
            }}
          >
            {type}
          </BreadCrumb>
          {accession && (
            <BreadCrumb
              to={{
                description: {
                  main: location.main,
                  [location.main.key]: { type, accession },
                },
              }}
            >
              {accession}
            </BreadCrumb>
          )}
          {value && <BreadCrumb>{value}</BreadCrumb>}
        </section>
        {accession && <BreadCrumbForSecondPart location={location} />}
      </>
    );
  };
  BreadCrumbsForSearchOrResult.propTypes = {
    location: locationType,
  };
  
  const BreadCrumbForFilters = ({ location, filters }) => {
    return filters.map((ep) => {
      const { db, accession } = location[ep];
      return (
        <section className={f('filter')} key={ep}>
          <BreadCrumb
            to={{
              description: { ...location, [ep]: { db, isFilter: true } },
            }}
          >
            {ep}{' '}
          </BreadCrumb>
          <BreadCrumb
            to={{
              description: { ...location, [ep]: { db, isFilter: true } },
            }}
          >
            {db}
          </BreadCrumb>
          {accession && (
            <BreadCrumb
              to={{
                description: {
                  ...location,
                  [ep]: { db, accession, isFilter: true },
                },
              }}
            >
              {accession}
            </BreadCrumb>
          )}
        </section>
      );
    });
  };
  BreadCrumbForFilters.propTypes = {
    location: locationType,
    filters: T.arrayOf(T.string),
  };
  
  const BreadCrumbForEntityDetail = (
    {
      location,
      detail,
      filters = [],
    } /*: {location: Location, detail?: string, filters: Array<string>} */,
  ) => {
    const detailToRender = !filters.length && !detail ? ' Overview' : detail;
    return (
      <section className={f('detail')}>
        {detailToRender && (
          <BreadCrumb
            to={
              detail && {
                description: {
                  ...location,
                  [location.main.key]: {
                    ...location[location.main.key],
                    detail,
                  },
                },
              }
            }
          >
            {detailToRender}
          </BreadCrumb>
        )}
        <BreadCrumbForFilters location={location} filters={filters} />
      </section>
    );
  };
  BreadCrumbForEntityDetail.propTypes = {
    location: locationType,
    filters: T.arrayOf(T.string),
    detail: T.string,
  };
  
  const BreadCrumbForSecondPart = ({ location } /*: {location: Location} */) => {
    const { accession, detail } = location[location.main.key];
    const filters = Object.keys(location)
      .filter((ep) => (location[ep] /*: any */)?.isFilter)
      .sort(
        (ep1, ep2) =>
          (location[ep1] /*: any */)?.order - (location[ep2] /*: any */).order,
      );
    return (
      <section className={f('secondary')}>
        {accession ? (
          <BreadCrumbForEntityDetail
            location={location}
            detail={detail}
            filters={filters}
          />
        ) : (
          <BreadCrumbForFilters location={location} filters={filters} />
        )}
      </section>
    );
  };
  BreadCrumbForSecondPart.propTypes = {
    location: locationType,
  };
  
  const BreadCrumbsForBrowse = ({ location } /*: {location: Location} */) => {
    const endpoint = location?.main?.key;
    if (!config.endpoints.includes(endpoint)) return null;
    const { db, accession } = location[location.main.key];
    return (
      <>
        <section className={f('main')}>
          <BreadCrumb
            to={{
              description: { main: { key: 'entry' }, entry: { db: 'interpro' } },
            }}
          >
            Browse
          </BreadCrumb>
          <BreadCrumb
            to={{
              description: { main: { key: endpoint }, [endpoint]: { db } },
            }}
          >
            By {endpoint}{' '}
          </BreadCrumb>
          <BreadCrumb
            to={{
              description: { main: { key: endpoint }, [endpoint]: { db } },
            }}
          >
            {db}
          </BreadCrumb>
          {accession && (
            <BreadCrumb
              to={{
                description: {
                  main: { key: endpoint },
                  [endpoint]: { db, accession },
                },
              }}
            >
              {accession}{' '}
            </BreadCrumb>
          )}
        </section>
        <BreadCrumbForSecondPart location={location} />
      </>
    );
  };
  BreadCrumbsForBrowse.propTypes = {
    location: locationType,
  };
  
  const BreadCrumbs = (
    { location, hash } /*: {location: Location, hash: string} */,
  ) => {
    // Dont show if it is at home
    if (!location?.main?.key && !location?.other?.length) return null;
    return (
      <nav className={f('breadcrumbs')}>
        <Link to={{ description: {} }} className={f('breadcrumb-link')}>
          <span
            className={f('small', 'icon', 'icon-common')}
            data-icon="&#xf015;"
            aria-label={'Home'}
          />
        </Link>
  
        <BreadCrumbsForOther other={location?.other} />
        <BreadCrumbsForSearchOrResult location={location} />
        <BreadCrumbsForBrowse location={location} />
        {location?.main?.key === 'result' && hash?.startsWith('/') && (
          <BreadCrumb>API Download Form</BreadCrumb>
        )}
      </nav>
    );
  };
  BreadCrumbs.propTypes = {
    location: locationType,
    hash: T.string,
  };
  
  const mapStateToProps = createSelector(
    customLocationSelector,
    (customLocation) => ({
      location: customLocation.description,
      hash: customLocation.hash,
    }),
  );
  
  export default connect(mapStateToProps)(BreadCrumbs);
  