package at.gv.util.client.mis.usp;

import java.net.URL;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;

import javax.annotation.Resource;
import javax.net.ssl.SSLContext;
import javax.xml.datatype.DatatypeConfigurationException;
import javax.xml.namespace.QName;
import javax.xml.ws.BindingProvider;
import javax.xml.ws.WebServiceContext;
import javax.xml.ws.handler.Handler;

import org.apache.commons.lang.RandomStringUtils;
import org.apache.cxf.configuration.jsse.TLSClientParameters;
import org.apache.cxf.endpoint.Client;
import org.apache.cxf.frontend.ClientProxy;
import org.apache.cxf.transport.http.HTTPConduit;
import org.apache.cxf.transports.http.configuration.HTTPClientPolicy;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import at.gv.util.LaxHostNameVerifier;
import at.gv.util.LoggingHandler;
import at.gv.util.MiscUtil;
import at.gv.util.client.pvp.rprofile.Pvp18pSoapHandler;
import at.gv.util.config.EgovUtilConfiguration;
import at.gv.util.ex.EgovUtilException;
import at.gv.util.wsdl.mis.usp.GetMandatesPortType;
import at.gv.util.wsdl.mis.usp.GetMandatesService;
import at.gv.util.xsd.mis.usp.GetMandatesRequest;
import at.gv.util.xsd.mis.usp.GetMandatesRequest.MandateFilters;
import at.gv.util.xsd.mis.usp.GetMandatesResponse;
import at.gv.util.xsd.mis.usp.IdentificationType;
import at.gv.util.xsd.szr.pvp.PvpTokenType;

public class USPClient {

	@Resource
  WebServiceContext wsContext;

	private EgovUtilConfiguration config = null;
	private Logger log = LoggerFactory.getLogger(USPClient.class);
	private boolean logEnabled = true;
	private GetMandatesPortType uspSuche = null; 
	
	public USPClient(EgovUtilConfiguration config, boolean logEnabled) throws EgovUtilException {
		MiscUtil.assertNotNull(config, "config");
		this.config = config;
		this.logEnabled = logEnabled;
		initialize();
	}
	
	public GetMandatesResponse getMandates(String bpkType, String bpkValue, List<String> mandateFilters) {
		
		// assemble request
		GetMandatesRequest request = new GetMandatesRequest();
		String requestId = createUSPRequestId();
		log.debug("USP request id: " + requestId);
		request.setRequestId(requestId);
		Date date = new Date();
		try {
	    request.setTimestamp(MiscUtil.getXMLGregorianCalendar(date));
    } catch (DatatypeConfigurationException e) {
	    log.warn("Error initializing USP client.", e);
	    return null;
    }
		log.debug("USP time stamp: " + date.toString());
		request.setApplicationId(config.getUSPApplicationId());
		log.debug("USP application id: " + config.getUSPApplicationId());
		
		// set identification
		IdentificationType idt = new IdentificationType();
		idt.setType(bpkType);
		idt.setValue(bpkValue);
		request.setIdentification(idt);
		
		// set filters
		if (mandateFilters != null && mandateFilters.size() > 0) {
			MandateFilters mf = new MandateFilters();
			mf.getMandateFilter().addAll(mandateFilters);
			request.setMandateFilters(mf);
		}
		
		return this.uspSuche.getMandatesOperation(request);
	}
	
  @SuppressWarnings({ "rawtypes", "unchecked" })
  private void initialize() throws EgovUtilException {
		URL url = USPClient.class.getResource("/wsdl/mis/usp/mis-usp-1.0.2.wsdl");
		GetMandatesService uspService = null;
		String uspURL = null;
		uspService = new GetMandatesService(url, new QName("http://reference.e-government.gv.at/namespace/mandates/mis/usp/1.0.2/wsdl", "GetMandatesService"));
		uspSuche = uspService.getGetMandatesPort();
		if (config.isUSPTestEnvironment()) {
			log.trace("Initializing USP test configuration.");
			uspURL = config.getUSPTestEnvironmentURL(); 
		} else {
			log.trace("Initializing USP productive configuration.");
			uspURL = config.getUSPProductionEnvironmentURL();
		}
		log.trace("USP connection URL: " + uspURL);
		BindingProvider bindingProvider = (BindingProvider) uspSuche;
		Map<String, Object> requestContext = bindingProvider.getRequestContext();
  	requestContext.put(
        BindingProvider.ENDPOINT_ADDRESS_PROPERTY, uspURL);
		
		log.trace("Adding JAX-WS request/response trace handler.");
  	List<Handler> handlerList = bindingProvider.getBinding().getHandlerChain();
  	if (handlerList == null) {
      handlerList = new ArrayList();
    }
    LoggingHandler loggingHandler = new LoggingHandler();
    if (this.logEnabled) {
    	handlerList.add(loggingHandler);
    }
    // PV authentication
    log.trace("Adding WS-Security Header handler.");
    PvpTokenType pvpToken = config.getURPVPToken();
	  Pvp18pSoapHandler szrSOAPHandler = new Pvp18pSoapHandler();
    szrSOAPHandler.configure(pvpToken);
    handlerList.add(szrSOAPHandler);
    bindingProvider.getBinding().setHandlerChain(handlerList);

	Client client = ClientProxy.getClient(uspSuche);
	HTTPConduit http = (HTTPConduit) client.getConduit();
	HTTPClientPolicy httpClientPolicy = new HTTPClientPolicy();

    //set HTTP Client Timeout (Default Timeout 60sec)
	httpClientPolicy.setConnectionTimeout(this.config.getHTTPRequestTimeout());
	httpClientPolicy.setAllowChunking(false);
	httpClientPolicy.setReceiveTimeout(32000);	 
	http.setClient(httpClientPolicy);
	        
    // check for ssl
    if (uspURL.toLowerCase().startsWith("https")) {
    	log.trace("Using ssl for SZR client request.");
    	SSLContext sslContext = this.config.getUSPsslConfiguration().getSSLContext(false);
    	if (sslContext == null) {
			throw new EgovUtilException("SSL context from configuration is empty. Please configure an SSL context in the configuration first.");
		}
    	TLSClientParameters tlsParams = new TLSClientParameters();			
    	tlsParams.setSSLSocketFactory(sslContext.getSocketFactory());
    				
    	// check for lax hostname
    	if (this.config.getUSPsslConfiguration().useLaxHostNameVerifier()) {
    		log.trace("LaxHostnameVerifier enabled. This setting is not recommended to use.");
    		tlsParams.setHostnameVerifier(new LaxHostNameVerifier());
    	}
    	http.setTlsClientParameters(tlsParams );	
    }
  }
  
  private String createUSPRequestId() {
  	return RandomStringUtils.randomAlphanumeric(12);
  }
  
}