import { Spinner, DeferredRender, Icon, FilterInput, HelpMessage, __TabBar, StickyElement, Popout } from '@vizality/components';
import { useForceUpdate, useFilter, useToggle } from '@vizality/hooks';
import { toPlural, toPluralOrSingular, toKebabCase } from '@vizality/util/string';
import React, { memo, useState, useEffect } from 'react';
import { joinClassNames } from '@vizality/util/dom';
import { getModule } from '@vizality/webpack';
import { error } from '@vizality/util/logger';
import { Events } from '@vizality/constants';
import { useRouteMatch } from 'react-router';
import { Messages } from '@vizality/i18n';

import SortFilterMenu from './menus/SortFilterMenu';
import OverflowMenu from './menus/OverflowMenu';
import DisplayMenu from './menus/DisplayMenu';
import TagsMenu from './menus/TagsMenu';
import AddonCard from '../AddonCard';

/**
 * @private
 */
const _labels = [ 'Component', 'AddonsList' ];
const _error = (...message) => error({ labels: _labels, message });

/**
 * Renders addon card fillers. The only purpose of this is to add filler addon items to
 * correct the item widths of the last flex row of the list.
 * @private
 * @component
 * @returns {React.MemoExoticComponent<function(): React.ReactElement>}
 */
const FillerAddonCards = memo(() => {
  const placeholders = [];
  for (let i = 0; i < 8; i++) {
    placeholders.push(
      <div className='vz-addon-card vz-addon-card-filler' />
    );
  }
  return placeholders;
});

/**
 * Renders a search results header, which shows what type of addons are being displayed
 * as well as how many, given the current query and filter results.
 * @private
 * @component
 * @returns {React.MemoExoticComponent<function(): React.ReactElement>}
 */
const SearchResultsHeader = memo(({ query, resultsCount, limit, type, tab }) => {
  const { header } = getModule('header', 'item', 'separator');
  const { marginTop20 } = getModule('marginTop20');
  return (
    <>
      {tab === 'browse' && (
        <HelpMessage messageType={HelpMessage.Types.WARNING} className={joinClassNames('vz-addons-list-help-message', marginTop20)}>
          The download counts are shown for testing and demo purposes only and do not currently provide an accurate representation of the addon's download count.
        </HelpMessage>
      )}
      <div className='vz-addons-list-search-results-text-wrapper'>
        <div className={joinClassNames('vz-addons-list-search-results-text', header)}>
          <span className='vz-addons-list-search-results-count'>{resultsCount}</span> {toPluralOrSingular(type, resultsCount)} found{!query && limit && resultsCount > limit && `... Showing ${limit}.`} {query && query !== '' && <>
            matching "<span className='vz-addons-list-search-results-matched'>{query}</span>"{limit && resultsCount > limit && `... Showing ${limit}.`}
          </>}
        </div>
      </div>
    </>
  );
});

/**
 * Renders the content body, which consists of the addon cards.
 * @private
 * @component
 * @returns {React.MemoExoticComponent<function(): React.ReactElement>}
 */
const ContentBody = memo(({ community, display, filteredResults, type, limit, resultsCount, handleResultsCount }) => {
  const { emptyStateImage } = getModule('emptyStateImage', 'emptyStateHeader');

  /**
   * If the filtered items count doesn't equal the current results count, update the value.
   */
  if (filteredResults.length !== resultsCount) {
    handleResultsCount(filteredResults.length);
  }

  return (
    <div className='vz-addons-list-items'>
      {filteredResults.length
        ? (
          <>
            {filteredResults.slice(0, limit || filteredResults.length).map(item => (
              <AddonCard
                community={community}
                display={display}
                type={type}
                addonId={item.addonId}
              />
            ))
            }
            <FillerAddonCards />
          </>
        )
        : (
          <div className='vz-addons-list-empty'>
            <div class={emptyStateImage} />
            <p>{Messages.GIFT_CONFIRMATION_HEADER_FAIL}</p>
            <p>{Messages.SEARCH_NO_RESULTS}</p>
          </div>
        )
      }
    </div>
  );
});

/**
 * Renders the page stickybar, which contains the tabs, search bar, and various display,
 * sorting, and filtering options.
 * @private
 * @component
 * @returns {React.MemoExoticComponent<function(): React.ReactElement>}
 */
const StickyBar = memo(({ query, type, tab, resetSearchOptions, display, handleQueryChange, handleDisplayChange }) => {
  const [ sticky ] = useState(false);
  const [ showSortFilterMenu, toggleSortFilterMenu ] = useToggle(false);
  const [ showTagsMenu, toggleTagsMenu ] = useToggle(false);
  const [ showDisplayMenu, toggleDisplayMenu ] = useToggle(false);
  const [ showOverflowMenu, toggleOverflowMenu ] = useToggle(false);

  /**
   * Formats a display type string into one matching its corresponding icon name.
   * @param {string} display Addon display type
   * @returns {string}
   */
  const formatDisplayIconName = display => {
    return `layout-${toKebabCase(display)}`;
  };

  /**
   * Configuration for the popouts used in this component.
   */
  const popoutConfig = {
    position: 'left',
    animation: '2'
  };

  return (
    <StickyElement
      wrapperClassName='vz-addons-list-sticky-bar-wrapper'
      className='vz-addons-list-sticky-bar'
    >
      <__TabBar type={__TabBar.Types.TOP_PILL}>
        <__TabBar.NavItem
          route={`/vizality/${toPlural(type)}/installed`}
          selected={tab === 'installed'}
          // onClick={() => handleTabChange('installed')}
        >
          {Messages.VIZALITY_INSTALLED}
        </__TabBar.NavItem>
        <__TabBar.NavItem
          disabled
          route={`/vizality/${toPlural(type)}/discover`}
        >
          {Messages.DISCOVER}
        </__TabBar.NavItem>
        <__TabBar.NavItem
          route={`/vizality/${toPlural(type)}/browse`}
          selected={tab === 'browse'}
          // onClick={() => handleTabChange('browse')}
        >
          Browse
        </__TabBar.NavItem>
      </__TabBar>
      <div className='vz-addons-list-search-options'>
        <div className='vz-addons-list-search'>
          <FilterInput
            value={query}
            onChange={handleQueryChange}
          />
        </div>
        <div className='vz-addons-list-filter-button vz-addons-list-search-options-button'>
          <Popout renderPopout={props => <SortFilterMenu {...props} />} {...popoutConfig} shouldShow={showSortFilterMenu} onRequestClose={toggleSortFilterMenu}>
            {props => (
              <Icon
                tooltip={`Sort & Filter`}
                size='20'
                tooltipPosition={sticky ? 'bottom' : 'top'}
                name='filter-alt'
                onClick={toggleSortFilterMenu}
                popoutProps={props}
              />
            )}
          </Popout>
        </div>
        <div className='vz-addons-list-tags-button vz-addons-list-search-options-button'>
          <Popout renderPopout={props => <TagsMenu type={type} {...props} />} {...popoutConfig} shouldShow={showTagsMenu} onRequestClose={toggleTagsMenu}>
            {props => (
              <Icon
                tooltip='Tags'
                name='store-tag'
                tooltipPosition={sticky ? 'bottom' : 'top'}
                onClick={toggleTagsMenu}
                popoutProps={props}
              />
            )}
          </Popout>
        </div>
        <div className='vz-addons-list-display-button vz-addons-list-search-options-button'>
          <Popout renderPopout={props => <DisplayMenu handleDisplayChange={handleDisplayChange} display={display} {...props} />} {...popoutConfig} shouldShow={showDisplayMenu} onRequestClose={toggleDisplayMenu}>
            {props => (
              <Icon
                tooltip='Display'
                tooltipPosition={sticky ? 'bottom' : 'top'}
                name={formatDisplayIconName(display)}
                onClick={toggleDisplayMenu}
                popoutProps={props}
              />
            )}
          </Popout>
        </div>
        <div className='vz-addons-list-more-button vz-addons-list-search-options-button'>
          <Popout renderPopout={props => <OverflowMenu type={type} resetSearchOptions={resetSearchOptions} {...props} />} {...popoutConfig} shouldShow={showOverflowMenu} onRequestClose={toggleOverflowMenu}>
            {props => (
              <Icon
                tooltip={Messages.MORE}
                tooltipPosition={sticky ? 'bottom' : 'top'}
                name='overflow-menu'
                onClick={toggleOverflowMenu}
                popoutProps={props}
              />
            )}
          </Popout>
        </div>
      </div>
    </StickyElement>
  );
});

/**
 * Renders the addons list.
 * @returns {React.MemoExoticComponent<function(): React.ReactElement>}
 */
export default memo(({ source, type, section, display, limit, className, community, showOptionsBar = true }) => {
  const { path, url } = useRouteMatch();
  type = type || 'plugin';
  section = section || 'installed';
  source = source || [ ...vizality.manager[toPlural(type)].values ];
  const { getSetting, updateSetting } = vizality.api.settings._fluxProps('addon-manager');
  const [ displayType, setDisplay ] = useState(display || getSetting('listDisplay', 'card'));
  const [ query, setQuery, filteredResults ] = useFilter({
    keys: [ 'manifest.name', 'manifest.author.name', 'manifest.author', 'manifest.description' ],
    data: source
  });
  const [ resultsCount, setResultsCount ] = useState(filteredResults?.length || 0);
  const forceUpdate = useForceUpdate();
  const { colorStandard } = getModule('colorStandard');

  useEffect(() => {
    console.log('test');
    /**
     * Add listeners for addons being installed and uninstalled.
     */
    vizality.manager[toPlural(type)].on(Events.VIZALITY_ADDON_INSTALL, forceUpdate);
    vizality.manager[toPlural(type)].on(Events.VIZALITY_ADDON_UNINSTALL, forceUpdate);

    /**
     * Remove listeners on dismount.
     */
    return () => {
      vizality.manager[toPlural(type)].removeListener(Events.VIZALITY_ADDON_INSTALL, forceUpdate);
      vizality.manager[toPlural(type)].removeListener(Events.VIZALITY_ADDON_UNINSTALL, forceUpdate);
    };
  }, []);

  useEffect(() => {
    console.log(section);
    console.log(path, url);
    forceUpdate();
  }, [ section ]);

  /**
   * Handles results count changes.
   * @param {number} count Results count
   * @returns {void}
   */
  const handleResultsCount = count => {
    return setResultsCount(count);
  };

  /**
   * Including these in this component so we can forceUpdate the switches.
   * There's probably a better way to do it.
   * @returns {void}
   */
  const resetSearchOptions = () => {
    return setQuery('');
  };

  /**
   * Handles the addon card Display option.
   * @param {('compact'|'cover'|'card'|'list')} display Addon card display style
   * @returns {void}
   */
  const handleDisplayChange = display => {
    updateSetting('listDisplay', display);
    return setDisplay(display);
  };

  /**
   * Handles updating the current search filter.
   * @param {string} query Search query
   * @returns {void}
   */
  const handleQueryChange = query => {
    return setQuery(typeof query === 'string' ? query : '');
  };

  return (
    <div
      className={joinClassNames('vz-addons-list', className, colorStandard)}
      vz-display={displayType}
      vz-type={type}
    >
      {showOptionsBar && (
        <StickyBar
          type={type}
          query={query}
          tab={section}
          display={displayType}
          handleQueryChange={handleQueryChange}
          handleDisplayChange={handleDisplayChange}
          resetSearchOptions={resetSearchOptions}
          getSetting={getSetting}
          updateSetting={updateSetting}
        />
      )}
      <DeferredRender
        idleTimeout={1500}
        fallback={
          <div className='vz-addons-list-inner' vz-loading=''>
            <Spinner className='vz-addons-list-spinner' />
          </div>
        }
      >
        <div className='vz-addons-list-inner'>
          <SearchResultsHeader
            query={query}
            resultsCount={resultsCount}
            limit={limit}
            type={type}
            tab={section}
          />
          <ContentBody
            filteredResults={filteredResults}
            community={community}
            display={displayType}
            type={type}
            limit={limit}
            resultsCount={resultsCount}
            handleResultsCount={handleResultsCount}
          />
        </div>
      </DeferredRender>
    </div>
  );
});
