package com.eactive.eai.adapter.socket2.protocol.http.servlet;

import java.net.URLDecoder;
import java.time.LocalDateTime;
import java.time.OffsetDateTime;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeFormatterBuilder;
import java.time.temporal.ChronoField;
import java.util.Enumeration;
import java.util.Iterator;
import java.util.concurrent.ConcurrentHashMap;

import org.apache.mina.common.IoSession;

import com.eactive.eai.adapter.AdapterManager;
import com.eactive.eai.adapter.AdapterVO;
import com.eactive.eai.adapter.socket2.common.Env;
import com.eactive.eai.adapter.socket2.common.ExceptionStackTrace;
import com.eactive.eai.adapter.socket2.common.TimeUtil;
import com.eactive.eai.adapter.socket2.config.SocketAdapterManager;
import com.eactive.eai.adapter.socket2.config.SocketLoggerManager;
import com.eactive.eai.adapter.socket2.protocol.ISocketAdapter;
import com.eactive.eai.adapter.socket2.protocol.http.HttpServlet;
import com.eactive.eai.adapter.socket2.protocol.http.codec.HttpRequest;
import com.eactive.eai.adapter.socket2.protocol.http.codec.HttpResponse;
import com.eactive.eai.common.util.Logger;

public class ZombieAdaptersServlet extends HttpServlet {
	static final SocketAdapterManager manager = SocketAdapterManager.getInstance();
	static Logger logger = SocketLoggerManager.getInstance().getLogger("FileLogger{NET}_HTTP");
	
	protected void doService(HttpRequest request, HttpResponse response) throws Exception {
		String filter = request.getContext();
		filter = filter.substring(filter.indexOf('/')+1);
		if ("zombies".equals(filter)) {
			filter = null;
		}
		
		String result = "";
		try {
			result = list(filter);
		} catch(Throwable e) {
			result = "<pre>" + ExceptionStackTrace.dump(e) + "</pre>";
		}
		
		if (!"".equals(request.getParameter("refresh"))) {
			response.appendBody("<meta http-equiv='refresh' content=" + request.getParameter("refresh") + "; url=" + request.getContext() + ">\n\n");
		}
		
		response.setContentType("text/html");
		response.setResponseCode(HttpResponse.HTTP_STATUS_SUCCESS);
		response.appendBody(result);
	}
	public static String getDate(String flagDate, int time) throws Exception {
		ZonedDateTime nowDateTime = ZonedDateTime.now();
		DateTimeFormatter fmt = new DateTimeFormatterBuilder()
				.appendPattern("MM-dd HH:mm:ss")
				.parseDefaulting(ChronoField.YEAR,Long.parseLong(DateTimeFormatter.ofPattern("yyyy").format(nowDateTime)))
				.toFormatter();

		LocalDateTime ldt = LocalDateTime.parse(flagDate,fmt).plusMinutes(time);
		ZonedDateTime zdt = ldt.atZone(ZoneId.systemDefault());
		return fmt.format(zdt);
	}
	public static long getParseDate(String flagDate) throws Exception {
		ZonedDateTime nowDateTime = ZonedDateTime.now();
		DateTimeFormatter fmt = new DateTimeFormatterBuilder()
				.appendPattern("MM-dd HH:mm:ss")
				.parseDefaulting(ChronoField.YEAR,Long.parseLong(DateTimeFormatter.ofPattern("yyyy").format(nowDateTime)))
				.toFormatter();
		
		LocalDateTime ldt = LocalDateTime.parse(flagDate,fmt);
		return ldt.toInstant(OffsetDateTime.now().getOffset()).toEpochMilli();
	}
	
	private String list(String filter) throws Exception {
//		boolean verbose = false;		
		StringBuilder sb = new StringBuilder();
		try{
			filter = URLDecoder.decode(filter,"UTF-8");
		}catch(Exception e){
			logger.warn("URLDecoder.decode error", e);
		}
		ConcurrentHashMap adapterGroups = (ConcurrentHashMap) SocketAdapterManager.getInstance().getSocketAdapterGroups();
		
		String prevAdapterGroupName = "";
		for (Enumeration e = adapterGroups.keys(); e.hasMoreElements();) {
			String adapterGroupName = (String) e.nextElement();    	
			ConcurrentHashMap adapters = (ConcurrentHashMap) adapterGroups.get(adapterGroupName);
			
			for (Enumeration ee = adapters.elements() ; ee.hasMoreElements() ;) {
				ISocketAdapter adapter = (ISocketAdapter) ee.nextElement();
				if(adapter == null) continue;
				if (filter != null && adapter.getContext().getAdapterName().toLowerCase().indexOf(filter.toLowerCase()) == -1
						&& adapter.getContext().getProtocol().toLowerCase().indexOf(filter.toLowerCase()) == -1
						&& String.valueOf(adapter.getContext().getPortNumber()).toLowerCase().indexOf(filter.toLowerCase()) == -1
						&& adapter.getContext().getDescription().indexOf(filter.toLowerCase()) == -1)
					continue;		    	
				
				ISocketAdapter adapter_imsi = manager.getSocketAdapter(adapterGroupName + "." + adapter.getContext().getAdapterName());
				
				if (adapter_imsi != null){
					if((adapter.getRecentErrorCount() < 3)|| ("CLIENT".equals((adapter_imsi.getContext().getSocketType())))){
						continue;
					}
					String currentTime = DateTimeFormatter.ofPattern("MM-dd HH:mm:ss").format(ZonedDateTime.now());
					String flagTime = getDate(currentTime,-10);
					String[] errors = adapter_imsi.getRecentErrors();
					String errMsg = errors[errors.length-1];
					String errTime = errMsg.substring(1,15);
					long errDate = getParseDate(errTime);
					long flagDate = getParseDate(flagTime);
					
					//10분 이내 에러난 것과, connection.limit.per.ip 에러가 아닌건 모두다 pass
					if (errDate-flagDate < 0 || errMsg.indexOf("limit") < 0){
						continue;
					}
				}
								
				if (!adapterGroupName.equals(prevAdapterGroupName)) {
					sb.append("ⓖ " + adapterGroupName);
					sb.append("\n");
				}
				
				sb.append("    ⓐ <a href='/adapter/show?adapter=" + adapterGroupName + "." + adapter.getContext().getAdapterName() + "'>");
				sb.append(adapter.getContext().getAdapterName());
				sb.append("</a>");
				
				sb.append(" (");
				if ("STARTED".equals(adapter.getState().name())) {
					sb.append(adapter.getState().name());
				} else {
					sb.append("<b>" + adapter.getState().name() + "</b>");
				}
				if (!Env.isWindows()) {
					sb.append(", " + getAdapterStatus(adapterGroupName, adapter.getContext().getAdapterName()));
				}
				sb.append(")");
				
				sb.append("  uptime:" + TimeUtil.formatDate(adapter.getUptime().getTime()));
				if (adapter.getSessionPool() != null) {
					sb.append(", pool:" + adapter.getSessionPool().remaining() + "/" + (adapter.getSessionPool().peekSize()));
				}
				sb.append(", recv:" + "<a href='/adapter/sendRecv?adapter=" + adapterGroupName + "." + adapter.getContext().getAdapterName() + "'>" + adapter.getMessageRecv() + "</a>");
				sb.append(", send:" + "<a href='/adapter/sendRecv?adapter=" + adapterGroupName + "." + adapter.getContext().getAdapterName() + "'>" + adapter.getMessageSend() + "</a>");
				if (adapter.getRecentErrorCount() > 0) {
					sb.append(", ");
					sb.append("<b><a href='/adapter/error?adapter=" + adapterGroupName + "." + adapter.getContext().getAdapterName() + "'>");
					sb.append("error:" + adapter.getRecentErrorCount());
					sb.append("</a></b>");
				} else {
					sb.append(", error:" + adapter.getRecentErrorCount());
				}
				
				sb.append("  (<a href='/adapter/start?adapter=" + adapterGroupName + "." + adapter.getContext().getAdapterName() + "'>start</a>");
				sb.append("|<a href='/adapter/stop?adapter=" + adapterGroupName + "." + adapter.getContext().getAdapterName() + "'>stop</a>)");
				
				sb.append("\n       ");
				sb.append("<font color='gray'>");
				sb.append("<b>");
				sb.append(adapter.getContext().getProtocol());
				sb.append("</b>");
				sb.append(", <b>" + adapter.getContext().getBoundUsage() + "</b>");
				sb.append(", <b>" + adapter.getContext().getSocketType() + "</b>");
				if (adapter.getContext().isClient()) {
					sb.append(" (" + adapter.getContext().getLocalHostName() + ":" + adapter.getContext().getLocalPortNumber());
					sb.append("->");
					sb.append("/" + adapter.getContext().getHostName() + ":" + adapter.getContext().getPortNumber() + ")");
				} else {
					sb.append(" (" + adapter.getContext().getHostName() + ":" + adapter.getContext().getPortNumber());
					sb.append("<-)");
				}
				if(adapter.getContext().isSync()) {
					sb.append(", <b>SYNC</b>");
				} else {
					sb.append(", <b>ASYN</b>");
				}
				if ("Y".equals(adapter.getContext().getAckSupport())) {
					sb.append(", ACK");
				} else {
					sb.append(", NO_ACK");
				}
				if(adapter.getContext().isPermanent()) {
					sb.append(", <b>PERMANENT</b>");
				} else {
					sb.append(", <b>NON_PERM</b>");
				}
				if(adapter.getContext().isBlocking()) {
					sb.append(", <b>BIO</b>");
				} else {
					sb.append(", NIO");
				}
				sb.append(", max_conn:" + (adapter.getContext().getCloseExistingConnection() ? "-" : "") + adapter.getContext().getMaxConnection());
				sb.append(", limit_per_ip:" + adapter.getContext().getConnLimitPerIp());
				sb.append(", max_thread:" + adapter.getContext().getMaxThread());
				
				switch(adapter.getContext().getTraceLevel()) {
				case 0:
					sb.append(", 0(DEFAULT)");
					break;
				case 1:
					sb.append(", 1(INFO)");
					break;
				case 2:
					sb.append(", 2(DEBUG)");
					break;
				case 3:
					sb.append(", 3(TRACE)");
					break;
				default :
				}
				
				if (adapter.getContext().isPollingUse()) {
					sb.append("\n       (");
					sb.append("<b>POLLING</b>");
					sb.append(", " + adapter.getContext().getPollingRole());
					if (adapter.getContext().isPollingOneway()) {
						sb.append(", oneway");
					}
					sb.append(", " + adapter.getContext().getPollingTimeout());
//					sb.append(", " + adapter.getContext().getPollingRequest());
//					sb.append(", " + adapter.getContext().getPollingRequestPattern());
//					sb.append(", " + adapter.getContext().getPollingReplacement());
//					sb.append(", " + adapter.getContext().getPollingResponsePattern());
					sb.append(")");
				}
				
				if ("FIXED_LENGTH_HEADER".equals(adapter.getContext().getProtocol()) 
						|| "UDP_FIXED_LENGTH_HEADER".equals(adapter.getContext().getProtocol()) 
						|| "EGSEED".equals(adapter.getContext().getProtocol())
						|| "TCSEED".equals(adapter.getContext().getProtocol())  //20101213 JGY	    		 
						|| "ENVELOP".equals(adapter.getContext().getProtocol())
						|| "XECURE".equals(adapter.getContext().getProtocol())	    		 
						|| "XECURE2".equals(adapter.getContext().getProtocol())
						|| "XECURE3".equals(adapter.getContext().getProtocol())) {
					sb.append("\n       (");
					sb.append("type=" + adapter.getContext().getLlFieldType());
					sb.append(", offset=" + adapter.getContext().getLlFieldOffset());
					sb.append(", length=" + adapter.getContext().getLlFieldLength());
					sb.append(", ll.field.include=" + adapter.getContext().getLlFieldInclude());
					sb.append(", ll.offset.exclude=" + adapter.getContext().getLlOffsetExclude());
					sb.append(", ll.include=" + adapter.getContext().getLlInclude());
					sb.append(")");
				}
				
				if ("FIXED_LENGTH_PAYLOAD".equals(adapter.getContext().getProtocol())
						|| "INISAFE_PAYLOAD".equals(adapter.getContext().getProtocol())	          // 20100515 JGY
						|| "UDP_FIXED_LENGTH_PAYLOAD".equals(adapter.getContext().getProtocol())) {
					sb.append("\n       (");
					sb.append("send.payload.length=");
					sb.append(adapter.getContext().getSendPayloadLength());
					sb.append(", recv.payload.length=");
					sb.append(adapter.getContext().getRecvPayloadLength());
					sb.append(")");
				}
				
				if ("XECURE".equals(adapter.getContext().getProtocol())
						|| "XECURE3".equals(adapter.getContext().getProtocol())) {
					sb.append("\n       (");
					sb.append("<b>");
					sb.append(adapter.getContext().getXecureType());
					sb.append("</b>");
					sb.append(", " + adapter.getContext().getXecureConfigPath());
					sb.append(", " + adapter.getContext().getXecureRemoteCert());
					sb.append(")");
				}
				
				if ("INISAFE".equals(adapter.getContext().getProtocol())) {
					sb.append("\n       (");
					sb.append("<b>INISAFE</b>");
					sb.append(", " + adapter.getContext().getInisafeConfigPath());
					sb.append(", " + adapter.getContext().getInisafeRelatedAdapterGroups());
					sb.append(", " + adapter.getContext().getInisafeStartMessage());
					sb.append(", " + adapter.getContext().getInisafeStopMessage());
					sb.append(")");
				}
				else if ("INISAFE_2020".equals(adapter.getContext().getProtocol())) {
					sb.append("\n       (");
					sb.append("<b>INISAFE_2020</b>");
					sb.append(", " + adapter.getContext().getInisafeConfigPath());
					sb.append(", " + adapter.getContext().getInisafeRelatedAdapterGroups());
					sb.append(", " + adapter.getContext().getInisafeStartMessage());
					sb.append(", " + adapter.getContext().getInisafeStopMessage());
					sb.append(")");
				}
				
				if ("INISAFE_NATN".equals(adapter.getContext().getProtocol())) {
					sb.append("\n       (");
					sb.append("<b>INISAFE_NATN</b>");
					sb.append(", " + adapter.getContext().getInisafeConfigPath());
					sb.append(", " + adapter.getContext().getInisafeRelatedAdapterGroups());
					sb.append(", " + adapter.getContext().getInisafeStartMessage());
					sb.append(", " + adapter.getContext().getInisafeStopMessage());
					sb.append(")");
				}
				
				// JGY 20100515		    	
				if ("INISAFE_PAYLOAD".equals(adapter.getContext().getProtocol())) {
					sb.append("\n       (");
					sb.append("<b>INISAFE_PAYLOAD</b>");
					sb.append(", " + adapter.getContext().getInisafeConfigPath());
					sb.append(", " + adapter.getContext().getInisafeIdPassword());
					sb.append(")");
				}
				
				// JGY 20100423
				if ("ENVELOP".equals(adapter.getContext().getProtocol())) {
					sb.append("\n       (");
					sb.append("<b>ENVELOP</b>");
					sb.append(", " + adapter.getContext().getEnvelopSigncertPath());
					sb.append(", " + adapter.getContext().getEnvelopSignkeyPath());
					sb.append(", " + adapter.getContext().getEnvelopPassword());
					sb.append(")");
				}
				
				// JGY 20101213
				if ("TCSEED".equals(adapter.getContext().getProtocol())) {
					sb.append("\n       (");
					sb.append("<b>TCSEED</b>");
					sb.append(", " + adapter.getContext().getTcseedKeyStore());
					sb.append(")");
				}		    	
				
				if (adapter.getContext().getDescription() != null && !"".equals(adapter.getContext().getDescription().trim())) {
					sb.append("\n       (" + adapter.getContext().getDescription() + ")");
				}
				
				sb.append("</font>");
				
				sb.append("\n<font color='gray'>");
				for (Iterator iii = adapter.getSessionPool().iterator() ; iii.hasNext(); ) {
					IoSession session = (IoSession) iii.next();
					sb.append("        ");
					if (!session.isConnected())
						sb.append("●");
					else if (session.isClosing())
						sb.append("◐");
					else if (System.currentTimeMillis() - session.getLastIoTime() < 1000L)
						sb.append("⊙");
					else
						sb.append("○");
					sb.append(" Session :");
					if (session.isClosing())
						sb.append(" [CLOSING]");
					if (!session.isConnected())
						sb.append(" [NOT CONNECTED]");
					sb.append(" " + session.getLocalAddress() + (adapter.getContext().isClient() ? "->" : "<-") + session.getRemoteAddress());
					sb.append(", recv:" + session.getReadMessages());
					sb.append("(" + session.getReadBytes() + ")");
					sb.append(", send:" + session.getWrittenMessages());
					sb.append("(" + session.getWrittenBytes() + ")");
					sb.append(", created:" + TimeUtil.formatDate(session.getCreationTime()));
					sb.append(", idle:" + TimeUtil.elapsedTime(session.getLastIoTime()) + "\n");
				}
				sb.append("</font>");
				
				sb.append("\n<font color='red'>");
				for (Iterator iii = adapter.getSessionPool().checkedOutIterator() ; iii.hasNext(); ) {
					IoSession session = (IoSession) iii.next();
					sb.append("        ");
					if (!session.isConnected())
						sb.append("●");
					else if (session.isClosing())
						sb.append("◐");
					else if (System.currentTimeMillis() - session.getLastIoTime() < 1000L)
						sb.append("⊙");
					else
						sb.append("○");
					sb.append(" Session :");
					if (session.isClosing())
						sb.append(" [CLOSING]");
					if (!session.isConnected())
						sb.append(" [NOT CONNECTED]");
					sb.append(" " + session.getLocalAddress() + (adapter.getContext().isClient() ? "->" : "<-") + session.getRemoteAddress());
					sb.append(", recv:" + session.getReadMessages());
					sb.append("(" + session.getReadBytes() + ")");
					sb.append(", send:" + session.getWrittenMessages());
					sb.append("(" + session.getWrittenBytes() + ")");
					sb.append(", created:" + TimeUtil.formatDate(session.getCreationTime()));
					sb.append(", idle:" + TimeUtil.elapsedTime(session.getLastIoTime()) + "\n");
				}
				sb.append("</font>");			    
				sb.append("\n");
				
				prevAdapterGroupName = adapterGroupName;
			}
		}		
		
		return "<pre>" + sb.toString() + "</pre>";
	}
	
	protected boolean getAdapterStatus(String adapterGroupName, String adapterName) {
		AdapterManager adapterManager = AdapterManager.getInstance();
		AdapterVO vo = adapterManager.getAdapterVO(adapterGroupName, adapterName);
		if (vo != null) {
			return vo.isStatus();
		}
		return false;
	}
}
