package be.iminds.ilabt.jfed.highlevel.jobs.states;

import be.iminds.ilabt.jfed.experiment.Experiment;
import be.iminds.ilabt.jfed.experiment.ExperimentPart;
import be.iminds.ilabt.jfed.experiment.SfaExperimentPart;
import be.iminds.ilabt.jfed.highlevel.jobs.AbstractJob;
import be.iminds.ilabt.jfed.highlevel.jobs.ExperimentPartStateSlice;
import be.iminds.ilabt.jfed.highlevel.jobs.ExperimentPartsSlicedState;
import be.iminds.ilabt.jfed.highlevel.jobs.Job;
import be.iminds.ilabt.jfed.highlevel.jobs.parts.ExperimentPartController;
import be.iminds.ilabt.jfed.highlevel.jobs.parts.ExperimentPartControllerManager;
import be.iminds.ilabt.jfed.highlevel.model.SfaModel;
import be.iminds.ilabt.jfed.highlevel.tasks.HighLevelTaskFactory;
import be.iminds.ilabt.jfed.highlevel.util.AggregateManagerWrapperFactory;
import be.iminds.ilabt.jfed.highlevel.util.ProxySocketFactoryProvider;
import be.iminds.ilabt.jfed.lowlevel.GeniUserProvider;
import be.iminds.ilabt.jfed.lowlevel.api.user_spec.UserSpec;
import be.iminds.ilabt.jfed.rspec.rspec_source.RequestRspecSource;

import javax.annotation.Nullable;
import javax.inject.Inject;
import javax.inject.Provider;
import javax.inject.Singleton;
import java.util.Collection;
import java.util.List;

/**
 * User: twalcari
 * Date: 11/27/2015
 * Time: 17:12
 */
@Singleton
public class JobStateFactory {
    private final Provider<HighLevelTaskFactory> hltfProvider;
    private final Provider<AggregateManagerWrapperFactory> aggregateManagerWrapperFactoryProvider;
    private final GeniUserProvider geniUserProvider;
    private final Provider<SfaModel> sfaModelProvider;
    private final Provider<ExperimentPartControllerManager> epcManagerProvider;
    private final ProxySocketFactoryProvider proxySocketFactoryProvider;

    @Inject
    public JobStateFactory(Provider<HighLevelTaskFactory> hltfProvider,
                           Provider<AggregateManagerWrapperFactory> aggregateManagerWrapperFactoryProvider,
                           GeniUserProvider geniUserProvider,
                           Provider<SfaModel> sfaModelProvider,
                           Provider<ExperimentPartControllerManager> epcManagerProvider,
                           ProxySocketFactoryProvider proxySocketFactoryProvider) {
        this.hltfProvider = hltfProvider;
        this.aggregateManagerWrapperFactoryProvider = aggregateManagerWrapperFactoryProvider;
        this.geniUserProvider = geniUserProvider;
        this.sfaModelProvider = sfaModelProvider;
        this.epcManagerProvider = epcManagerProvider;
        this.proxySocketFactoryProvider = proxySocketFactoryProvider;
    }

    public UpdateUntilReadyOrFailTaskState createGetStatusUntilReadyOrFailTaskState(
            Job<?> job, SfaExperimentPart experimentPart) {
        return new UpdateUntilReadyOrFailTaskState(job, experimentPart, hltfProvider.get());
    }

    public CreateUserSpecListState createCreateUserSpecListState(Experiment experiment) {
        return new CreateUserSpecListState(experiment, geniUserProvider, sfaModelProvider.get());
    }


    public AllocateSliversOnAuthorityState createAllocateSliversOnAuthorityState(
            AbstractJob<?> job, SfaExperimentPart experimentPart, List<UserSpec> userSpecs,
            @Nullable RequestRspecSource overridingRequestRspecSource) {
        return new AllocateSliversOnAuthorityState(job, experimentPart, userSpecs, overridingRequestRspecSource,
                hltfProvider.get(), aggregateManagerWrapperFactoryProvider.get());
    }

    public AllocateSliversOnAuthorityState createAllocateSliversOnAuthorityState(
            AbstractJob<?> job, SfaExperimentPart experimentPart, List<UserSpec> userSpecs) {
        return createAllocateSliversOnAuthorityState(job, experimentPart, userSpecs, null);
    }

    public ProvisionSliversOnAuthorityState createProvisionSliversOnAuthorityState(
            AbstractJob<?> job, SfaExperimentPart experimentPart, List<UserSpec> userSpecs) {
        return new ProvisionSliversOnAuthorityState(experimentPart, userSpecs, hltfProvider.get());
    }

    public StopExperimentAtAuthorityPartState createStopExperimentAtAuthorityPartState(Job<?> job, SfaExperimentPart experimentPart) {
        return new StopExperimentAtAuthorityPartState(job, experimentPart, hltfProvider.get());
    }

    public ShareLanAtAuthorityPartState createShareLanState(Job<?> job, SfaExperimentPart experimentPart, String linkClientId, String sharedLanName) {
        return new ShareLanAtAuthorityPartState(job, experimentPart, hltfProvider.get(), linkClientId, sharedLanName);
    }

    public DistributeSshKeysState createDistributeSshKeysState(Job<?> job){
        return new DistributeSshKeysState(job, geniUserProvider, proxySocketFactoryProvider);
    }
    public ExecuteAnsibleServicesState createExecuteAnsibleServicesState(Job<?> job){
        return new ExecuteAnsibleServicesState(job, geniUserProvider, proxySocketFactoryProvider);
    }


    public ExperimentPartsSlicedState<ExperimentPartStateSlice> createUpdateSshKeysState(Job<?> job, Collection<UserSpec> userSpecs) {
        return new ExperimentPartsSlicedState<ExperimentPartStateSlice>("Update SSH keys on authorities", job.getExperiment(), epcManagerProvider.get()) {
            @Override
            public <EP extends ExperimentPart> ExperimentPartStateSlice createJobSlice(ExperimentPartController<EP> controller, EP experimentPart) {
                return controller.editSshKeys(job, experimentPart, userSpecs);
            }
        };
    }

//    public ExperimentPartsSlicedState<ExperimentPartStateSlice> createStopExperimentState(Job<?> job) {
//        return new StopExperimentState(job);
//    }

    public class StopExperimentState extends ExperimentPartsSlicedState<ExperimentPartStateSlice> {
        private final Job<?> job;

        private StopExperimentState(Job<?> job) {
            super("Delete all resources", job.getExperiment(), JobStateFactory.this.epcManagerProvider.get());
            this.job = job;
        }

        @Override
        public <EP extends ExperimentPart> ExperimentPartStateSlice createJobSlice(ExperimentPartController<EP> controller, EP experimentPart) {
            return controller.stop(job, experimentPart);
        }
    }
}
