package eu.dnetlib.enabling.aas.admin;

import java.io.IOException;
import java.io.PrintWriter;
import java.util.List;

import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.log4j.Logger;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.support.WebApplicationContextUtils;

import eu.dnetlib.enabling.aas.service.A2Exception;


/**
 * A2 Administration servlet.
 * @author mhorst
 *
 */
public class AdministrationServlet extends HttpServlet {

	/**
	 * 
	 */
	private static final long serialVersionUID = -9063805316074093658L;

	private static final Logger log = Logger.getLogger(AdministrationServlet.class);
	
	private List authorizedHostsList = null;
	
	private IAdministrationService administrationBean = null;
	
	/* bean names */
	private static final String BEAN_AUTHORIZED_HOSTS_LIST = "authorizedHostsList";
	
	private static final String BEAN_ADMINISTRATION_SERVICE = "A2ServiceAdministration";
	
	/* errors */
	private static final String MSG_ACCESS_DENIED = "Access denied!";
	
	private static final String MSG_NO_AUTH_HOSTS = "No authorized hosts list defined as spring bean!";
	
	private static final String MSG_NO_ACTION_PARAMETER = "No action parameter specified!";
	
	private static final String MSG_UNSUPPORTED_ACTION_PARAMETER = "Unsupported action parameter specified!";
	
	private static final String MSG_NO_RESOURCE_PARAMETER = "No resource parameter specified!";
	
	private static final String MSG_UNSUPPORTED_RESOURCE_PARAMETER = "Unsupported resource parameter specified!";
	
	private static final String MSG_EXCEPTION_OCCURED = "Exception occured when performing administration operation!";
	
	/* infos */
	private static final String MSG_INVALIDATION_FINISHED = "Invalidation completed!";
	
//	private static final String MSG_DUMP_FINISHED = "All content retrieved!";
	
	private static final String MSG_DROP_FINISHED = "Resources deleted!";
	
	/* params */
	private static final String PARAM_ACTION = "action";
	
	private static final String PARAM_RESOURCE = "resource";
	
	/* actions */
	private static final String ACTION_INVALIDATE = "invalidate";

	private static final String ACTION_DUMP = "dump";
	
	private static final String ACTION_DROP = "drop";
	
	/* resources */
	private static final String RESOURCE_SEC_CTXS_ALL = "secCtxs";
	
	private static final String RESOURCE_SEC_CTXS_IS = "secCtxsIS";
	
	private static final String RESOURCE_SEC_CTXS_BUFF = "secCtxsBuffered";
	
	private static final String RESOURCE_SEC_PROFS = "secProfs";
	
	private static final String RESOURCE_SEC_POLICIES = "secPolicies";

	
	
	/* (non-Javadoc)
	 * @see javax.servlet.GenericServlet#init(javax.servlet.ServletConfig)
	 */
	@Override
	public void init(ServletConfig config) throws ServletException {
		super.init(config);
		if (authorizedHostsList == null) {
			WebApplicationContext appContext = WebApplicationContextUtils
					.getWebApplicationContext(config.getServletContext());
			authorizedHostsList = (List) appContext
					.getBean(BEAN_AUTHORIZED_HOSTS_LIST);
		}
		if (administrationBean == null) {
			WebApplicationContext appContext = WebApplicationContextUtils
					.getWebApplicationContext(config.getServletContext());
			administrationBean = (IAdministrationService) appContext
					.getBean(BEAN_ADMINISTRATION_SERVICE);
		}
	}
	
	/* (non-Javadoc)
	 * @see javax.servlet.http.HttpServlet#doGet(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse)
	 */
	@Override
	protected void doGet(HttpServletRequest req, HttpServletResponse res)
			throws ServletException, IOException {
		if (!isHostAuthorized(req.getRemoteHost())) {
			log.warn(MSG_ACCESS_DENIED);
			log.warn("Host: " + req.getRemoteHost() + " is not authorized!");
			res.sendError(HttpServletResponse.SC_UNAUTHORIZED,
					MSG_ACCESS_DENIED);
			return;
		}
		
		String actionValue = req.getParameter(AdministrationServlet.PARAM_ACTION);
		if (actionValue==null) {
			log.error(MSG_NO_ACTION_PARAMETER);
			res.sendError(HttpServletResponse.SC_BAD_REQUEST, MSG_NO_ACTION_PARAMETER);
			return;
		} 
		
		String resourceValue = req.getParameter(AdministrationServlet.PARAM_RESOURCE);
		if (resourceValue==null) {
			log.error(MSG_NO_RESOURCE_PARAMETER);
			res.sendError(HttpServletResponse.SC_BAD_REQUEST, MSG_NO_RESOURCE_PARAMETER);
			return;
		}
		
		if (actionValue.equals(AdministrationServlet.ACTION_INVALIDATE)) {
			if (resourceValue.equals(AdministrationServlet.RESOURCE_SEC_POLICIES)) {
				administrationBean.invalidatePolicies();
				putMessageToResponse(MSG_INVALIDATION_FINISHED,res);
				log.info("Security policies invalidation performed!");
				return;
			} else if (resourceValue.equals(AdministrationServlet.RESOURCE_SEC_PROFS)) {
				administrationBean.invalidateSecProfs();
				putMessageToResponse(MSG_INVALIDATION_FINISHED,res);
				log.info("Security profiles invalidation performed!");
				return;
			} else if (resourceValue.equals(AdministrationServlet.RESOURCE_SEC_CTXS_ALL)
					|| resourceValue.equals(AdministrationServlet.RESOURCE_SEC_CTXS_BUFF)) {
				administrationBean.invalidateSecCtxs();
				putMessageToResponse(MSG_INVALIDATION_FINISHED,res);
				log.info("Security contexts invalidation performed!");
				return;
			} else {
				log.error(MSG_UNSUPPORTED_RESOURCE_PARAMETER);
				res.sendError(HttpServletResponse.SC_BAD_REQUEST, MSG_UNSUPPORTED_RESOURCE_PARAMETER);
				return;
			}
			
		} else if (actionValue.equals(AdministrationServlet.ACTION_DUMP)) {
			if (resourceValue.equals(AdministrationServlet.RESOURCE_SEC_POLICIES)) {
				administrationBean.dumpPolicies(res.getOutputStream());
//				putMessageToResponse(MSG_DUMP_FINISHED,res);
				log.info("Security policies dumping performed!");
				return;
			} else if (resourceValue.equals(AdministrationServlet.RESOURCE_SEC_PROFS)) {
				try {
					administrationBean.dumpSecProfs(res.getOutputStream());
//					putMessageToResponse(MSG_DUMP_FINISHED,res);
					log.info("Security profiles from cache dumping performed!");
					return;
				} catch (A2Exception e) {
					log.error("Exception occured when dumping cached Security Profiles!",e);
					putMessageToResponse(MSG_EXCEPTION_OCCURED + 
							'\n' + e.getMessage(),res);
					return;
				}
			} else if (resourceValue.equals(AdministrationServlet.RESOURCE_SEC_CTXS_BUFF)) {
				try {
					administrationBean.dumpBufferedSecCtxs(res.getOutputStream());
//					putMessageToResponse(MSG_DUMP_FINISHED,res);
					log.info("Security contexts from cache dumping performed!");
					return;
				} catch (A2Exception e) {
					log.error("Exception occured when dumping cached SecCtxs!",e);
					putMessageToResponse(MSG_EXCEPTION_OCCURED + 
							'\n' + e.getMessage(),res);
					return;
				}
			} else if (resourceValue.equals(AdministrationServlet.RESOURCE_SEC_CTXS_IS)) {
				try {
					administrationBean.dumpISSecCtxs(res.getOutputStream());
//					putMessageToResponse(MSG_DUMP_FINISHED,res);
					log.info("Security contexts from IS dumping performed!");
					return;
				} catch (A2Exception e) {
					log.error("Exception occured when dumping IS SecCtxs!",e);
					putMessageToResponse(MSG_EXCEPTION_OCCURED + 
							'\n' + e.getMessage(),res);
					return;
				}
			} else {
				log.error(MSG_UNSUPPORTED_RESOURCE_PARAMETER);
				res.sendError(HttpServletResponse.SC_BAD_REQUEST, MSG_UNSUPPORTED_RESOURCE_PARAMETER);
				return;
			}
		} else if (actionValue.equals(AdministrationServlet.ACTION_DROP)) {
			if (resourceValue.equals(AdministrationServlet.RESOURCE_SEC_CTXS_ALL)) {
				try {
					administrationBean.dropSecCtxs();
					putMessageToResponse(MSG_DROP_FINISHED,res);
					log.info("All security contexts removed!");
					return;
				} catch (A2Exception e) {
					log.error("Exception occured when deleting SecCtxs!",e);
					putMessageToResponse(MSG_EXCEPTION_OCCURED + 
							'\n' + e.getMessage(),res);
					return;
				}
			} else {
				log.error(MSG_UNSUPPORTED_RESOURCE_PARAMETER);
				res.sendError(HttpServletResponse.SC_BAD_REQUEST, MSG_UNSUPPORTED_RESOURCE_PARAMETER);
				return;
			}
			
		} else {
			log.error(MSG_UNSUPPORTED_ACTION_PARAMETER);
			res.sendError(HttpServletResponse.SC_BAD_REQUEST, MSG_UNSUPPORTED_ACTION_PARAMETER);
			return;
		}
		
	}
	
	/* (non-Javadoc)
	 * @see javax.servlet.http.HttpServlet#doPost(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse)
	 */
	@Override
	protected void doPost(HttpServletRequest arg0, HttpServletResponse arg1)
			throws ServletException, IOException {
		doGet(arg0, arg1);
	}
	
	/**
	 * Puts message given as parameter to the response.
	 * 
	 * @param msg
	 * @param response
	 * @throws IOException
	 */
	private void putMessageToResponse(String msg, HttpServletResponse response)
			throws IOException {
		response.setContentType("text/plain");
		response.setCharacterEncoding("utf-8");
		PrintWriter out = response.getWriter();
		out.write(msg);
		out.write('\n');
		// out.close();
	}
	
	/**
	 * Checks if host name given as parameter is authorized.
	 * 
	 * @param host
	 * @return true if host is authorized
	 */
	private boolean isHostAuthorized(String host) {
		if (authorizedHostsList == null) {
			log.error(MSG_NO_AUTH_HOSTS);
			return false;
		} else if (authorizedHostsList.contains(host)) {
			return true;
		}
		return false;
	}
}
