package com.eactive.eai.manage.tools;

import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.Base64;
import java.util.Map;
import java.util.Properties;
import java.util.TreeMap;

import javax.xml.bind.DatatypeConverter;

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

import com.eactive.eai.agent.encryption.EncryptionManager;

/**
 * base64 / hex / DAMO 변환을 테스트하기 위한 서비스.
 *
 * DAMO 암복호화는 EncryptionManager와 동일하게 com.eactive.ext.djb.DamoManager를 직접 사용한다.
 * EncryptionManager.encryptDBData/decryptDBData는 encryptYN=Y로 설정된 환경에서만 동작하므로
 * (비활성 환경에서는 원문을 그대로 반환), 환경 설정과 무관하게 항상 실제 암복호화를 확인할 수 있도록
 * DamoManager를 직접 호출한다.
 *
 * encryptionManagerEncrypt/Decrypt는 위와 별개로, 실제 운영 코드가 사용하는
 * EncryptionManager.encryptDBData/decryptDBData를 그대로 호출해 현재 서버 설정(encryptYN,
 * dbEncryptSolutionName)이 반영된 실제 동작을 확인하기 위한 용도다.
 */
@Service
public class ToolsService {

	private static final String MODULE_KEY_PREFIX = "module.";

	private Charset resolveCharset(String charset) {
		return StringUtils.isNotBlank(charset) ? Charset.forName(charset) : StandardCharsets.UTF_8;
	}

	public String base64Encode(String text, String charset) {
		byte[] bytes = text.getBytes(resolveCharset(charset));
		return Base64.getEncoder().encodeToString(bytes);
	}

	public String base64Decode(String base64Text, String charset) {
		byte[] bytes = Base64.getDecoder().decode(base64Text);
		return new String(bytes, resolveCharset(charset));
	}

	public String hexEncode(String text, String charset) {
		byte[] bytes = text.getBytes(resolveCharset(charset));
		return DatatypeConverter.printHexBinary(bytes);
	}

	public String hexDecode(String hexText, String charset) {
		byte[] bytes = DatatypeConverter.parseHexBinary(hexText.trim());
		return new String(bytes, resolveCharset(charset));
	}

	public String damoEncrypt(String text) {
		return new com.eactive.ext.djb.DamoManager().encrypt(text);
	}

	public String damoDecrypt(String text) {
		return new com.eactive.ext.djb.DamoManager().decrypt(text);
	}

	public String encryptionManagerEncrypt(String text) {
		return EncryptionManager.getInstance().encryptDBData(text);
	}

	public String encryptionManagerDecrypt(String text) {
		return EncryptionManager.getInstance().decryptDBData(text);
	}

	public EncryptionManagerStatusDTO encryptionManagerStatus() {
		EncryptionManager manager = EncryptionManager.getInstance();
		EncryptionManagerStatusDTO status = new EncryptionManagerStatusDTO();
		status.setEncryptYN(manager.getEncryptYN());
		status.setDbEncryptSolutionName(manager.getDBEncryptSolutionName());
		status.setEncryptEnabled(manager.isEncrypt());
		return status;
	}

	/**
	 * eapim-online(WAR를 생성하는 루트 프로젝트) build.gradle의 generateVersionInfo 태스크가
	 * processResources 이전에 생성하는 classpath 리소스 version.info(version=git describe 결과,
	 * buildTime=...)를 읽는다. WAR는 WEB-INF/classes와 WEB-INF/lib가 클래스로더를 공유하므로
	 * 이 클래스(elink-online-common 모듈)에서 조회해도 정상적으로 찾을 수 있다.
	 * 재빌드 없이 IDE에서 바로 기동한 로컬 환경 등 파일이 없는 경우 null을 반환한다.
	 */
	public VersionInfoDTO getVersionInfo() {
		try (InputStream in = getClass().getResourceAsStream("/version.info")) {
			if (in == null) {
				return null;
			}
			return parseVersionInfo(in);
		} catch (IOException e) {
			throw new RuntimeException("version.info 읽기 실패", e);
		}
	}

	/** version.info 내용을 파싱한다. 리소스 조회와 분리해 테스트에서 직접 검증할 수 있게 한다. */
	VersionInfoDTO parseVersionInfo(InputStream in) throws IOException {
		Properties props = new Properties();
		// Properties.load(InputStream)은 ISO-8859-1로 고정 해석되어 UTF-8로 기록된
		// version.info의 한글(git tag 등)이 깨진다. Reader로 감싸 UTF-8로 디코딩해서 넘긴다.
		props.load(new InputStreamReader(in, StandardCharsets.UTF_8));

		VersionInfoDTO dto = new VersionInfoDTO();
		dto.setVersion(props.getProperty("version"));
		dto.setBuildTime(props.getProperty("buildTime"));

		// module.<서브모듈명>=<git describe 결과> 형태의 키를 모아 서브모듈별 버전으로 담는다.
		Map<String, String> moduleVersions = new TreeMap<>();
		for (String key : props.stringPropertyNames()) {
			if (key.startsWith(MODULE_KEY_PREFIX)) {
				moduleVersions.put(key.substring(MODULE_KEY_PREFIX.length()), props.getProperty(key));
			}
		}
		dto.setModuleVersions(moduleVersions);

		return dto;
	}
}
