package life.catalogue.dao;

import life.catalogue.api.model.Dataset;
import life.catalogue.api.model.Page;
import life.catalogue.api.model.ResultPage;
import life.catalogue.api.search.DatasetSearchRequest;
import life.catalogue.common.io.DownloadUtil;
import life.catalogue.db.DatasetPageable;
import life.catalogue.db.mapper.DatasetMapper;
import life.catalogue.db.mapper.DecisionMapper;
import life.catalogue.db.mapper.EstimateMapper;
import life.catalogue.db.mapper.SectorMapper;
import life.catalogue.es.name.index.NameUsageIndexService;
import life.catalogue.img.ImageService;
import life.catalogue.img.LogoUpdateJob;
import org.apache.commons.lang3.StringUtils;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.annotation.Nullable;
import java.io.File;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.function.BiFunction;

public class DatasetDao extends EntityDao<Integer, Dataset, DatasetMapper> {
  
  @SuppressWarnings("unused")
  private static final Logger LOG = LoggerFactory.getLogger(DatasetDao.class);
  
  private final DownloadUtil downloader;
  private final ImageService imgService;
  private final BiFunction<Integer, String, File> scratchFileFunc;
  private final DatasetImportDao diDao;
  private final NameUsageIndexService indexService;
  
  public DatasetDao(SqlSessionFactory factory,
                    DownloadUtil downloader,
                    ImageService imgService,
                    DatasetImportDao diDao,
                    NameUsageIndexService indexService,
                    BiFunction<Integer, String, File> scratchFileFunc) {
    super(false, factory, DatasetMapper.class);
    this.downloader = downloader;
    this.imgService = imgService;
    this.scratchFileFunc = scratchFileFunc;
    this.diDao = diDao;
    this.indexService = indexService;
  }
  
  public ResultPage<Dataset> list(Page page) {
    return super.list(DatasetMapper.class, page);
  }
  
  public ResultPage<Dataset> search(@Nullable DatasetSearchRequest nullableRequest, @Nullable Page page) {
    page = page == null ? new Page() : page;
    final DatasetSearchRequest req = nullableRequest == null || nullableRequest.isEmpty() ? new DatasetSearchRequest() : nullableRequest;
    if (req.getSortBy() == null) {
      if (!StringUtils.isBlank(req.getQ())) {
        req.setSortBy(DatasetSearchRequest.SortBy.RELEVANCE);
      } else {
        req.setSortBy(DatasetSearchRequest.SortBy.KEY);
      }
    } else if (req.getSortBy() == DatasetSearchRequest.SortBy.RELEVANCE && StringUtils.isBlank(req.getQ())) {
      req.setQ(null);
      req.setSortBy(DatasetSearchRequest.SortBy.KEY);
    }
    
    try (SqlSession session = factory.openSession()){
      DatasetMapper dm = session.getMapper(DatasetMapper.class);
      List<Dataset> result = dm.search(req, page);
      return new ResultPage<>(page, result, () -> dm.count(req));
    }
  }
  
  @Override
  protected void deleteBefore(Integer key, Dataset old, int user, DatasetMapper mapper, SqlSession session) {
    // remove decisions, sectors and estimates
    for (Class<DatasetPageable<?>> mapperCLass : new Class[]{SectorMapper.class, DecisionMapper.class, EstimateMapper.class}) {
      session.getMapper(mapperCLass).deleteByDataset(key);
    }
    // delete data partitions
    Partitioner.delete(session, key);
    session.commit();
    // now also clear filesystem
    diDao.removeMetrics(key);
  }

  @Override
  protected void deleteAfter(Integer key, Dataset old, int user, DatasetMapper mapper, SqlSession session) {
    // clear search index asnychroneously
    CompletableFuture.supplyAsync(() -> indexService.deleteDataset(key))
            .exceptionally(e -> {
              LOG.error("Failed to delete ES docs for dataset {}", key, e.getCause());
              return 0;
            });
  }

  @Override
  protected void createAfter(Dataset obj, int user, DatasetMapper mapper, SqlSession session) {
    pullLogo(obj);
  }
  
  @Override
  protected void updateAfter(Dataset obj, Dataset old, int user, DatasetMapper mapper, SqlSession session) {
    pullLogo(obj);
  }
  
  private void pullLogo(Dataset d) {
    LogoUpdateJob.updateDatasetAsync(d, factory, downloader, scratchFileFunc, imgService);
  }
  
}

@@ -226,55 +226,16 @@
 gue.
-common.io.DownloadUtil;%0Aimport life.catalogue.d
+api.voca
 b.Da
@@ -239,24 +239,22 @@
 .Dataset
-Pageable
+Origin
 ;%0Aimport
@@ -273,79 +273,30 @@
 gue.
-db.mapper.DatasetMapper;%0Aimport life.catalogue.db.mapper.DecisionMapper
+common.io.DownloadUtil
 ;%0Aim
@@ -322,29 +322,23 @@
 .db.
-mapper.EstimateMapper
+DatasetPageable
 ;%0Aim
@@ -367,28 +367,17 @@
 .mapper.
-SectorMapper
+*
 ;%0Aimport
@@ -3952,32 +3952,129 @@
  pullLogo(obj);%0A
+    if (obj.getOrigin() == DatasetOrigin.MANAGED) %7B%0A      recreatePartition(obj.getKey());%0A    %7D%0A
   %7D%0A  %0A  @Overri
@@ -4202,28 +4202,352 @@
 ogo(obj);%0A  
-%7D%0A
   
+if (obj.getOrigin() == DatasetOrigin.MANAGED && !session.getMapper(DatasetPartitionMapper.class).exists(obj.getKey())) %7B%0A      recreatePartition(obj.getKey());%0A    %7D%0A  %7D%0A%0A  private void recreatePartition(int datasetKey) %7B%0A    Partitioner.partition(factory, datasetKey);%0A    Partitioner.indexAndAttach(factory, datasetKey);%0A  %7D
 %0A  private v

Create data partitions for managed datasets, fixes #604