package uk.ac.ebi.atlas.experimentimport.analyticsindex;

import uk.ac.ebi.atlas.model.analyticsindex.SolrInputDocumentInputStream;
import uk.ac.ebi.atlas.model.experiment.Experiment;
import uk.ac.ebi.atlas.solr.BioentityPropertyName;
import org.apache.solr.client.solrj.SolrClient;
import org.apache.solr.client.solrj.SolrServerException;
import org.apache.solr.client.solrj.response.UpdateResponse;
import org.apache.solr.common.SolrInputDocument;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Qualifier;
import uk.ac.ebi.atlas.profiles.IterableObjectInputStream;

import javax.inject.Inject;
import javax.inject.Named;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;

@Named
public class AnalyticsIndexerService {
    private static final Logger LOGGER = LoggerFactory.getLogger(AnalyticsIndexerService.class);

    private final SolrClient solrClient;
    private final ExperimentDataPointStreamFactory experimentDataPointStreamFactory;

    @Inject
    public AnalyticsIndexerService(@Qualifier("solrClientAnalytics") SolrClient solrClient,
                                   ExperimentDataPointStreamFactory experimentDataPointStreamFactory) {
        this.solrClient = solrClient;
        this.experimentDataPointStreamFactory = experimentDataPointStreamFactory;
    }

    public int index(
            Experiment experiment,
            Map<String, Map<BioentityPropertyName, Set<String>>> bioentityIdToProperties, int batchSize) {

        List<SolrInputDocument> toLoad = new ArrayList<>(batchSize);
        int addedIntoThisBatch = 0;
        int addedInTotal = 0;

        try (SolrInputDocumentInputStream solrInputDocumentInputStream =
                new SolrInputDocumentInputStream(
                        experimentDataPointStreamFactory.stream(experiment),
                        bioentityIdToProperties)) {

            Iterator<SolrInputDocument> it = new IterableObjectInputStream<>(solrInputDocumentInputStream).iterator();
            while (it.hasNext()) {
                while (addedIntoThisBatch < batchSize && it.hasNext()) {
                    SolrInputDocument analyticsInputDocument = it.next();
                    toLoad.add(analyticsInputDocument);
                    addedIntoThisBatch++;
                }
                if (addedIntoThisBatch > 0) {
                    UpdateResponse r = solrClient.add(toLoad);
                    LOGGER.info(
                            "Sent {} documents for {}, qTime:{}",
                            addedIntoThisBatch, experiment.getAccession(), r.getQTime());
                    addedInTotal += addedIntoThisBatch;
                    addedIntoThisBatch = 0;
                    toLoad.clear();
                }
            }

        } catch (Exception e) {
            LOGGER.error(e.getMessage(), e);
            throw new RuntimeException(e);
        }
        LOGGER.info("Finished: " + experiment.getAccession());
        return addedInTotal;
    }


    // synchronized necessary because we do an explicit commit here
    public synchronized void deleteExperimentFromIndex(String accession) {
        LOGGER.info("Deleting documents for {}", accession);
        try {
            solrClient.deleteByQuery("experiment_accession:" + accession);
            solrClient.commit();
        } catch (IOException | SolrServerException e) {
            rollBackAndPropagateException(e);
        }
        LOGGER.info("Done deleting documents for {}", accession);
    }

    public synchronized void deleteAll() {
        LOGGER.info("Deleting all documents");
        try {
            solrClient.deleteByQuery("*:*");
            solrClient.commit();
            solrClient.optimize();
        } catch (IOException | SolrServerException e) {
            rollBackAndPropagateException(e);
        }
        LOGGER.info("Done deleting all documents");
    }

    public synchronized void commitAfterAdd() {
        LOGGER.info("Committing the index");
        try {
            solrClient.commit();
        } catch (IOException | SolrServerException e) {
            rollBackAndPropagateException(e);
        }
        LOGGER.info("Index committed successfully");
    }

    public synchronized void optimize() {
        LOGGER.info("Optimizing index");
        try {
            solrClient.optimize();
        } catch (IOException | SolrServerException e) {
            rollBackAndPropagateException(e);
        }
        LOGGER.info("Index optimized successfully");
    }

    private void rollBackAndPropagateException(Exception exception) {
        try {
            LOGGER.error(exception.getMessage(), exception);
            solrClient.rollback();
            throw new RuntimeException(exception);
        } catch (IOException | SolrServerException e) {
            LOGGER.error(e.getMessage());
        }
    }
}

@@ -702,24 +702,61 @@
 OException;%0A
+import java.io.UncheckedIOException;%0A
 import java.
@@ -3487,34 +3487,95 @@
 ch (IOException 
-%7C 
+e) %7B%0A            logAndPropagateException(e);%0A        %7D catch (
 SolrServerExcept
@@ -3591,32 +3591,27 @@
             
-rollBack
+log
 AndPropagate
@@ -3612,33 +3612,50 @@
 pagateException(
-e
+new IOException(e)
 );%0A        %7D%0A   
@@ -3962,34 +3962,95 @@
 ch (IOException 
-%7C 
+e) %7B%0A            logAndPropagateException(e);%0A        %7D catch (
 SolrServerExcept
@@ -4066,32 +4066,27 @@
             
-rollBack
+log
 AndPropagate
@@ -4087,33 +4087,50 @@
 pagateException(
-e
+new IOException(e)
 );%0A        %7D%0A   
@@ -4346,34 +4346,95 @@
 ch (IOException 
-%7C 
+e) %7B%0A            logAndPropagateException(e);%0A        %7D catch (
 SolrServerExcept
@@ -4450,32 +4450,27 @@
             
-rollBack
+log
 AndPropagate
@@ -4471,33 +4471,50 @@
 pagateException(
-e
+new IOException(e)
 );%0A        %7D%0A   
@@ -4723,34 +4723,95 @@
 ch (IOException 
-%7C 
+e) %7B%0A            logAndPropagateException(e);%0A        %7D catch (
 SolrServerExcept
@@ -4831,24 +4831,19 @@
         
-rollBack
+log
 AndPropa
@@ -4848,33 +4848,50 @@
 pagateException(
-e
+new IOException(e)
 );%0A        %7D%0A   
@@ -4968,16 +4968,11 @@
 oid 
-rollBack
+log
 AndP
@@ -4989,16 +4989,18 @@
 ception(
+IO
 Exceptio
@@ -5002,24 +5002,16 @@
 eption e
-xception
 ) %7B%0A    
@@ -5011,37 +5011,23 @@
  %7B%0A     
-   try %7B%0A
        
-     
 LOGGER.e
@@ -5032,24 +5032,16 @@
 .error(e
-xception
 .getMess
@@ -5048,26 +5048,73 @@
 age(), e
-xception);
+);%0A            // Roll back not available in SolrCloud D:
 %0A       
@@ -5109,32 +5109,35 @@
 d D:%0A           
+ //
  solrClient.roll
@@ -5170,144 +5170,32 @@
 new 
-RuntimeException(exception);%0A        %7D catch (IOException %7C SolrServerException e) %7B%0A            LOGGER.error(e.getMessage());%0A        %7D
+UncheckedIOException(e);
 %0A   

Remove roll back: not available in SolrCloud