content: import React, { Component } from 'react';
  
  import _ from 'lodash';
  
  import { Header, Main } from './components';
  
  import { searchForTerm,
           getThemeDetails } from './lib/rebrickableServices';
  
  import { pickRandomSetNum,
           hasValidImageUrl,
           removeSetsAlreadyOwned,
           removeSingleSet,
           updateUserSetStructure,
           addSingleItem } from './lib/helpers';
  
  import { getThemes,
           getSets,
           processPopularTheme,
           processUserSet,
           deleteOneSet } from './lib/jsonServerServices';
  
  import { preloadThemesOnJsonServer,
           saveNewThemeToCache } from './lib/dbSync';
  
  import { pipe } from './lib/utils';
  
  import './assets/stylesheets/globals.scss';
  import './assets/stylesheets/app.scss';
  
  export class App extends Component {
  
    state = {
      featuredSet: {},
      userSets: [],
      cachedThemes: [],
      popularThemes: [],
      statusMessage: '',
      searchTerm: '',
      searchResults: []
    };
  
    componentWillMount() {
      preloadThemesOnJsonServer();
    };
  
    componentDidMount() {
      this.initState();
    }
  
    initState = () => {
      let themes = getThemes();
      let sets = getSets();
  
      Promise.all([themes, sets]).then(results => {
        let popularThemes = _.map(results[0], theme => {
          return processPopularTheme(theme.theme_id, theme.name);
        });
  
        Promise.all(popularThemes).then(data => {
          this.setState({
            cachedThemes: results[0],
            userSets: results[1],
            popularThemes: data
          });
  
          this.showAnotherSet(this.state.userSets, this.state.cachedThemes);
        });
      });
    };
  
    showStatusMessage = statusMessage => {
      this.setState({statusMessage});
  
      setTimeout(() => this.setState({statusMessage: ''}), 2000);
    };
  
    getUserSetNumList = userSets => {
      if (!_.isEmpty(userSets)) {
        return _.map(userSets, set => set.details.set_num);
      }
  
      return [];
    };
  
    getThemeSetNumList = cachedThemes => {
      if (!_.isEmpty(cachedThemes)) {
        let sets = _.map(cachedThemes, theme => theme.sets);
        sets = _.flatten(sets);
  
        return _.map(sets, set => set.details.set_num);
      }
  
      return [];
    };
    
    getSetFromThemeCache = (setNum, cachedThemes) => {
        if (_.isNil(cachedThemes) || _.isEmpty(cachedThemes)) {
          return {};
        }
  
        let cachedSets = _.map(cachedThemes, theme => theme.sets);
        cachedSets = _.flatten(cachedSets);
  
        const targetSet = _.find(cachedSets, target => {
          return target.details.set_num === setNum;
        });
  
        if (_.isNil(targetSet)) {
          return {};
        };
  
        return targetSet;        
    };
  
    getCurrentSet = (setNum) => {
      let userSets = this.state.userSets;
      let cachedThemes = this.state.cachedThemes;
      let searchResults = this.state.searchResults;
  
      let currentSet = {};
  
      const ownedSet = _.find(userSets, set => {
        return set.details.set_num === setNum;
      });
  
      if (!_.isNil(ownedSet) || !_.isEmpty(ownedSet)) {
        return _.assign(currentSet, ownedSet);
      }
  
      let targetSet = this.getSetFromThemeCache(setNum, cachedThemes);
  
      if (!_.isEmpty(targetSet)) {
        return _.assign(currentSet, targetSet);
      }
  
      let searchSet = this.getSetFromThemeCache(setNum, searchResults);
  
      if (!_.isEmpty(searchSet)) {
        return _.assign(currentSet, searchSet);
      }
  
      return currentSet;
    };
  
    showAnotherSet = (userSets, cachedThemes) => {
      const getRandomSet = pipe(removeSetsAlreadyOwned, pickRandomSetNum);
      const ownedSetNumList = this.getUserSetNumList(userSets);
      const allSetNumList = this.getThemeSetNumList(cachedThemes);
  
      let randomSet = getRandomSet(ownedSetNumList, allSetNumList);
      let featuredSet = this.getSetFromThemeCache(randomSet, cachedThemes);
  
      if (_.isNil(featuredSet) ||
          _.isEmpty(featuredSet) || 
          _.isNil(randomSet) ||
          !hasValidImageUrl(featuredSet)) {
        this.showAnotherSet(userSets, cachedThemes);
      } else {
        this.setState({
          featuredSet
        });
      }
    };
  
    updateSearchTerm = searchTerm => {
      this.setState({searchTerm});
    }
  
    searchForMyTerm = searchTerm => {
      searchForTerm(searchTerm).then(result => {
        let resultSets = updateUserSetStructure(result.results);
        let processedResults = [];
        let promiseCollection = [];
  
        _.forEach(resultSets, set => {
          if (_.isEmpty(processedResults)) {
            processedResults.push({
              theme_id: set.details.theme_id,
              name: '',
              sets: [set]
            });
  
            promiseCollection.push({
              theme_id: set.details.theme_id,
              theme_details: getThemeDetails(set.details.theme_id)
            });
          } else {
            let currentTheme = _.find(processedResults, theme => {
              return theme.theme_id === set.details.theme_id;
            });
  
            if (currentTheme) {
              let themeId = currentTheme.theme_id;
              let themeIndex = _.findIndex(processedResults, ['theme_id', themeId]);
              let promiseIndex = _.findIndex(promiseCollection, ['theme_id', themeId]);
  
              processedResults[themeIndex].sets.push(set);
  
              // handles case where >1 sets share a theme, that theme already
              // discovered, the name is unknown, but theme details search
              // promise not yet added to array
              if (_.isEmpty(currentTheme.name) && promiseIndex === -1) {
                promiseCollection.push({
                  theme_id: set.details.theme_id,
                  theme_details: getThemeDetails(set.details.theme_id)
                });
              }
            } else {
              processedResults.push({
                theme_id: set.details.theme_id,
                name: '',
                sets: [set]
              });
  
              promiseCollection.push({
                theme_id: set.details.theme_id,
                theme_details: getThemeDetails(set.details.theme_id)
              });
            }
          }
        });
  
        let promises = _.map(promiseCollection, obj => {
          return obj.theme_details;
        });
  
        Promise.all(promises).then(details => {
          let addThemeToCache = [];
  
          _.forEach(details, detail => {
            let themeName = detail.name;
            let themeId = detail.id;
  
            let index = _.findIndex(processedResults, ['theme_id', themeId]);
            let name = processedResults[index].name;
            
            if (_.isEmpty(name)) {
              processedResults[index].name = themeName;
            }
  
            let isThemeInCache = this.isThemeInCache(themeId, this.state.cachedThemes);
            if (!_.isNil(isThemeInCache) && !isThemeInCache) {
              addThemeToCache.push(saveNewThemeToCache(themeId, themeName));
            }
          });
  
          Promise.all(addThemeToCache).then(details => {
            this.initState();
          });
  
          this.setState({
            searchResults: processedResults
          });
        });
      });
    }
  
    isThemeInCache = (themeId, cachedThemes) => {
      if (_.isNil(themeId) ||
          _.isNil(cachedThemes) ||
          _.isEmpty(cachedThemes)) {
        return;
      }
  
      const targetTheme = _.find(cachedThemes, theme => {
        return theme.theme_id === themeId;
      });
  
      if (_.isNil(targetTheme)) {
        return false;
      };
  
      return true;
    }
  
    handleShowAnother = e => {
      e.preventDefault();
  
      this.showAnotherSet(this.state.userSets, this.state.cachedThemes);
    };
  
    handleAddSet = (currentSet, userSets, e) => {
      e.preventDefault();
      this.showStatusMessage('Set added to collection.');
  
      const setNum = currentSet.details.set_num;
  
      processUserSet(setNum, currentSet).then(data => {
        this.setState({
          userSets: addSingleItem(data, userSets)
        });
  
        this.showAnotherSet(this.state.userSets, this.state.cachedThemes);
      }).catch(msg => {
        this.showStatusMessage(msg);
      });
    };
  
    handleRemoveSet = (id, setNum, sets, e) => {
      e.preventDefault();
  
      deleteOneSet(id).then(data => {
        this.showStatusMessage('Set removed from collection.');
  
        this.setState({
          userSets: removeSingleSet(setNum, sets)
        });
      }).catch(msg => {
        this.showStatusMessage(msg);
      });
    };
  
    handleFindSet = e => {
      e.preventDefault();
  
      this.searchForMyTerm(this.state.searchTerm);
    };
  
    handleInputChange = searchTerm => {
      if (searchTerm.keyCode === 13) {
        this.searchForMyTerm(searchTerm.target.value);
        return;
      }
  
      this.updateSearchTerm(searchTerm.target.value);
    };
  
    render() {
      return (
        <div className="app-wrapper">
          <Header />
          <Main handleShowAnother={this.handleShowAnother}
                handleAddSet={this.handleAddSet}
                handleRemoveSet={this.handleRemoveSet}
                handleFindSet={this.handleFindSet}
                handleInputChange={this.handleInputChange}
                getCurrentSet={this.getCurrentSet}
                featuredSet={this.state.featuredSet}
                userSets={this.state.userSets}
                cachedThemes={this.state.cachedThemes}
                popularThemes={this.state.popularThemes}
                statusMessage={this.state.statusMessage}
                searchResults={this.state.searchResults} />
        </div>
      );
    }
  }
  