package eu.dnetlib.functionality.modular.ui.objectStore;

import com.google.common.base.Function;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.gson.Gson;
import eu.dnetlib.data.objectstore.modular.ModularObjectStoreDeliver;
import eu.dnetlib.data.objectstore.modular.ModularObjectStoreService;
import eu.dnetlib.data.objectstore.modular.connector.ObjectStore;
import eu.dnetlib.data.objectstore.rmi.ObjectStoreFile;
import eu.dnetlib.data.objectstore.rmi.ObjectStoreServiceException;
import eu.dnetlib.enabling.is.lookup.rmi.ISLookUpException;
import eu.dnetlib.enabling.is.lookup.rmi.ISLookUpService;
import eu.dnetlib.enabling.locators.UniqueServiceLocator;
import eu.dnetlib.enabling.resultset.ResultSetListener;
import eu.dnetlib.functionality.modular.ui.objectStore.info.ObjectStoreInfo;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;

import java.util.List;
import java.util.Map;

/**
 * Created by Sandro La Bruzzo on 11/18/15.
 */

@Controller
public class ObjectStoreServiceInternalController {


    private static final Log log = LogFactory.getLog(ObjectStoreServiceInternalController.class);

    @Autowired
    public UniqueServiceLocator serviceLocator;


    @Autowired
    public ModularObjectStoreService objectStoreservice;

    private final Gson gson = new Gson();


    private final Function<String, ObjectStoreFile> convertFunction = new Function<String, ObjectStoreFile>() {
        @Override
        public ObjectStoreFile apply(String input) {
            return gson.fromJson(input, ObjectStoreFile.class);
        }
    };


    @RequestMapping(value = "/ui/objectStore/listObjectStores.do")
    @ResponseBody
    public List<ObjectStoreInfo> listObjectStoreInfo(final ModelMap map) throws ObjectStoreServiceException {
        final Map<String, ObjectStoreInfo> inputObjectStore = Maps.newHashMap();
        retrieveObjectStoreFromProfiles(inputObjectStore);
        retrieveObjectStoreFromService(inputObjectStore);
        return Lists.newArrayList(inputObjectStore.values());
    }


    private void retrieveObjectStoreFromService(final Map<String, ObjectStoreInfo> inputMap) {

        final List<String> stores = objectStoreservice.getFeeder().getDao().listObjectStores();
        for (final String s : stores) {
            if (!inputMap.containsKey(s)) {
                try {
                    final ObjectStoreInfo info = new ObjectStoreInfo();
                    final ObjectStore store = objectStoreservice.getObjectStoreDeliver().getDao().getObjectStore(s);
                    info.setInterpretation(store.getInterpretation());
                    info.setSize(store.getSize());
                    info.setObjectStoreId(s);
                    info.setMissingObjectStore(false);
                    inputMap.put(s, info);
                } catch (ObjectStoreServiceException e) {
                    log.error(e);
                }
            } else {
                inputMap.get(s).setMissingObjectStore(false);
            }
        }
    }


    private void retrieveObjectStoreFromProfiles(final Map<String, ObjectStoreInfo> inputMap) {
        final String query = "for $x in  collection('/db/DRIVER/ObjectStoreDSResources/ObjectStoreDSResourceType') \n" +
                "let $interpretation :=$x//OBJECTSTORE_INTERPRETATION\n" +
                "let $uri := $x//RESOURCE_URI/@value/string()  \n" +
                "let $id := $x//RESOURCE_IDENTIFIER/@value/string() \n" +
                "let $lastDate := $x//LAST_STORAGE_DATE  \n" +
                "let $size :=$x//STORE_SIZE \n" +
                "return concat($uri,'@::@',$id,'@::@',$interpretation,'@::@',$lastDate,'@::@',$size) ";


        ISLookUpService lookUpService = serviceLocator.getService(ISLookUpService.class);
        try {
            List<String> results = lookUpService.quickSearchProfile(query);
            if (results == null) return;
            for (String result : results) {
                String dataInfo[] = result.split("@::@");
                if (dataInfo != null && dataInfo.length == 5) {
                    final String webUri = dataInfo[0];
                    final String id = dataInfo[1];
                    final String interpretation = dataInfo[2];
                    final String lastDate = dataInfo[3];
                    final int size = Integer.parseInt(dataInfo[4]);
                    final ObjectStoreInfo currentInfo = new ObjectStoreInfo();
                    currentInfo.setObjectStoreId(id);
                    currentInfo.setInterpretation(interpretation);
                    currentInfo.setLastStorageDate(lastDate);
                    currentInfo.setSize(size);
                    currentInfo.setServiceURI(webUri);
                    currentInfo.setMissingProfile(false);
                    inputMap.put(id, currentInfo);
                }
            }
        } catch (ISLookUpException e) {
            log.error(String.format("%s", "Error on making query on is " + query, e));
        }
    }


    @RequestMapping(value = "/ui/objectStore/getObjectStoreInfo.do")
    @ResponseBody
    public ObjectStoreInfo getObjectStoreInfo(
            final ModelMap map, @RequestParam(value = "objId", required = true) final String objId,
            @RequestParam(value = "from", required = true) final int from,
            @RequestParam(value ="id",required = false) final String recordId) {
        try {
            final ModularObjectStoreDeliver objectStoreDeliver = objectStoreservice.getObjectStoreDeliver();
            final ObjectStore store = objectStoreDeliver.getDao().getObjectStore(objId);

            ResultSetListener rs;
            if (recordId!=null && !StringUtils.isEmpty(recordId)){
                rs = store.deliverIds(Lists.newArrayList(recordId));
            }
            else {
             rs = store.deliver((long) 0, System.currentTimeMillis());
            }
            final List<String> page = rs.getResult((1 + from), (from + 25));


            final ObjectStoreInfo info = new ObjectStoreInfo();
            info.setInterpretation(store.getInterpretation());
            info.setObjectStoreId(store.getId());
            info.setSize(store.getSize());
            info.setMissingObjectStore(false);
            info.setMissingProfile(existId(objId));
            info.setResults(Lists.transform(page, convertFunction));
            return info;
        } catch (ObjectStoreServiceException e) {
            return null;
        }
    }


    private boolean existId(final String objectStoreId) {

        ISLookUpService lookUpService = serviceLocator.getService(ISLookUpService.class);
        try {
            final String resourceProfile = lookUpService.getResourceProfile(objectStoreId);
            return resourceProfile == null;
        } catch (ISLookUpException e) {
            log.debug(objectStoreId + " not found!");
        }
        return true;
    }

}
