/*
 * Copyright Avaya Inc., All Rights Reserved.
 *
 * THIS IS UNPUBLISHED PROPRIETARY SOURCE CODE OF Avaya Inc.
 *
 * The copyright notice above does not evidence any actual or intended
 * publication of such source code.
 *
 * Some third-party source code components may have been modified from their
 * original versions by Avaya Inc.
 *
 * The modifications are Copyright Avaya Inc., All Rights Reserved.
 */

package com.avaya.oceanalytics.openinterface.websocketclient.auth;

import com.avaya.oceanalytics.openinterface.websocketclient.RestClient;
import com.avaya.oceanalytics.openinterface.websocketclient.RestClientException;
import net.jodah.expiringmap.ExpirationPolicy;
import net.jodah.expiringmap.ExpiringEntryLoader;
import net.jodah.expiringmap.ExpiringMap;
import net.jodah.expiringmap.ExpiringValue;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.util.Date;
import java.util.concurrent.TimeUnit;

/**
 * Cache storing authentication tokens.
 * 
 * @author seprokof
 *
 */
@Component
public class AuthenticationTokenCache {
    private static final Logger LOGGER = LoggerFactory.getLogger(AuthenticationTokenLoader.class);

    private final RestClient restClient;
    private final ExpiringMap<UserCredentials, String> cache = ExpiringMap.builder()
            .expiringEntryLoader(new AuthenticationTokenLoader()).build();

    @Autowired
    public AuthenticationTokenCache(RestClient restClient) {
        this.restClient = restClient;
    }

    /**
     * Retrieves authentication token for the given credentials. If token is not found in cache of expired, performs
     * transparent loading.
     * 
     * @param credentials
     *            of the user asking for token. Should not be <code>null</code>
     * @return authentication token
     * @throws RestClientException
     *             will be thrown in case of any failure during authentication
     * @throws NullPointerException
     *             will be thrown if given credentials is <code>null</code>
     */
    public String getToken(UserCredentials credentials) {
        return cache.get(credentials);
    }

    private class AuthenticationTokenLoader implements ExpiringEntryLoader<UserCredentials, String> {

        @Override
        public ExpiringValue<String> load(UserCredentials key) {
            LOGGER.trace("No valid token found in cache for '{}'", key.getUsername());
            AuthenticationToken authToken = restClient.authenticate(key.getUsername(), key.getPassword());
            Long expireAt = Long.valueOf(authToken.getExpiry());
            LOGGER.trace("Auth token valid until {} obtained for {}", new Date(expireAt), key.getUsername());
            return new ExpiringValue<String>(authToken.getAuthData(), ExpirationPolicy.CREATED,
                    expireAt - System.currentTimeMillis(), TimeUnit.MILLISECONDS);
        }

    }
}
