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.lowlevel.AnyCredential;
import be.iminds.ilabt.jfed.lowlevel.JFedException;
import be.iminds.ilabt.jfed.lowlevel.api_wrapper.UserAndSliceApiWrapper;
import be.iminds.ilabt.jfed.util.JavaFXLogger;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.stream.Collectors;

/**
 * User: twalcari
 * Date: 6/16/2015
 * Time: 16:25
 */
public class UserCredentialTask extends Task {
    private static final Logger LOG = LoggerFactory.getLogger(UserCredentialTask.class);
    private static final long TIME_BETWEEN_RETRIES_IN_MS = 15_000;
    private static final int MAX_RETRIES = (int)(UserAndSliceApiWrapper.CREDENTIAL_EXPIRATION_MARGIN_IN_MS / TIME_BETWEEN_RETRIES_IN_MS) + 1;

    private final UserAndSliceApiWrapper w;
    private final JavaFXLogger logger;


    public UserCredentialTask(UserAndSliceApiWrapper w, JavaFXLogger logger) {
        super("Get User Credential");
        this.w = w;
        this.logger = logger;
    }

    @Override
    public void doTask(TaskExecution taskExecution) throws JFedException, InterruptedException {
        LOG.debug("Get User Credential started");

        if (tryGetUserCredential(taskExecution, 0)) {
            LOG.trace("Successfully retrieved a new user credential");
        } else {
            throw new JFedException("Could not retrieve a valid new user credential");
        }
    }

    public boolean tryGetUserCredential(TaskExecution taskExecution, int tryNumber) throws JFedException {
        if (tryNumber >= MAX_RETRIES) {
            LOG.error("Maximum number of retries ({}) exceeded to get a valid credential.", MAX_RETRIES);
            return false;
        }

        List<AnyCredential> res = w.getLocalUserCredentials(JavaFXLogger.wrap(logger, taskExecution));

        logReceivedCredentials(res);

        if (w.hasUserCredentials()) {
            assert res != null;
            assert !res.isEmpty();

            return true;
        } else {
            LOG.warn("The received credentials are not valid (long enough). Trying again later");
            try {
                Thread.sleep(TIME_BETWEEN_RETRIES_IN_MS);
            } catch (InterruptedException ignored) {
                return false;
            }

            return tryGetUserCredential(taskExecution, tryNumber + 1);
        }

    }

    private void logReceivedCredentials(List<AnyCredential> res) {
        if (LOG.isDebugEnabled()) {
            try {
                List<Date> expireDatesRecv =
                        res.stream().map(AnyCredential::getExpiresDate).collect(Collectors.toList());
                List<Date> expireDatesStored =
                        w.getCachedUserCredentialsForAM().stream().map(AnyCredential::getExpiresDate).collect(Collectors.toList());

                LOG.debug("Expire dates of received creds: {}. " +
                        "Expire dates of stored credentials: {}", expireDatesRecv, expireDatesStored);
            } catch (Throwable e) {
                LOG.warn("Exception while creating log output", e);
            }
        }
    }

    @Override
    public boolean newExecutionNeeded() {
        boolean hasUserCredentials = w.hasUserCredentials();

        //do some logging to ease debugging
        try {
            List<Date> expireDates =
                    w.getCachedUserCredentialsForAM() != null ?
                            w.getCachedUserCredentialsForAM().stream()
                                    .map(AnyCredential::getExpiresDate)
                                    .collect(Collectors.toList())
                            : null;
            LOG.info("getUserCredential newExecutionNeeded() called -> hasUserCredentials={} now={} expireDates={}",
                    hasUserCredentials, new Date(), expireDates);

        } catch (Throwable t) {
            LOG.warn("Exception in logging ignored. hasUserCredentials=" + hasUserCredentials, t);
        }
        return !hasUserCredentials;
    }

    @Override
    public List<Task> initDependsOn() {
        return Collections.emptyList();
    }
}
