package eu.dnetlib.enabling.ui.server.auth;

import java.math.BigInteger;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.List;

import javax.xml.namespace.QName;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.ws.BindingProvider;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.cxf.headers.Header;
import org.springframework.beans.factory.annotation.Required;
import org.w3c.dom.Element;

import com.google.common.collect.Lists;

import eu.dnetlib.enabling.aas.rmi.A2Service;
import eu.dnetlib.enabling.aas.rmi.AuthenticateRequest;
import eu.dnetlib.enabling.aas.rmi.AuthenticateResp;
import eu.dnetlib.enabling.aas.rmi.AuthorizeRequest;
import eu.dnetlib.enabling.aas.rmi.TypedString;
import eu.dnetlib.enabling.tools.ServiceLocator;

public class AuthenticationManagerAAS implements AuthenticationManager {

	private static final String HTTP_HEADER_SEC_CTX = "DriverCtxIdChain";
	/**
	 * Locator of A2Service.
	 */
	private ServiceLocator<A2Service> a2SLocator;
	/**
	 * Resource for which the authorization is requested.
	 */
	private String resource;
	/**
	 * Action to be performed on the resource.
	 */
	private String action;

	/**
	 * logger.
	 */
	private static final Log log = LogFactory.getLog(AuthenticationManagerAAS.class); // NOPMD by marko on 11/24/08 5:02 PM

	/**
	 * {@inheritDoc}
	 *
	 * @see eu.dnetlib.enabling.ui.server.auth.AuthenticationManager#authenticate(java.lang.String, java.lang.String)
	 */
	public Principal authenticate(final String email, final String password) {
		try {
			final A2Service aas = a2SLocator.getService();

			final AuthenticateRequest request = new AuthenticateRequest();

			final TypedString principals = this.createTypedString("login", email);
			final TypedString credentials = this.createTypedString("password", asMD5(password));

			final TypedString principalsArray[] = new TypedString[] { principals };
			final TypedString credentialsArray[] = new TypedString[] { credentials };

			request.setPrincipals(principalsArray);
			request.setCredentials(credentialsArray);

			final AuthenticateResp a2Response = aas.authenticate(request);

			if ((a2Response.getErrors() != null && a2Response.getErrors().length > 0))
				return null;

			@SuppressWarnings("unchecked")
			final String secCtxId = ((Element) ((List<Header>) ((BindingProvider) aas).getResponseContext().get(Header.HEADER_LIST)).get(0).getObject())
					.getTextContent();

			return new Principal(email, secCtxId);
		} catch (final Exception e) {
			log.error("Authentication Error: " + e.getMessage());
			return null;
		}
	}
	public boolean authorize(final Principal principal) {
		return authorize(principal, resource, action);
	}

	public boolean authorize(final Principal principal, String resource, String action) {
		log.warn("authorizing " + this + ": " + principal);

		if (principal == null)
			return false;

		try {
			final AuthorizeRequest req2 = new AuthorizeRequest();

			final DocumentBuilder builder = DocumentBuilderFactory.newInstance().newDocumentBuilder();
			final Element secCtx = builder.newDocument().createElement(HTTP_HEADER_SEC_CTX);

			secCtx.setTextContent(principal.getSecurityContext());

			req2.setResource(createTypedString(null, resource));
			req2.setAction(createTypedString(null, action));

			final A2Service aas = a2SLocator.getService();
			synchronized (aas) {
				((BindingProvider) aas).getRequestContext().put(Header.HEADER_LIST,
						Lists.newArrayList(new Header(new QName(HTTP_HEADER_SEC_CTX), secCtx)));

				return aas.authorize(req2).isAuthorized();
			}
		} catch (final Exception e) {
			log.error("Authentication Error: " + e.getMessage());
			return false;
		}
	}

	private TypedString createTypedString(final String type, final String value) {
		final TypedString s = new TypedString();

		s.setText(value);
		s.setType(type);

		return s;
	}

	private String asMD5(final String input) throws NoSuchAlgorithmException {
		final MessageDigest md = MessageDigest.getInstance("MD5");
		md.update(input.getBytes());
		final BigInteger hash = new BigInteger(1, md.digest());
		return Integer.toHexString(hash.intValue());
	}

	public ServiceLocator<A2Service> getA2SLocator() {
		return a2SLocator;
	}

	@Required
	public void setA2SLocator(final ServiceLocator<A2Service> aasLocator) {
		this.a2SLocator = aasLocator;
	}

	public String getResource() {
		return resource;
	}

	@Required
	public void setResource(final String resource) {
		this.resource = resource;
	}

	public String getAction() {
		return action;
	}

	@Required
	public void setAction(final String action) {
		this.action = action;
	}

}
