package com.eactive.eai.agent.encryption;

import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Component;

import com.eactive.eai.common.exception.ExceptionUtil;
import com.eactive.eai.common.lifecycle.Lifecycle;
import com.eactive.eai.common.lifecycle.LifecycleException;
import com.eactive.eai.common.lifecycle.LifecycleListener;
import com.eactive.eai.common.lifecycle.LifecycleSupport;
import com.eactive.eai.common.property.PropManager;
import com.eactive.eai.common.server.Keys;
import com.eactive.eai.common.util.ApplicationContextProvider;
import com.eactive.eai.common.util.Logger;
import com.eactive.ext.djb.DamoManager;
import com.eactive.ext.kjb.safedb.KjbSafedbWrapper;

@Component
public class EncryptionManager implements Lifecycle {
	static Logger logger = Logger.getLogger(Logger.LOGGER_DEFAULT);
	private static final String GROUP_NAME = "ENCRYPTION";

	private String encryptYN = "";
	private String dbEncryptSolutionName = "DAMO";
	
	private boolean started;
	private LifecycleSupport lifecycle = new LifecycleSupport(this);

	private EncryptionManager() {

	}

	public static EncryptionManager getInstance() {
		return ApplicationContextProvider.getContext().getBean(EncryptionManager.class);
	}

	public void start() throws LifecycleException {
		if (started)
			throw new LifecycleException("RECEAICMM201");

		lifecycle.fireLifecycleEvent(STARTING_EVENT, this);

		try {
			init();
		} catch (Exception e) {
			throw new LifecycleException(ExceptionUtil.getErrorCode(e, "RECEAICMM202"));
		}

		started = true;
		lifecycle.fireLifecycleEvent(STARTED_EVENT, this);
	}

	private void init() throws Exception {
		encryptYN = "";
		encryptYN = PropManager.getInstance().getProperty(GROUP_NAME, System.getProperty(Keys.SERVER_KEY));
		if (encryptYN == null) {
			encryptYN = "N";
		} else {
			if (encryptYN.trim().length() == 0) {
				encryptYN = "N";
			}
		}
		dbEncryptSolutionName = PropManager.getInstance().getProperty(GROUP_NAME, "dbEncryptSolutionName");
		if (dbEncryptSolutionName == null) {
			dbEncryptSolutionName = "DAMO";
		} else {
			if (dbEncryptSolutionName.trim().length() == 0) {
				dbEncryptSolutionName = "DAMO";
			}
		}
		logger.warn("EncryptionManager] init :: encryption YN [ " + encryptYN + " ], dbEncryptSolutionName [ " + dbEncryptSolutionName + " ]");
	}

	public synchronized void reload() throws Exception {
		if (logger.isWarn()) {
			logger.warn("EncryptionManager] reload all Started ...");
		}
		init();
		if (logger.isWarn()) {
			logger.warn("EncryptionManager] reload all finished ...");
		}
	}

	public void stop() throws LifecycleException {
		if (!started)
			throw new LifecycleException("RECEAICMM203");

		lifecycle.fireLifecycleEvent(STOPING_EVENT, this);

		encryptYN = null;
		started = false;
		lifecycle.fireLifecycleEvent(STOPPED_EVENT, this);
	}

	public void addLifecycleListener(LifecycleListener listener) {
		lifecycle.addLifecycleListener(listener);
	}

	public LifecycleListener[] findLifecycleListeners() {
		return lifecycle.findLifecycleListeners();
	}

	public void removeLifecycleListener(LifecycleListener listener) {
		lifecycle.removeLifecycleListener(listener);
	}

	public boolean isStarted() {
		return this.started;
	}

	public String getEncryptYN() {
		return encryptYN;
	}
	
	public String getDBEncryptSolutionName() {
		return dbEncryptSolutionName;
	}
	
	public boolean isEncrypt() {
		return "Y".equalsIgnoreCase(encryptYN);
	}
	
	public String encryptDBData(String plainText) {
		if(isEncrypt()) {
			if ("DAMO".equals(dbEncryptSolutionName)) {
				com.eactive.ext.djb.DamoManager damoManager = new com.eactive.ext.djb.DamoManager();
				return damoManager.encrypt(plainText);
			} else if ("SAFEDB".equals(dbEncryptSolutionName)) {
				String originalAttribute = plainText;
				if (StringUtils.isNotEmpty(plainText)) {
					KjbSafedbWrapper wrapper = KjbSafedbWrapper.getInstance();
					plainText = wrapper.encryptNotRnnoString(plainText);
					
					if (logger.isInfo()) {
						// originalAttribute 홀수 글자 마스킹
						StringBuilder sb = new StringBuilder();
						for (int i = 0; i < originalAttribute.length(); i++) {
							if (i % 2 == 0) {
								sb.append(originalAttribute.charAt(i));
							} else {
								sb.append("*");
							}
						}
	
						originalAttribute = sb.toString();
						// System.out.println("DB 암호화 #2 : {"+ originalAttribute +"} ->
						// {"+attribute+"}");
						// logger.debug("DB 암호화 #2 : {} -> {}", originalAttribute, attribute);
					}
				}
				return plainText;
			} else {
				return plainText;
			}
		} else {
			return plainText;
		}
			
	}

	public String decryptDBData(String dbData) {
		if (StringUtils.isNotEmpty(dbData)) {
			if (this.isEncrypted(dbData)) {
				if ("DAMO".equals(dbEncryptSolutionName)) {
					com.eactive.ext.djb.DamoManager damoManager = new com.eactive.ext.djb.DamoManager();
					return damoManager.decrypt(dbData);
				} else if ("SAFEDB".equals(dbEncryptSolutionName)) {
					KjbSafedbWrapper safeDBWrapper = KjbSafedbWrapper.getInstance();
					try {
						dbData = safeDBWrapper.decryptNotRnno(dbData);
					} catch (Exception e) {
						// 복호화 실패시 원본 반환
						String logData = dbData.length() <= 3 ? dbData : dbData.substring(0, 3) + "...";
						logger.warn("DB 복호화 실패: 복호화하지 않고 원본 반환. dbData={} (Length : {})", logData, dbData.length());
						return dbData;
					}
				}
			} else {
				logger.debug("DB 복호화 경고: Base64 형식이 아님. 복호화하지 않고 원본 반환. dbData={}", dbData);
			}
		}
		// logger.debug("DB 복호화 #2 : {} -> {}", dbDataOriginal, dbData);
		return dbData;
	}

	/**
	 * 암호화 여부 확인(base64 인코딩 여부로 판단, 또는 0-9, - 로 구성된 경우 암호화 되지 않은 걸로 판단(연락처))
	 * 
	 * @param data
	 * @return
	 */
	private boolean isEncrypted(String data) {
		if (StringUtils.isEmpty(data)) {
			return false;
		}

		// 숫자, - 로만 구성된 경우 암호화 되지 않은 걸로 판단 (ex: 연락처)
		if (data.matches("^[0-9-]+$")) {
			return false;
		}

		data = data.trim();

		// Base64 형식 체크
		if (!isBase64(data)) {
			return false;
		}

		// Base64 길이 체크 (4의 배수)
		if (data.length() % 4 != 0) {
			return false;
		}

		return true;
	}
	
	public static boolean isBase64(String str) {
		if (str == null || str.isEmpty()) {
			return false;
		}
		String base64Pattern = "^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$";
		return str.matches(base64Pattern);
	}

}
