package org.gcube.application.cms.plugins.implementations;

import com.vdurmont.semver4j.Semver;
import lombok.extern.slf4j.Slf4j;
import org.bson.Document;
import org.gcube.application.cms.plugins.IndexerPluginInterface;
import org.gcube.application.cms.plugins.LifecycleManager;
import org.gcube.application.cms.plugins.MaterializationPlugin;
import org.gcube.application.cms.plugins.faults.EventException;
import org.gcube.application.cms.plugins.faults.InvalidPluginRequestException;
import org.gcube.application.cms.plugins.faults.MaterializationException;
import org.gcube.application.cms.plugins.implementations.executions.GuardedStepExecution;
import org.gcube.application.cms.plugins.reports.EventExecutionReport;
import org.gcube.application.cms.plugins.reports.StepExecutionReport;
import org.gcube.application.cms.plugins.requests.BaseRequest;
import org.gcube.application.geoportal.common.model.configuration.Configuration;
import org.gcube.application.geoportal.common.model.configuration.Index;
import org.gcube.application.geoportal.common.model.document.lifecycle.LifecycleInformation;
import org.gcube.application.geoportal.common.model.plugins.OperationDescriptor;
import org.gcube.application.geoportal.common.model.rest.ConfigurationException;
import org.gcube.application.geoportal.common.utils.Files;

import java.util.Collections;

@Slf4j
public class Default3PhaseManager extends SimpleLifeCycleManager implements LifecycleManager {

    private static class Phases {
        public static final String PENDING_APPROVAL="Pending Approval";
        public static final String PUBLISHED="Published";

    }

    private static class STEPS{
        public static final OperationDescriptor SUBMIT=new OperationDescriptor("SUBMIT-FOR-REVIEW","Submits the Draft for reviewing");
        public static final OperationDescriptor REJECT=new OperationDescriptor("REJECT-DRAFT","Rejects the submitted Draft");
        public static final OperationDescriptor APPROVE=new OperationDescriptor("APPROVE-SUBMITTED","Approves the submitted Draft");

        static {
            SUBMIT.setAppliableToPhases(Collections.singletonList(LifecycleInformation.CommonPhases.DRAFT_PHASE));
            REJECT.setAppliableToPhases(Collections.singletonList(Phases.PENDING_APPROVAL));
            APPROVE.setAppliableToPhases(Collections.singletonList(Phases.PENDING_APPROVAL));
        }
    }

    private static class PARAMETERS{
        public static final String NOTES="notes";
    }


    @Override
    protected EventExecutionReport onDeleteDocument(EventExecutionReport report) throws ConfigurationException, InvalidPluginRequestException, MaterializationException, EventException {
        report =  super.onDeleteDocument(report);
        for(IndexerPluginInterface indexer : getIndexers(report.getTheRequest()))
            report= deIndex(report,indexer,getInternalIndexParams(report.getTheRequest()));
        return report;
    }

    @Override
    protected EventExecutionReport onDeleteFileSet(EventExecutionReport theReport) throws ConfigurationException, InvalidPluginRequestException, MaterializationException, EventException {
        theReport =  super.onDeleteFileSet(theReport);
        String phase = theReport.getTheRequest().getDocument().getLifecycleInformation().getPhase();
        Document parameters = null;
        if(phase.equals(Phases.PENDING_APPROVAL))
            parameters =getInternalIndexParams(theReport.getTheRequest());
        if(phase.equals(Phases.PUBLISHED))
            parameters = getPublicIndexParams(theReport.getTheRequest());
        if(parameters!= null)
            for(IndexerPluginInterface indexer : getIndexers(theReport.getTheRequest()))
                theReport = index(theReport,indexer,getPublicIndexParams(theReport.getTheRequest()));
        return theReport;
    }

    @Override
    protected void registerSteps() {
        // register steps
        setStep(new GuardedStepExecution(STEPS.SUBMIT) {
            @Override
            protected StepExecutionReport run() throws Exception {
                // Materialize

                for(MaterializationPlugin mat : getMaterializers(theReport.getTheRequest()))
                    theReport = materializeDocument(theReport,mat,getMaterializationParameters(theReport.getTheRequest()));
                if(theReport.getToSetLifecycleInformation().getLastOperationStatus().equals(LifecycleInformation.Status.OK)){
                    // Index
                    for(IndexerPluginInterface indexer : getIndexers(theReport.getTheRequest()))
                        theReport = index(theReport,indexer,getInternalIndexParams(theReport.getTheRequest()));
                    // setPhase
                    if(theReport.getToSetLifecycleInformation().getLastOperationStatus().equals(LifecycleInformation.Status.OK))
                        theReport.getToSetLifecycleInformation().setPhase(Phases.PENDING_APPROVAL);
                }
                return theReport;
            }
        });


        setStep(new GuardedStepExecution(STEPS.APPROVE) {
            @Override
            protected StepExecutionReport run() throws Exception {
                    // Index
                for(IndexerPluginInterface indexer : getIndexers(theReport.getTheRequest()))
                    theReport = index(theReport,indexer,getPublicIndexParams(theReport.getTheRequest()));
                    // setPhase
                    if(theReport.getToSetLifecycleInformation().getLastOperationStatus().equals(LifecycleInformation.Status.OK))
                        theReport.getToSetLifecycleInformation().setPhase(Phases.PUBLISHED);
                return theReport;
            }
        });

        setStep(new GuardedStepExecution(STEPS.REJECT) {
            @Override
            protected StepExecutionReport run() throws Exception {
                if(theReport.getToSetLifecycleInformation().getLastOperationStatus().equals(LifecycleInformation.Status.OK))
                    theReport.getToSetLifecycleInformation().setPhase(LifecycleInformation.CommonPhases.DRAFT_PHASE);
                return theReport;
            }
        });
    }

    public Default3PhaseManager() {
        DESCRIPTOR.setId("DEFAULT-3PHASE");
        DESCRIPTOR.setDescription("Default 3-phase lifecycle manager. This plugin supports a simple moderated publication lifecycle.");
        DESCRIPTOR.setVersion(new Semver("1.0.0"));
        DESCRIPTOR.setLabel("Default 3-Phase");
    }




//    @Override
//    public StepExecutionReport performStep(StepExecutionRequest request) throws StepException, InvalidPluginRequestException, InvalidProfileException, ConfigurationException, InsufficientPrivileges {
//        StepExecutionReport report = super.performStep(request);
//        LifecycleInformation info=report.getToSetLifecycleInformation();
//        try {
//            if(!canInvokeStep(request.getStep(),request.getCaller(),getConfigurationFromProfile(request.getUseCaseDescriptor())))
//                throw new InsufficientPrivileges("Insufficient privileges for executing step "+request.getStep());
//
//            MaterializationPlugin plugin;
//            IndexerPluginInterface indexerPlugin;
//            plugin= (MaterializationPlugin) pluginManager.getById("SDI-Default-Materializer");
//            indexerPlugin = (IndexerPluginInterface) pluginManager.getById("SDI-Indexer-Plugin");
//
//
//
//            switch (request.getStep()) {
//                case STEPS.SUBMIT:{
//                    //TODO validation
//
//                    if(!request.getDocument().getLifecycleInformation().getPhase().equals(LifecycleInformation.CommonPhases.DRAFT_PHASE))
//                        throw new StepException("Document is not in "+LifecycleInformation.CommonPhases.DRAFT_PHASE+" phase");
//                    //Materialize layers
//                    MaterializationRequest matReq = new MaterializationRequest(request.getUseCaseDescriptor(),request.getCaller(), request.getContext(), request.getDocument());
//
//                    matReq.setDocument(request.getDocument());
//                    matReq.setUseCaseDescriptor(request.getUseCaseDescriptor());
//                    Document params = new Document();
//                    String workspace = request.getUseCaseDescriptor().getId() + request.getContext().getId();
//                    params.put("workspace", Files.fixFilename(workspace));
//
//
//                    matReq.setCallParameters(params);
//                    MaterializationReport matRep = plugin.materialize(matReq);
//
//
//                    report.setResultingDocument(matRep.getResultingDocument());
//
//
//                    switch(matRep.getStatus()){
//                        case OK : {
//                            index(report,indexerPlugin);
//                            //TODO Optional Notifications
//                            break;
//                        }
//                        case ERROR : {
//                            info.setLastOperationStatus(LifecycleInformation.Status.ERROR);
//                            matRep.getMessages().forEach(s -> info.addErrorMessage(s));
//                            break;
//                        }
//                        case WARNING : {
//                            info.setLastOperationStatus(LifecycleInformation.Status.WARNING);
//                            matRep.getMessages().forEach(s -> info.addWarningMessage(s));
//                            break;
//                        }
//                    }
//
//                }
//                break;
//                case STEPS.REJECT:{
//                    if(!request.getDocument().getLifecycleInformation().getPhase().equals(Phases.PENDING_APPROVAL))
//                        throw new StepException("Document is not in "+Phases.PENDING_APPROVAL+" phase");
//                    // TODO OPTIONAL Notification
//
//                    info.setPhase(Phases.PENDING_APPROVAL);
//                    if(request.getCallParameters()!=null&&request.getCallParameters().containsKey(PARAMETERS.NOTES))
//                    info.setNotes(request.getCallParameters().getString(PARAMETERS.NOTES));
//                    break;
//                }
//
//                case STEPS.APPROVE:{
//                    // Index-published
//                    if(!request.getDocument().getLifecycleInformation().getPhase()
//                            .equals(Phases.PENDING_APPROVAL))
//                        throw new StepException("Document is not in "+Phases.PENDING_APPROVAL+" phase");
//
//                   index(report,indexerPlugin);
//                   break;
//                }
//
//                default:
//                    throw new StepException("Invalid Step " + request.getStep());
//            }
//        }catch (StepException e){
//            throw e;
//        }catch (Throwable t){
//            log.error("Unable to perform step "+request.getStep(),t);
//            String msg = "Unable to execute Step "+request.getStep()+". Error was "+t.getMessage();
//            report.setStatus(Report.Status.ERROR);
//            report.putMessage(msg);
//            info.setLastOperationStatus(LifecycleInformation.Status.ERROR);
//            info.addErrorMessage(msg);
//        }
//        return report;
//    }


    @Override
    public Configuration getCurrentConfiguration(BaseRequest req) throws ConfigurationException {
        Configuration toReturn = super.getCurrentConfiguration(req);

        IndexerPluginInterface indexerPlugin;
        indexerPlugin = (IndexerPluginInterface) pluginManager.getById("SDI-Indexer-Plugin");
        BaseRequest indexRequest = new BaseRequest(req.getUseCaseDescriptor(),req.getCaller(),req.getContext());

        // Info on internal_index
        try {
            indexRequest.setCallParameters(getInternalIndexParams(req));
            Index internalIndex = indexerPlugin.getIndex(indexRequest);
            internalIndex.put("flag", "internal");
            toReturn.getIndexes().add(internalIndex);
        }catch(ConfigurationException e){
            toReturn.addErrorMessage("Unable to gather information on internal GIS Centroids Index : "+e.getMessage());
            log.error("Unable to gather information on internal GIS Centroids Index",e);
        }
        return toReturn;
    }

    private Document getInternalIndexParams(BaseRequest req){
        Document callParameters = new Document();

        callParameters.put("workspace", Files.fixFilename(req.getUseCaseDescriptor().getId()+"_internal_"+req.getContext().getName()));
        callParameters.put("indexName",Files.fixFilename(req.getUseCaseDescriptor().getId()+"_internal_"+req.getContext().getName()+"_centroids"));
        return callParameters;
    }
}
