import { Artist, ArtistSearch } from 'backend-api/models';
import { ErrorView } from 'common-v2/components';
import { Loading } from 'common-v2/types';
import { SEARCH_DEBOUNCE_TIME } from 'common/constants';
import { Typography, TYPOGRAPHY_TYPE, useManageToasts, THEME, TOAST_TYPE } from 'gdb-web-shared-components';
import { debounce } from 'lodash';
import {
  addRecentArtist,
  clearAllArtists,
  closeEditArtistsModal,
  removeArtist,
  removeRecentArtist,
  resetSearchResults,
  searchArtists,
  setArtistsSearch,
} from 'project-v2/actions';
import { EditArtistsModal } from 'project-v2/components';
import { ArtistItem, Loader } from 'project-v2/components/edit-artists-modal/components';
import { editArtistsModalSelector } from 'project-v2/selectors';
import React, { useCallback, useEffect, useMemo } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import { getErrorViewConfig } from 'reporting-v2/components/manage-campaigns-modal/transducers';
import { MAX_FEATURED_ARTISTS_COUNT } from './constants';
import { suggestedArtists } from './transducers';

interface EditArtistsModalContainerProps {
  onArtistsUpdated(artists: ArtistSearch[]): void;
}

export const EditArtistsModalContainer = React.memo(({ onArtistsUpdated }: EditArtistsModalContainerProps) => {
  const {
    isOpen,
    projectArtists,
    search,
    processing,
    recentlyAddedArtists,
    searchResults: { data, loading },
  } = useSelector(editArtistsModalSelector);
  const dispatch = useDispatch();

  const { openToast } = useManageToasts(THEME.light);

  const errorState = useMemo(() => {
    const viewConfig = getErrorViewConfig(
      <Typography type={TYPOGRAPHY_TYPE.body2}>
        An error occurred while loading Artists data.
        <br />
        Please try again
      </Typography>
    );

    return <ErrorView config={viewConfig} />;
  }, []);

  const closeEditArtists = useCallback(() => {
    dispatch(closeEditArtistsModal());
  }, [dispatch]);

  const onSearchUpdated = useCallback(
    (event: React.ChangeEvent<HTMLInputElement>) => dispatch(setArtistsSearch(event.target.value)),
    [dispatch]
  );

  const onCleanSearch = useCallback(() => {
    dispatch(setArtistsSearch(''));
    dispatch(resetSearchResults());
  }, [dispatch]);

  const onAddArtist = useCallback(
    (artist: Artist) => {
      dispatch(addRecentArtist(artist));
      dispatch(setArtistsSearch(''));
      dispatch(resetSearchResults());
    },
    [dispatch]
  );

  const onClearAll = useCallback(() => {
    dispatch(clearAllArtists());
  }, [dispatch]);

  const onApplyChanges = useCallback(() => {
    onArtistsUpdated([...recentlyAddedArtists, ...projectArtists]);
    closeEditArtists();
  }, [closeEditArtists, onArtistsUpdated, projectArtists, recentlyAddedArtists]);

  const projectArtistsNodes = useMemo(
    () =>
      projectArtists.map(artist => (
        <ArtistItem
          key={artist.id}
          isDisabled={processing === Loading.Started}
          artist={artist}
          onDelete={() => dispatch(removeArtist(artist))}
        />
      )),
    [projectArtists, processing, dispatch]
  );

  const recentlyAddedArtistsNodes = useMemo(() => {
    return recentlyAddedArtists.map(artist => (
      <ArtistItem
        key={artist.id}
        isDisabled={processing === Loading.Started}
        artist={artist}
        onDelete={() => dispatch(removeRecentArtist(artist))}
      />
    ));
  }, [dispatch, recentlyAddedArtists, processing]);

  const suggestedArtist = useMemo(() => suggestedArtists(data, projectArtists, recentlyAddedArtists), [
    data,
    projectArtists,
    recentlyAddedArtists,
  ]);

  const suggestedArtistsNodes = useMemo(() => {
    switch (loading) {
      case Loading.Idle:
      case Loading.Started:
        return [<Loader key="loader" />];
      case Loading.Finished:
        return suggestedArtist.map(artist => (
          <ArtistItem key={artist.id} artist={artist} onArtistClick={onAddArtist} />
        ));
      case Loading.Failed:
        return [errorState];
    }
  }, [loading, suggestedArtist, errorState, onAddArtist]);

  const title = `Featured Artists (${recentlyAddedArtists.length +
    projectArtists.length}/${MAX_FEATURED_ARTISTS_COUNT}) `;

  const debouncedSearch = useMemo(
    () =>
      debounce((value = '') => {
        const onFailure = () =>
          openToast({
            id: 'search-artists-failure',
            message: 'An error occurred while search for artists, please try again.',
            type: TOAST_TYPE.ERROR,
          });

        dispatch(searchArtists.request({ name: value, onFailure }));
      }, SEARCH_DEBOUNCE_TIME),
    [dispatch, openToast]
  );

  useEffect(() => {
    if (!isOpen || search.length === 0) return;

    debouncedSearch(search);
  }, [debouncedSearch, isOpen, search]);

  return (
    <EditArtistsModal
      isOpened={isOpen}
      loading={loading}
      onClose={closeEditArtists}
      onApply={onApplyChanges}
      title={title}
      addedNodes={projectArtistsNodes}
      recentNodes={recentlyAddedArtistsNodes}
      suggestedNodes={suggestedArtistsNodes}
      onClearAll={onClearAll}
      searchValue={search}
      onSearchChange={onSearchUpdated}
      clearSearch={onCleanSearch}
    />
  );
});
