package com.ejie.ad74a.handler;

import java.io.StringWriter;
import java.util.HashSet;
import java.util.Properties;
import java.util.Set;

import javax.xml.namespace.QName;
import javax.xml.soap.Detail;
import javax.xml.soap.Name;
import javax.xml.soap.SOAPBody;
import javax.xml.soap.SOAPElement;
import javax.xml.soap.SOAPException;
import javax.xml.soap.SOAPFactory;
import javax.xml.soap.SOAPFault;
import javax.xml.soap.SOAPMessage;
import javax.xml.transform.Result;
import javax.xml.transform.Source;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import javax.xml.ws.handler.MessageContext;
import javax.xml.ws.handler.soap.SOAPHandler;
import javax.xml.ws.handler.soap.SOAPMessageContext;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathExpression;
import javax.xml.xpath.XPathExpressionException;
import javax.xml.xpath.XPathFactory;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.w3c.dom.Document;
import org.w3c.dom.Node;

import com.ejie.ad74a.constantes.Constantes;
import com.ejie.ad74a.util.comun.UtilidadesXML;
import com.ejie.ad74a.utilProperties.config.Config;

import n38c.exe.N38API;

public class BeneficiarioHandler implements SOAPHandler<SOAPMessageContext> {

	/**
	 * Final static logger.
	 */
	private static final Logger logger = LoggerFactory.getLogger(BeneficiarioHandler.class);

	private String tokenU86XQuery = "//n38";
	private XPathExpression expression;
	private TransformerFactory transformerFactory;

	/**
	 * Constructor por defecto.
	 * 
	 * @throws XPathExpressionException XPathExpressionException
	 */
	public BeneficiarioHandler() throws XPathExpressionException {
		// inicializacion de la expresión XPATH
		XPathFactory xpFactory = XPathFactory.newInstance();
		XPath xpath = xpFactory.newXPath();
		this.expression = xpath.compile(tokenU86XQuery);
		this.transformerFactory = TransformerFactory.newInstance();
	}

	public Set<QName> getHeaders() {
		return new HashSet<QName>();
	}

	public void close(MessageContext context) {
		// Nada que hacer
	}

	/**
	 * @see javax.xml.ws.handler.Handler#handleFault(javax.xml.ws.handler.MessageContext)
	 */
	public boolean handleFault(SOAPMessageContext context) {
		return true;
	}

	/**
	 * @see javax.xml.ws.handler.Handler#handleMessage(javax.xml.ws.handler.MessageContext)
	 */
	public boolean handleMessage(SOAPMessageContext context) {
		logger.info("BeneficiarioHandler - handleMessage");
		boolean valid = true;

		Boolean outbound = (Boolean) context.get(MessageContext.MESSAGE_OUTBOUND_PROPERTY);
		logger.info("BeneficiarioHandler - handleMessage:" + outbound);
		// solo para los mensajes entrantes
		if (outbound != null && !outbound.booleanValue()) {
			valid = false;
			try {
				Properties prop = Config.loadProperties(Constantes.CONFIG_PATH);

				String local = (String) prop.getProperty("entorno.local");

				if (local.equals("S")) {
					return true;
				}

				String tokenN38 = doExtractToken(context.getMessage().getSOAPHeader());
				logger.info("BeneficiarioHandler - token38:" + tokenN38);
				if (tokenN38 != null && !tokenN38.equals("")) {
					// verificar que la sesion es valida con el API java de
					// XLNets y que tiene autorizacion
					logger.info("BeneficiarioHandler - handleRequest - Cogiendo nodos Paso 1");
					Document tokenDocument = UtilidadesXML.getDocumentFromString(tokenN38);
					logger.info("BeneficiarioHandler - handleRequest - Cogiendo nodos Paso 1.1");
					N38API n38 = new N38API(tokenDocument);
					logger.info("BeneficiarioHandler - handleRequest - Cogiendo nodos Paso 1.2");

					Document n38ItemSesion = n38.n38ItemSesion();

					logger.info("BeneficiarioHandler - handleRequest - Cogiendo nodos Paso 1.3");

					Node nodeNologedIn = UtilidadesXML.retrieveNodeXPath(n38ItemSesion, "/n38/warning[@codigo='W009']");

					logger.info("BeneficiarioHandler - handleRequest - Cogiendo nodos Paso 2");

					Node nodeNoAuth = UtilidadesXML.retrieveNodeXPath(n38ItemSesion, "/n38/warning[@codigo='W111']");

					logger.info("BeneficiarioHandler - handleRequest - Cogiendo nodos Paso 3");

					Node node = UtilidadesXML.retrieveNodeXPath(n38ItemSesion,
							"/n38/elementos/elemento/parametro[@id='n38login']");

					logger.info("BeneficiarioHandler - handleRequest - nodeNologedIn:" + nodeNologedIn);
					logger.info("BeneficiarioHandler - handleRequest - nodeNoAuth:" + nodeNoAuth);
					logger.info("BeneficiarioHandler - handleRequest - nodeAuth:" + node);

					if (nodeNologedIn != null) {
						logger.info(
								"BeneficiarioHandler - handleRequest - El usuario logeado no tiene acceso al servicio web");
						generateSOAPErrMessage(context, "Usuario no logado NO ACCESO a servicio web");
						return false;
					} else if (nodeNoAuth != null) {
						logger.info("BeneficiarioHandler - handleRequest - El usuario no tiene autorizaci�n");
						generateSOAPErrMessage(context, "Usuario SIN AUTORIZACION");
						return false;
					} else if (node != null && com.ejie.ad74a.constantes.Constantes.USER_XLNETS_TOOLKIT
							.equalsIgnoreCase(node.getTextContent().trim())) {
						logger.info("BeneficiarioHandler - handleRequest - El usuario SI tiene autorizacion");
					} else {
						logger.info(
								"BeneficiarioHandler - handleRequest - El usuario NO tiene la funci�n de autorizacion correcta");
						generateSOAPErrMessage(context, "Usuario SIN ACCESO AL WS");
						return false;
					}
					logger.info("BeneficiarioHandler - handleRequest - FINAL");
					valid = true;
				}
			} catch (Exception e) {
				logger.error("BeneficiarioHandler - handleMessage - Excepcion:" + e.getMessage());
			}
		}
		return valid;
	}

	private void generateSOAPErrMessage(SOAPMessageContext context, String reason) {
		logger.info("BeneficiarioHandler - generateSOAPErrMessage - Inicio");
		try {
			SOAPMessage msg = context.getMessage();

			SOAPBody soapBody = msg.getSOAPPart().getEnvelope().getBody();

			soapBody.removeContents();

			SOAPFault soapFault = soapBody.addFault();
			soapFault.setFaultString(reason);
			soapFault.setFaultCode("01");

			SOAPFactory soapFactory = SOAPFactory.newInstance();

			Detail detail = soapFault.addDetail();

			Name a61tExceptionTokenName = soapFactory.createName("ad74aException");
			SOAPElement a61tExceptionTokenMsgElem = soapFactory.createElement(a61tExceptionTokenName);

			Name codigoName = soapFactory.createName("codigo");
			SOAPElement codigoMsgElem = soapFactory.createElement(codigoName);
			codigoMsgElem.addTextNode("01");
			a61tExceptionTokenMsgElem.addChildElement(codigoMsgElem);

			Name mensajeName = soapFactory.createName("mensaje");
			SOAPElement mensajeMsgElem = soapFactory.createElement(mensajeName);
			mensajeMsgElem.addTextNode(reason);
			a61tExceptionTokenMsgElem.addChildElement(mensajeMsgElem);

			detail.addChildElement(a61tExceptionTokenMsgElem);

			context.getMessage().getSOAPHeader().removeContents();

		} catch (SOAPException e) {
			logger.error("BeneficiarioHandler - generateSOAPErrMessage - Excepcion:" + e.getMessage());
		}
	}

	/**
	 * Extrae el xml correspondiente a la sesi�n de aplicaci�n.
	 * 
	 * @param node
	 * @return
	 * @throws XPathExpressionException
	 * @throws TransformerException
	 */
	private String doExtractToken(Node node) throws XPathExpressionException, TransformerException {
		logger.info("BeneficiarioHandler - doExtractToken - Inicio");
		String tokenN38 = "";

		try {
			// busqueda XPath del nodo n38
			logger.info("BeneficiarioHandler - doExtractToken - Paso 1 - node:" + node);
			Node tokenN38Node = (Node) expression.evaluate(node, XPathConstants.NODE);
			logger.info("BeneficiarioHandler - doExtractToken - Paso 2 - tokenN38Node:" + tokenN38Node);
			if (tokenN38Node != null) {
				logger.info("BeneficiarioHandler - doExtractToken - Paso 3");
				// convertimos a String el nodo del token
				Source tokenN38NodeSource = new DOMSource(tokenN38Node);
				logger.info("BeneficiarioHandler - doExtractToken - Paso 4");
				StringWriter writer = new StringWriter();
				logger.info("BeneficiarioHandler - doExtractToken - Paso 5");
				Result result = new StreamResult(writer);
				logger.info("BeneficiarioHandler - doExtractToken - Paso 6");
				transformerFactory.newTransformer().transform(tokenN38NodeSource, result);
				logger.info("BeneficiarioHandler - doExtractToken - Paso 7");
				tokenN38 = writer.toString();
				logger.info("BeneficiarioHandler - doExtractToken - Paso 8 - tokenN38:" + tokenN38);
			}
		} catch (Exception e) {
			logger.error("BeneficiarioHandler - doExtractToken - Excepcion:" + e.getMessage());
		}

		return tokenN38;
	}

}
