package be.iminds.ilabt.jfed.highlevel.util;

import be.iminds.ilabt.jfed.highlevel.controller.Task;
import be.iminds.ilabt.jfed.highlevel.controller.TaskExecution;
import be.iminds.ilabt.jfed.highlevel.controller.TaskThread;
import be.iminds.ilabt.jfed.highlevel.model.Slice;
import be.iminds.ilabt.jfed.highlevel.model.Sliver;
import be.iminds.ilabt.jfed.highlevel.tasks.SliceAuthorityTaskFactory;
import be.iminds.ilabt.jfed.log.ResultListener;
import be.iminds.ilabt.jfed.lowlevel.api_wrapper.UserAndSliceApiWrapper;
import be.iminds.ilabt.jfed.fedmon.webapi.service.json.*;
import be.iminds.ilabt.jfed.util.GeniUrn;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import javax.inject.Inject;
import javax.inject.Provider;
import java.time.Instant;
import java.util.Collection;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;

/**
 * User: twalcari
 * Date: 6/18/2015
 * Time: 11:12
 */
public class SliceRegistryUtil {
    private static final Logger LOG = LoggerFactory.getLogger(SliceRegistryUtil.class);

    //environment parameters
    protected final UserAndSliceApiWrapper userAndSliceApiWrapper;
    protected final TaskThread tt;
    protected final Provider<SliceAuthorityTaskFactory> sliceAuthorityTaskFactoryProvider;

    @Inject
    public SliceRegistryUtil(UserAndSliceApiWrapper userAndSliceApiWrapper,
                             TaskThread tt,
                             Provider<SliceAuthorityTaskFactory> sliceAuthorityTaskFactoryProvider) {
        this.tt = tt;
        this.sliceAuthorityTaskFactoryProvider = sliceAuthorityTaskFactoryProvider;
        this.userAndSliceApiWrapper = userAndSliceApiWrapper;
    }


    /**
     * this wil create and execute a Task
     *
     * @param slivers             a collection of slivers, belonging to the same slice, and existing on the same authority
     * @param extraTaskToDependOn a task that the returned task must depend on.
     */
    public TaskExecution registerSliversAtSATaskExecution(@Nonnull Collection<Sliver> slivers,
                                                          Task extraTaskToDependOn,
                                                          ResultListener... extraResultListeners) {
        if (!userAndSliceApiWrapper.isRegisterAggregatesForSliceSupported()) {
            LOG.info("Ignoring sliver register request, as this is not supported by the SA");
            return null;
        }

        if (slivers.isEmpty()) {
            LOG.warn("Received an empty list of slivers to register. Ignoring!");
            return null;
        }

        Collection<GeniUrn> sliverUrns = slivers.stream()
                .filter(sliver -> sliver.getUrn() != null)
                .map(Sliver::getUrn)
                .collect(Collectors.toList());

        Instant sliverExpirationDate = null;
        Slice slice = null;
        Server auth = null;
        for (Sliver sliver : slivers) {
            if (sliver.getExpirationDate() != null)
                if (sliverExpirationDate == null)
                    sliverExpirationDate = sliver.getExpirationDate();
                else {
                    if (!Objects.equals(sliverExpirationDate, sliver.getExpirationDate()))
                        LOG.warn("The slivers should expire at same time, they were created with single call. " +
                                        "sliverExpirationDate={} sliver.getExpirationDate()={} for {}",
                                sliverExpirationDate, sliver.getExpirationDate(),
                                sliver.getUrnString() + sliver.getExpirationDate());
                }

            assert slice == null || slice.getUrn().equals(sliver.getSlice().getUrn()) :
                    "All slivers provided to this call should be on the same slice";
            slice = sliver.getSlice();
            assert auth == null || auth.getDefaultComponentManagerUrn().equals(sliver.getServer().getDefaultComponentManagerUrn()) :
                    "All slivers provided to this call should be on the same authority";
            auth = sliver.getServer();
        }
        assert slice != null;
        assert auth != null;
        return tt.addTask(sliceAuthorityTaskFactoryProvider.get()
                .registerSlivers(slice, auth, sliverUrns, sliverExpirationDate, extraTaskToDependOn, extraResultListeners));

    }

    public TaskExecution unregisterSliversAtSATaskExecution(@Nonnull Slice slice, @Nonnull Server auth,
                                                            ResultListener... extraResultListeners) {
        return unregisterSliversAtSATaskExecution(slice, auth, null, extraResultListeners);
    }


    public TaskExecution unregisterSliversAtSATaskExecution(@Nonnull Slice slice, @Nonnull Server auth,
                                                            @Nullable Task extraTaskToDependOn,
                                                            ResultListener... extraResultListeners) {
        List<GeniUrn> sliversToUnregister =
                slice.getSlivers().stream().filter(s -> s.getUrn() != null)
                        .filter(s -> Objects.equals(s.getUrn().getEncodedTopLevelAuthority_withoutSubAuth(), auth.getDefaultComponentManagerAsGeniUrn().getEncodedTopLevelAuthority_withoutSubAuth()))
                        .map(Sliver::getUrn)
                        .collect(Collectors.toList());

        return unregisterSliversAtSATaskExecution(slice, auth, sliversToUnregister, extraTaskToDependOn, extraResultListeners);
    }

    public TaskExecution unregisterSliversAtSATaskExecution(@Nonnull Slice slice, @Nonnull Server auth,
                                                            @Nonnull Collection<GeniUrn> sliversToUnregister,
                                                            @Nullable Task extraTaskToDependOn,
                                                            ResultListener... extraResultListeners) {
        if (!userAndSliceApiWrapper.isRegisterAggregatesForSliceSupported()) {
            LOG.info("Ignoring sliver unregister request, as this is not supported by the SA");
            return null;
        }

        if (sliversToUnregister.isEmpty()) {
            LOG.warn("Could not find any slivers of authority {} in slice {} to unregister. Skipping!",
                    auth.getDefaultComponentManagerUrn(), slice.getUrnString());
            return null;
        }


        return tt.addTask(sliceAuthorityTaskFactoryProvider.get()
                .unregisterSlivers(slice, auth, sliversToUnregister, extraTaskToDependOn, extraResultListeners));


    }

}
