package eu.dnetlib.enabling.aas.proxy;

import org.opensaml.lite.common.SAMLObject;
import org.opensaml.lite.saml2.core.Assertion;
import org.springframework.beans.factory.annotation.Required;

import pl.edu.icm.yadda.aas.audit.user.IIdExtractor;
import pl.edu.icm.yadda.aas.audit.user.IdExtractorException;
import pl.edu.icm.yadda.aas.refresher.IRefresher;
import pl.edu.icm.yadda.aas.refresher.RefresherException;
import eu.dnetlib.enabling.aas.DNetAuthenticateRequest;
import eu.dnetlib.enabling.aas.DNetAuthenticateResponse;
import eu.dnetlib.enabling.aas.IAAService;
import eu.dnetlib.enabling.aas.client.AssertionRefreshingHelper;
import eu.dnetlib.enabling.aas.holder.IDataHolder;
import eu.dnetlib.enabling.aas.xacml.ctx.DecisionType.DECISION;

/**
 * Performs assertion refreshing using predefined AAS instance.
 * Does not handle encrypted assertions.
 * @author mhorst
 *
 */
public class SimpleAssertionRefresher implements IRefresher<Assertion> {

	/**
	 * AAS to be used for assertion refreshing.
	 */
	protected IAAService aaService;
	
	/**
	 * Assertion data holder.
	 */
	protected IDataHolder<SAMLObject[]> samlDataHolder;
	
	/**
	 * User id extractor module.
	 */
	protected IIdExtractor idExtractor;

	
	/* (non-Javadoc)
	 * @see pl.edu.icm.yadda.aas.refresher.IRefresher#refresh(java.lang.Object)
	 */
	@Override
	public Assertion refresh(Assertion source) throws RefresherException {
		try {
			DNetAuthenticateRequest authnReq = AssertionRefreshingHelper.buildAssertionRefreshingRequest(
					idExtractor.extractId(source), 
					AssertionRefreshingHelper.extractSubjectValue(source));
			samlDataHolder.storeData(new SAMLObject[] {source});
			SAMLObject[] samlTokens;
			DNetAuthenticateResponse authnResp;
			try {
				authnResp = aaService.authenticate(authnReq);	
			} finally {
//				making sure user data is always retrieved
				samlTokens = samlDataHolder.getData();
			}
			if (samlTokens!=null && samlTokens.length>0) {
				return (Assertion) samlTokens[0];
			} else {
				if (authnResp.getErrors()!=null && authnResp.getErrors().length>0) {
//					returning first error as exception
					throw new RefresherException("got error in assertion " + source.getID() +
							"refreshing response: " + 
							authnResp.getErrors()[0].getErrorId() + ':' + 
							authnResp.getErrors()[0].getMessage(), authnResp.getErrors()[0].getThrowable());
					
				} else if (authnResp.getResult()!=null && authnResp.getResult().getDecision()!=null &&
						!DECISION.Permit.equals(authnResp.getResult().getDecision().getDecision())) {
//					assertion non-refreshable
					throw new RefresherException("Couldn't refresh assertion, " + source.getID() +
							"got decision: " + authnResp.getResult().getDecision().getDecision());
				} else {
//					just no assertion found in response, probably assertion not-refreshable
					throw new RefresherException("Got no assertion in AAService response, " +
							"probably assertion " + source.getID() + " wasn't refreshable!");
				}
			}
		} catch (IdExtractorException e) {
			throw new RefresherException("exception occurred when extracting " +
					"subject id from assertion " + source.getID(), e);
		}
	}

	/**
	 * Sets id extractor module.
	 * @param idExtractor
	 */
	@Required
	public void setIdExtractor(IIdExtractor idExtractor) {
		this.idExtractor = idExtractor;
	}

	/**
	 * Sets AAS to be used for assertion refreshing.
	 * @param aaService
	 */
	@Required
	public void setAaService(IAAService aaService) {
		this.aaService = aaService;
	}

	/**
	 * Sets assertion data holder.
	 * @param samlDataHolder
	 */
	@Required
	public void setSamlDataHolder(IDataHolder<SAMLObject[]> samlDataHolder) {
		this.samlDataHolder = samlDataHolder;
	}

}
