package org.exoplatform.social.core.storage.cache.selector;

import java.util.ArrayList;
import java.util.List;

import org.apache.commons.collections4.CollectionUtils;

import org.exoplatform.services.cache.ExoCache;
import org.exoplatform.services.cache.ObjectCacheInfo;
import org.exoplatform.social.core.space.model.Space;
import org.exoplatform.social.core.storage.cache.SocialStorageCacheService;
import org.exoplatform.social.core.storage.cache.model.data.ListSpacesData;
import org.exoplatform.social.core.storage.cache.model.key.ListSpacesKey;
import org.exoplatform.social.core.storage.cache.model.key.SpaceFilterKey;
import org.exoplatform.social.core.storage.cache.model.key.SpaceKey;
import org.exoplatform.social.core.storage.cache.model.key.SpaceType;

/**
 * Cache selector for last accessed spaces. It select all cache entries for the
 * given userId and for space type LATEST_ACCESSED or VISITED.
 */
public class LastAccessedSpacesCacheSelector extends CacheSelector<ListSpacesKey, ListSpacesData> {

  private String                    remoteId;

  private long                      templateId;

  private Space                     space;

  private SocialStorageCacheService cacheService;

  private boolean                   updateStore = true;

  public LastAccessedSpacesCacheSelector(String remoteId, Space space, SocialStorageCacheService cacheService) {
    this.remoteId = remoteId;
    this.space = space;
    this.cacheService = cacheService;
  }

  @Override
  public boolean select(ListSpacesKey listSpacesKey, ObjectCacheInfo<? extends ListSpacesData> objectCacheInfo) {
    if (listSpacesKey == null || listSpacesKey.getKey() == null) {
      return false;
    } else {
      SpaceFilterKey spaceFilterKey = listSpacesKey.getKey();
      return remoteId.equals(spaceFilterKey.getUserId())
             && SpaceType.LATEST_ACCESSED.equals(spaceFilterKey.getType());
    }
  }

  @SuppressWarnings("unchecked")
  @Override
  public void onSelect(ExoCache<? extends ListSpacesKey, ? extends ListSpacesData> exoCache,
                       ListSpacesKey listSpacesKey,
                       ObjectCacheInfo<? extends ListSpacesData> objectCacheInfo) throws Exception {
    if (objectCacheInfo != null && objectCacheInfo.get() != null) {
      ListSpacesData listSpacesData = objectCacheInfo.get();
      List<SpaceKey> ids = listSpacesData.getIds();
      if (ids != null && !ids.isEmpty()) {
        if (ids.get(0).getId() == space.getSpaceId()) {
          updateStore = false;
          return;
        } else if (listSpacesKey.getOffset() == 0
                   && SpaceType.LATEST_ACCESSED.equals(listSpacesKey.getKey().getType())
                   && (CollectionUtils.size(listSpacesKey.getKey().getTemplateIds()) != 1
                       || templateId == listSpacesKey.getKey().getTemplateIds().get(0))) {
          SpaceKey spaceKey = new SpaceKey(space.getSpaceId());
          ids = new ArrayList<>(ids);
          if (ids.contains(spaceKey)) {
            ids.remove(spaceKey);
            ids.add(0, spaceKey);
            listSpacesData.setIds(ids);
            // Update cache after value change because ISPN returns a clone of
            // object And not the real cached object
            ((ExoCache<ListSpacesKey, ListSpacesData>) exoCache).put(listSpacesKey, listSpacesData);
            // Cache enry updated, so no need to clear it
            return;
          } else if (ids.size() == listSpacesKey.getLimit()) {
            ids.remove(ids.size() - 1);
            ids.add(0, spaceKey);
            listSpacesData.setIds(ids);
            cacheService.getSpacesCountCache().remove(listSpacesKey);
            ((ExoCache<ListSpacesKey, ListSpacesData>) exoCache).put(listSpacesKey, listSpacesData);
            // Cache enry updated, so no need to clear it
            return;
          } else {
            ids.add(0, spaceKey);
            listSpacesData.setIds(ids);
            cacheService.getSpacesCountCache().remove(listSpacesKey);
            ((ExoCache<ListSpacesKey, ListSpacesData>) exoCache).put(listSpacesKey, listSpacesData);
            // Cache enry updated, so no need to clear it
            return;
          }
        }
      }
      exoCache.remove(listSpacesKey);
      cacheService.getSpacesCountCache().remove(listSpacesKey);
    }
  }

  public boolean isUpdateStore() {
    return updateStore;
  }
}
