package servicenow.api;

import java.io.IOException;
import java.io.StringReader;
import java.net.URI;

import org.apache.http.Header;
import org.apache.http.HttpEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpEntityEnclosingRequestBase;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpUriRequest;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.util.EntityUtils;
import org.jdom2.Document;
import org.jdom2.JDOMException;
import org.jdom2.input.SAXBuilder;
import org.slf4j.Logger;

class XmlRequest extends ServiceNowRequest {

	final Logger logger = Log.logger(this.getClass());

	final Document requestDoc;
	final HttpUriRequest request;
	
	public XmlRequest(CloseableHttpClient client, URI uri, Document requestDoc) {
		super(client, uri, getMethod(requestDoc));
		this.requestDoc = requestDoc;
		logger.debug(Log.REQUEST, uri.toString());
		// if requestDoc is null then use GET
		// this is only applicable for WSDL
		if (method == HttpMethod.GET) {
			requestText = null;
			request = new HttpGet(uri);
		}
		// otherwise use POST
		else {
			requestText = XmlFormatter.format(requestDoc);	
			HttpEntity requestEntity = new StringEntity(requestText, ContentType.TEXT_XML);
			HttpEntityEnclosingRequestBase httpPost = new HttpPost(uri);
			httpPost.setEntity(requestEntity);
			request = httpPost;
		}		
	}
	
	private static HttpMethod getMethod(Document requestDoc) {
		if (requestDoc == null) 
			return HttpMethod.GET;
		else 
			return HttpMethod.POST;
	}
	
	public Document execute() throws IOException {
		CloseableHttpResponse response = client.execute(request);		
		statusLine = response.getStatusLine();		
		int statusCode = statusLine.getStatusCode();
		HttpEntity responseEntity = response.getEntity();
		Header contentTypeHeader = responseEntity.getContentType();
		responseContentType = contentTypeHeader == null ? null : contentTypeHeader.getValue();
		responseText = EntityUtils.toString(responseEntity);
		int responseLen = responseText == null ? 0 : responseText.length();
		logger.debug(Log.RESPONSE,
			String.format("status=\"%s\" contentType=%s len=%d", 
				statusLine, responseContentType, responseLen));
		if (statusCode == 401 || statusCode == 403) {
			logger.error(Log.RESPONSE, this.dump());
			throw new InsufficientRightsException(this);
		}
		if (responseContentType == null) {
			logger.error(Log.RESPONSE, this.dump());
			throw new NoContentException(this);
		}
		// If we asked for XML and we got HTML, it must be an error page
		if ("text/html".equals(responseContentType))
			throw new InstanceUnavailableException(this);
		SAXBuilder parser = new SAXBuilder();
		Document responseDoc = null;	
		try {
			responseDoc = parser.build(new StringReader(responseText));
		} catch (JDOMException e) {
			logger.error(Log.RESPONSE, "REQUEST:\n" + requestText + "\nRESPONSE:\n" + responseText, e);
			throw new XmlParseException(uri, e);
		}
		return responseDoc;		
	}

}