package com.github.hburgmeier.jerseyoauth2.authsrv.jpa;

import java.util.List;

import javax.inject.Inject;
import javax.persistence.EntityManagerFactory;

import net.sf.ehcache.CacheManager;
import net.sf.ehcache.Ehcache;
import net.sf.ehcache.Element;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.github.hburgmeier.jerseyoauth2.api.token.InvalidTokenException;
import com.github.hburgmeier.jerseyoauth2.api.user.IUser;
import com.github.hburgmeier.jerseyoauth2.authsrv.api.IConfiguration;
import com.github.hburgmeier.jerseyoauth2.authsrv.api.client.IAuthorizedClientApp;
import com.github.hburgmeier.jerseyoauth2.authsrv.api.token.IAccessTokenInfo;
import com.github.hburgmeier.jerseyoauth2.authsrv.api.token.IAccessTokenStorageService;
import com.github.hburgmeier.jerseyoauth2.authsrv.api.token.TokenStorageException;

public class CachingAccessTokenStorage implements IAccessTokenStorageService {

	private static final Logger LOGGER = LoggerFactory.getLogger(CachingAccessTokenStorage.class);
	
	protected final DatabaseAccessTokenStorage delegate;
	protected final Ehcache tokenCache;

	@Inject
	public CachingAccessTokenStorage(EntityManagerFactory emf, IConfiguration config, CacheManager cacheManager) {
		this.delegate = new DatabaseAccessTokenStorage(emf, config);
		tokenCache = cacheManager.getEhcache("tokenCache");
		assert(tokenCache!=null);
	}

	@Override
	public IAccessTokenInfo getTokenInfoByAccessToken(String accessToken) throws InvalidTokenException {
		Element element = tokenCache.get(accessToken);
		if (element != null &&
				!element.isExpired()) {
			IAccessTokenInfo token = (IAccessTokenInfo) element.getObjectValue();
			if (!token.isExpired())
			{
				return token;
			}
		}
		IAccessTokenInfo token = delegate.getTokenInfoByAccessToken(accessToken);
		if (token!=null)
		{
			tokenCache.put(new Element(accessToken, token));
		}
		return token;
	}

	@Override
	public IAccessTokenInfo issueToken(String accessToken, String refreshToken, IAuthorizedClientApp clientApp) {
		IAccessTokenInfo token = delegate.issueToken(accessToken, refreshToken, clientApp);
		tokenCache.put(new Element(accessToken, token));
		return token;
	}

	@Override
	public IAccessTokenInfo getTokenInfoByRefreshToken(String refreshToken) throws InvalidTokenException {
		return delegate.getTokenInfoByRefreshToken(refreshToken);
	}

	@Override
	public IAccessTokenInfo refreshToken(String oldAccessToken, String newAccessToken, String newRefreshToken) throws TokenStorageException {
		tokenCache.remove(oldAccessToken);
		IAccessTokenInfo newToken = delegate.refreshToken(oldAccessToken, newAccessToken, newRefreshToken);
		tokenCache.put(new Element(newAccessToken, newToken));
		LOGGER.debug("token refreshed");
		return newToken;
	}

	@Override
	public List<IAccessTokenInfo> invalidateTokensForUser(IUser user) {
		List<IAccessTokenInfo> tokens = delegate.invalidateTokensForUser(user);
		for (IAccessTokenInfo token : tokens)
		{
			tokenCache.remove(token.getAccessToken());
			LOGGER.debug("token invalidated");
		}
		return tokens;
	}

}