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

import be.iminds.ilabt.jfed.highlevel.controller.Task;
import be.iminds.ilabt.jfed.highlevel.controller.TaskExecution;
import be.iminds.ilabt.jfed.highlevel.model.Slice;
import be.iminds.ilabt.jfed.highlevel.model.Sliver;
import be.iminds.ilabt.jfed.highlevel.util.AggregateManagerWrapperFactory;
import be.iminds.ilabt.jfed.lowlevel.JFedException;
import be.iminds.ilabt.jfed.lowlevel.api_wrapper.AggregateManagerWrapper;
import be.iminds.ilabt.jfed.lowlevel.api_wrapper.StatusDetails;
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 be.iminds.ilabt.jfed.util.JavaFXLogger;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.time.Instant;
import java.util.*;

/**
 * User: twalcari
 * Date: 6/17/2015
 * Time: 14:32
 */
public final class RenewSliversTask extends RenewTask {
    private static final Logger LOG = LoggerFactory.getLogger(RenewSliversTask.class);

    //input parameters
    private final Server auth;
    private final Collection<Sliver> slivers;
    private final boolean onlyIfNeeded;

    private final Collection<Task> extraDependencies = new ArrayList<>();

    //environment parameters
    private final AggregateManagerWrapperFactory aggregateManagerWrapperFactory;


    /**
     * Renew one or more or all slivers at the given authority.
     * <p>
     * This doesn't care about slice expiration date, it just tries to renew.
     *
     * @param slice              the slice the slivers are all in. may ot be null.
     * @param newExpirationDate  new expiration date. Not null
     * @param auth               may not be null
     * @param slivers           may be null to renew all slivers at the authority. May be list of selected slivers.
     */
    RenewSliversTask(@Nonnull Slice slice, @Nonnull Instant newExpirationDate,
                            @Nonnull Server auth, @Nullable Collection<Sliver> slivers,
                            boolean onlyIfNeeded,
                            UserAndSliceApiWrapper userAndSliceApiWrapper, AggregateManagerWrapperFactory aggregateManagerWrapperFactory,
                            JavaFXLogger logger, HighLevelTaskFactory highLevelTaskFactory) {
        super("Renew Slivers @ " + auth.getName(), slice, newExpirationDate, userAndSliceApiWrapper, logger, highLevelTaskFactory);
        this.auth = auth;
        this.slivers = slivers;
        this.onlyIfNeeded = onlyIfNeeded;

        this.aggregateManagerWrapperFactory = aggregateManagerWrapperFactory;
    }

    void addExtraDependency(Task t) {
        extraDependencies.add(t);
    }

    @Override
    protected void doTask(TaskExecution taskExecution) throws JFedException, InterruptedException {
        if (onlyIfNeeded) {
            boolean needed = false;
            Instant newExpirationDateNoSubSecond = Instant.ofEpochSecond(newExpirationDate.getEpochSecond());

            List<Sliver> sliversToRenew = new ArrayList<>();
            if (slivers == null) {
                sliversToRenew.addAll(slice.getSlivers());
            } else {
                sliversToRenew.addAll(slivers);
            }

            //if no slivers known, fall back to trying renew anyway
            if (sliversToRenew.isEmpty())
                needed = true;

            for (Sliver sliver : sliversToRenew) {
                if (sliver.getExpirationDate() != null && !Objects.equals(sliver.getExpirationDate(), newExpirationDateNoSubSecond)) {
                    LOG.debug("RenewSliversTask task needs Renew for sliver " + sliver.getUrn() + " expiring at " + sliver.getExpirationDate());
                    needed = true;
                    break;
                } else {
                    LOG.debug("RenewSliversTask task does not need renew for sliver " + sliver.getUrn() + " expiring at " + sliver.getExpirationDate());
                }
            }
            if (!needed) {
                LOG.info("RenewSliversTask task running for date " + newExpirationDate + ", but none of the " + sliversToRenew.size() + " slivers need to be renewed: will do nothing.");
                return;
            } else {
                LOG.info("RenewSliversTask task running for date " + newExpirationDate + ", and at least one sliver (of " + sliversToRenew.size() + ") needs to be renewed: will continue.");
            }
        }

        List<GeniUrn> sliverUrns = new ArrayList<>();
        if (slivers != null) {
            for (Sliver sliver : slivers) {
                if (sliver.getUrn() != null &&
                        (Objects.equals(sliver.getStatus().getGlobalStatus(), StatusDetails.SliverStatus.READY)
                                || Objects.equals(sliver.getStatus().getGlobalStatus(), StatusDetails.SliverStatus.CHANGING)
                                || Objects.equals(sliver.getStatus().getGlobalStatus(), StatusDetails.SliverStatus.FAIL)
                        ))
                    sliverUrns.add(sliver.getUrn());
                else {
                    LOG.info("Ignoring renew request for sliver {}, as it's status is {}",
                            sliver.getUrnString(), sliver.getStatus().getGlobalStatus());
                }
            }
        }


        AggregateManagerWrapper amWrapper =
                aggregateManagerWrapperFactory.getAggregateManagerWrapper(taskExecution, auth);
        amWrapper.renewSlivers(
                slice.getUrn(),
                !sliverUrns.isEmpty() ? sliverUrns : null,
                slice.getCredentials(),
                Date.from(newExpirationDate));

        if (!sliverUrns.isEmpty()) {
            updateSlivers(sliverUrns, taskExecution);
        }

    }

    @Override
    public List<Task> initDependsOn() {
        List<Task> res = new ArrayList<>();
        res.add(highLevelTaskFactory.getSliceCredential(slice));
        res.addAll(extraDependencies);
        return res;
    }
}
