
package gov.va.med.cds.audit;

import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.dom4j.Document;
import org.dom4j.DocumentHelper;
import org.dom4j.Element;
import org.springframework.util.StringUtils;

import gov.va.med.cds.audit.persistence.AuditLogPersistenceManagerInterface;
import gov.va.med.cds.audit.persistence.CdsAuditClobStore;
import gov.va.med.cds.audit.persistence.CdsAuditClobStoreInterface;
import gov.va.med.cds.audit.persistence.PathwaysAuditClobStore;
import gov.va.med.cds.exception.AuditClobStoreException;
import gov.va.med.cds.exception.ErrorCodeEnum;

public class VhimRequestAuditLogger implements VhimRequestAuditLoggerInterface {
	private static Log logger = LogFactory.getLog(VhimRequestAuditLogger.class);
	private final String CLIENT_NAME_ELEMENT = "clientName";
	private final String CLIENT_REQUEST_TM_ELEMENT = "clientRequestInitiationTime";
	private ReadAuditFactoryInteface readAuditFactory = null;
	private String cdsAppName;
	private AuditLogPersistenceManagerInterface auditLogPersistenceManager;
	private CdsAuditClobStoreInterface auditClobStore;
	private String sendingFacilityStartElement;
	private String sendingFacilityEndElement;
	private String messageDateTimeStartElement;
	private String messageDateTimeEndElement;
	private List<String> haTemplateIdList;
	private List<String> pathwaysCreateTemplateIdList;
	private List<String> vhimWriteClientSendingFacilityIdList;

	public void auditReadRequest(String aTemplateId, String aFilterRequest, String aFilterId, String aRequestId,
			long aStartTime, String aResponse) {
		this.auditReadRequest(null, aTemplateId, aFilterRequest, aFilterId, aRequestId, aStartTime, aResponse);
	}

	public void auditReadRequest(String clientId, String templateId, String filterRequest, String filterId,
			String requestId, long startTime, String response) {
		AbstractAuditLog auditLog = readAuditFactory.createAuditLog(clientId, templateId, filterRequest, filterId,
				requestId, startTime, response);

		try {
			auditLogPersistenceManager.storeAuditLog(auditLog);
		} catch (Exception e) {
			StringBuilder sb = new StringBuilder();
			sb.append(ErrorCodeEnum.CDS_AUDIT_LOG_ERROR.getClientMessage());
			sb.append(" ");
			sb.append(e.getMessage());

			if (logger.isErrorEnabled()) {
				logger.error(
						gov.va.med.cds.util.LogMessageUtil.buildMessage(null, requestId, cdsAppName, sb.toString()));
			}
			// As per REQ R7.7.2 - Results shall be returned to the client
			// regardless of success or failure of audit.
			// REQ R7.7.3 - In case of failure of audit, an exception shall be
			// logged to CDS_APPLICATION_LOG
			// Exception is swallowed here....
			// throw new CdsAuditLogException(
			// ErrorCodeEnum.CDS_AUDIT_LOG_ERROR, msgString );
		}
	}

	public void auditWriteRequest(String templateId, String request, String requestId) {
		if (!StringUtils.hasText(templateId)) {
			templateId = "<<templateId>>";
		}

		if (!StringUtils.hasText(requestId)) {
			requestId = "<<requestId>>";
		}

		if (!StringUtils.hasText(request)) {
			request = "<<request>>";
		}

		String sendingFacility = getElementValue(request, sendingFacilityStartElement, sendingFacilityEndElement);
		if (!StringUtils.hasText(sendingFacility)) {
			sendingFacility = "<<StationNumber>>";
		}

		String messageType = "<<SendingApp>>";
		String updateDateTime = getElementValue(request, messageDateTimeStartElement, messageDateTimeEndElement);
		if (!StringUtils.hasText(updateDateTime)) {
			updateDateTime = "<<updateDateTime>>";
		}

		Document requestDoc = null;
		String clientName = null;
		String clientRequestInitiationTime = null;
		try {
			requestDoc = DocumentHelper.parseText(request);
			clientName = getElementValue(requestDoc, CLIENT_NAME_ELEMENT);
			clientRequestInitiationTime = getElementValue(requestDoc, CLIENT_REQUEST_TM_ELEMENT);
		} catch (Exception e) {
			// We still want to persist an audit log even if the request is
			// invalid
		}

		try {
			if (pathwaysCreateTemplateIdList.contains(templateId)) {
				PathwaysAuditClobStore pathwaysAuditClobStore = new PathwaysAuditClobStore(requestId, sendingFacility,
						request, updateDateTime, messageType, templateId, clientName, clientRequestInitiationTime);
				auditClobStore.persistPathwaysAuditClob(pathwaysAuditClobStore);
			} else {
				CdsAuditClobStore auditClob = new CdsAuditClobStore(requestId, sendingFacility, request, updateDateTime,
						messageType, templateId, clientName, clientRequestInitiationTime);
				auditClobStore.persistCdsAuditClob(auditClob);
			}
		} catch (Exception e) {
			StringBuilder sb = new StringBuilder();
			sb.append(ErrorCodeEnum.CDS_AUDIT_CLOB_STORE_ERROR.getClientMessage());
			sb.append(" ");
			sb.append(e.getMessage());

			String msgString = sb.toString();

			if (logger.isErrorEnabled()) {
				logger.error(gov.va.med.cds.util.LogMessageUtil.buildMessage(null, requestId, cdsAppName, msgString));
			}

			throw new AuditClobStoreException(ErrorCodeEnum.CDS_AUDIT_CLOB_STORE_ERROR, msgString);
		}
	}

	private String getElementValue(String request, String startReqEx, String endReqEx) {
		String returnString = null;

		Pattern pattern = Pattern.compile(startReqEx, Pattern.CASE_INSENSITIVE);
		Matcher matcher = pattern.matcher(request);

		int searchPosition = 0;
		if (matcher.find()) {
			searchPosition = matcher.end();
		}

		pattern = Pattern.compile(endReqEx, Pattern.CASE_INSENSITIVE);
		matcher = pattern.matcher(request);

		if (searchPosition > 0 && matcher.find(searchPosition)) {
			returnString = request.substring(searchPosition, matcher.start());
		}

		return returnString;
	}

	private String getElementValue(Document document, String element) {
		Element targetElement = (Element) document.selectSingleNode(String.format("//%s", element));
		if (targetElement != null) {
			return targetElement.getText();
		}

		return null;
	}

	public void setAuditLogPersistenceManager(AuditLogPersistenceManagerInterface auditLogPersistenceManager) {
		this.auditLogPersistenceManager = auditLogPersistenceManager;
	}

	public void setCdsAuditClobStore(CdsAuditClobStoreInterface cdsAuditClobStore) {
		this.auditClobStore = cdsAuditClobStore;
	}

	public void setSendingFacilityStartElement(String sendingFacilityStartElement) {
		this.sendingFacilityStartElement = sendingFacilityStartElement;
	}

	public void setSendingFacilityEndElement(String sendingFacilityEndElement) {
		this.sendingFacilityEndElement = sendingFacilityEndElement;
	}

	public void setMessageDateTimeStartElement(String messageDateTimeStartElement) {
		this.messageDateTimeStartElement = messageDateTimeStartElement;
	}

	public void setMessageDateTimeEndElement(String messageDateTimeEndElement) {
		this.messageDateTimeEndElement = messageDateTimeEndElement;
	}

	public void setCdsAppName(String cdsAppName) {
		this.cdsAppName = cdsAppName;
	}

	public List<String> getHaTemplateIdList() {
		return haTemplateIdList;
	}

	public void setHaTemplateIdList(List<String> haTemplateIdList) {
		this.haTemplateIdList = haTemplateIdList;
	}

	public void setReadAuditFactory(ReadAuditFactoryInteface readAuditFactory) {
		this.readAuditFactory = readAuditFactory;
	}

	public void setVhimWriteClientSendingFacilityIdList(List<String> vhimWriteClientSendingFacilityIdList) {
		this.vhimWriteClientSendingFacilityIdList = vhimWriteClientSendingFacilityIdList;
	}

	public void setPathwaysCreateTemplateIdList(List<String> pathwaysCreateTemplateIdList) {
		this.pathwaysCreateTemplateIdList = pathwaysCreateTemplateIdList;
	}
}
