package com.eactive.eai.rms.common.context;

import java.net.InetAddress;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Enumeration;
import java.util.List;
import java.util.Map;
import java.util.Properties;

import javax.annotation.PostConstruct;

import org.apache.commons.lang.StringUtils;
import org.apache.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.eactive.eai.rms.common.Version;
import com.eactive.eai.rms.common.util.ContainerUtil;
import com.eactive.eai.rms.data.entity.man.monitoringProperty.MonitoringPropertyId;
import com.eactive.eai.rms.data.entity.man.monitoringProperty.service.MonitoringPropertyGroupService;
import com.eactive.eai.rms.data.entity.man.monitoringProperty.service.MonitoringPropertyService;
import com.eactive.eai.rms.ext.djb.common.DjbPropertyHolder;
import com.eactive.eai.rms.ext.djb.common.DjbPropertyInjector;

@Service("monitoringContext")
@Transactional(transactionManager = "transactionManagerForEMS")
class MonitoringContextImpl implements MonitoringContext {

	/** The Constant logger. */
	private static final Logger logger = Logger.getLogger(MonitoringContextImpl.class);

	private static final String EAI_AGENTID = "eai.agentid";

	@Autowired
	private MonitoringContextDAO monitoringContextDAO;

	private Properties properties = new Properties();

	private String instancePropertyGroupName;

	@Autowired
	private MonitoringPropertyService monitoringPropertyService;
	
	public void refresh() {
		init();
		DjbPropertyHolder.reload();
	}

	public void refresh(String propName) {
		try {
			monitoringPropertyService
					.findById(
							new MonitoringPropertyId(MonitoringPropertyGroupService.MONITORING_PROPERTY_KEY, propName))
					.ifPresent(p -> {
						monitoringPropertyService.refresh(p);
						logger.info(propName + "=" + p.getPrpty2Val());
						this.properties.put(propName, p.getPrpty2Val());
					});
		} catch (Exception e) {
			logger.error(e.getMessage(), e);
		}
	}

	public Properties getProperties() {
		return this.properties;
	}

	@PostConstruct
	public void init() {
		try {
			String hostIp = InetAddress.getLocalHost().getHostAddress();
			this.instancePropertyGroupName = String
					.format(MonitoringPropertyGroupService.MONITORING_PROPERTY_KEY_FORMAT, hostIp);
			Properties loaded = monitoringContextDAO.getProperties(instancePropertyGroupName);
			if (loaded != null) {
				this.properties = loaded;
			}

			String eaiAgentId = System.getProperty(EAI_AGENTID);
			if (StringUtils.isBlank(eaiAgentId)) {
				eaiAgentId = properties.getProperty("ems_agent_id");
				if (StringUtils.isNotBlank(eaiAgentId)) {
					System.setProperty(EAI_AGENTID, eaiAgentId);
				}
			}

			setPortMapper(monitoringContextDAO.getPortMap());

			// DjbPropertyHolder 초기화
			DjbPropertyInjector injector = new DjbPropertyInjector(monitoringPropertyService, "Monitoring");
			DjbPropertyHolder.initialize(injector::inject);

			logger.info("SSO AgentId : " + System.getProperty(EAI_AGENTID));

		} catch (Exception e) {
			logger.fatal("MonitoringContext 를 초기화 할 수 없습니다.", e);
		}

		printProperties();
	}

	private void addAgentInfomationForPrint(StringBuilder sb, String text) {
		sb.append("# ").append(text).append("\n");
	}

	private void addAgentInfomationForPrint(StringBuilder sb, String id, String value) {
		sb.append("# ").append(id).append("\t: ").append(value).append("\n");
	}

	private void addLineForPrint(StringBuilder sb) {
		sb
				.append("###################################")
				.append("############################################")
				.append("\n");
	}

	private void printProperties() {

		String javaVersion = System.getProperty("java.version");
		String javaVender = System.getProperty("java.vendor");
		String was = ContainerUtil.getString();

		if (logger.isInfoEnabled()) {
			StringBuilder sb = new StringBuilder(1024);
			addLineForPrint(sb.append("\n"));
			addAgentInfomationForPrint(sb, Version.getVersionInfomation());
			addAgentInfomationForPrint(sb, "WAS", was);
			addAgentInfomationForPrint(sb, "JDK VENDER", javaVender);
			addAgentInfomationForPrint(sb, "JDK VERSION", javaVersion);
			addAgentInfomationForPrint(sb, "SERVER IP", instancePropertyGroupName);
			addLineForPrint(sb);
			logger.info(sb.toString());
		}

		try {
			Enumeration<Object> keysEnum = properties.keys();
			List<String> keyList = new ArrayList<>();
			while (keysEnum.hasMoreElements()) {
				keyList.add((String) keysEnum.nextElement());
			}
			Collections.sort(keyList);

			for (String key : keyList) {
				logger.info(key + "=" + properties.getProperty(key));
			}
		} catch (Exception e) {
			logger.warn("monitoringContext properties is null " + e.getMessage());
		}

	}

	private Map<String, String> portMapper = null;

	@Override
	public Map<String, String> getPortMapper() {
		return portMapper;
	}

	public void setPortMapper(Map<String, String> portMapper) {
		this.portMapper = portMapper;
	}

	public String getStringProperty(String propertyName) {
		return properties.getProperty(propertyName);
	}

	public String getStringProperty(String propertyName, String defaultString) {
		String value = getStringProperty(propertyName);
		return value != null ? value : defaultString;
	}

	public int getIntProperty(String key) {
		return getIntProperty(key, 0);
	}

	public int getIntProperty(String propertyName, int defaultInt) {
		String prop = properties.getProperty(propertyName);
		return prop == null ? defaultInt : Integer.parseInt(prop.trim());
	}

	public long getLongProperty(String propertyName) {
		String prop = properties.getProperty(propertyName);
		return prop == null ? 0 : Long.parseLong(prop.trim());
	}

	public long getLongProperty(String propertyName, long defaultLong) {
		String prop = properties.getProperty(propertyName);
		return prop == null ? defaultLong : Long.parseLong(prop.trim());
	}

	public boolean getBooleanProperty(String propertyName) {
		String prop = properties.getProperty(propertyName);
		return Boolean.parseBoolean(prop.trim());
	}

	public boolean getBooleanProperty(String propertyName, boolean defaultBoolean) {
		String prop = properties.getProperty(propertyName);
		return prop == null ? defaultBoolean : Boolean.parseBoolean(prop.trim());
	}

	@Override
	public String getProperty(String keyVal) {
		return getStringProperty(keyVal);
	}

	@Override
	public String getProperty(String keyVal, String defaultString) {
		return getStringProperty(keyVal, defaultString);
	}
}
