package com.eactive.eai.custom.inbound.processor;

import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Properties;
import java.util.Set;

import org.apache.commons.lang3.StringUtils;

import com.eactive.eai.common.inflow.Bucket;
import com.eactive.eai.common.inflow.InflowTargetVO;
import com.eactive.eai.common.inflow.dual.DualBucket;
import com.eactive.eai.common.inflow.dual.DualCustomBucket;
import com.eactive.eai.common.message.EAIMessage;
import com.eactive.eai.common.property.PropManager;
import com.eactive.eai.common.util.Logger;
import com.eactive.eai.custom.inflow.ClientDualBucket;
import com.eactive.eai.inbound.processor.ProcessVO;
import com.eactive.eai.inbound.processor.RequestProcessor;
import com.eactive.eai.message.StandardItem;
import com.eactive.eai.message.StandardMessage;

/**
 * 어댑터/인터페이스/클라이언트 유량제어 차단 시 어느 버킷(PER_SECOND/THRESHOLD)에서
 * 차단됐는지 에러 메시지에 포함하는 RequestProcessor 확장.
 *
 * <p>ClientDualInflowControlManager와 함께 사용:
 * <pre>
 * inflow.control.bucket.className=com.eactive.eai.common.inflow.dual.ClientDualInflowControlManager
 * </pre>
 *
 * <p>Spring Bean 등록 후 기존 RequestProcessor 대신 이 클래스를 어댑터에 설정한다.
 */
public class DJBRequestProcessor extends RequestProcessor {
	
	static Logger logger = Logger.getLogger(Logger.LOGGER_DEFAULT);
	
	@Override
	protected void afterFoundEaiMessage(EAIMessage eaiMsg, ProcessVO vo, Properties prop) {
		logger.debug("DJBRequestProcessor interface found.");
		try {
			this.resolveStandardHeaderMapping(eaiMsg, prop);
		} catch (Exception e) {
			logger.warn("DJBRequestProcessor header set failed.", e);
		}
	}
	
	private void resolveStandardHeaderMapping(EAIMessage eaiMsg, Properties prop) {
		StandardMessage standardMessage = eaiMsg.getStandardMessage();
		HashMap<String, String> headerMapping = getPropertyMap("DJB_IN_HTTHEAD_STDHEAD_MAPPING", eaiMsg.getSngSysItfTp());

		for (Entry<String, String> entry : headerMapping.entrySet()) {
			String stdHeaderPath = entry.getKey();
			int dotIdx = stdHeaderPath.indexOf('.');
			if(dotIdx > 0) {
				String firstPath = stdHeaderPath.substring(0, dotIdx);
				StandardItem topLevelItem = standardMessage.findItem(firstPath);
				topLevelItem.setHidden(false);
			}
			StandardItem item = standardMessage.findItem(entry.getKey());

			String value = resolveNestedValue(prop, entry.getValue());
			if (item != null && value != null)
				item.setValue(value);
			else
				logger.debug("header mapping item not found : {} ", entry);
		}
	}

	private HashMap<String, String> getPropertyMap(String propertyGroupName, String prefix) {
		Properties prop = PropManager.getInstance().getProperties(propertyGroupName);
		Set<String> keys = prop.stringPropertyNames();
		HashMap<String, String> tmpToJsonArrayInterface = new HashMap<>();
		for (String key : keys) {
			if (StringUtils.startsWith(key, prefix + ".")) {
				String propValue = prop.getProperty(key);
				String replacedKey = StringUtils.replace(key, prefix + ".", "");
				tmpToJsonArrayInterface.put(replacedKey, propValue);
			}
		}
		return tmpToJsonArrayInterface;
	}

	private String resolveNestedValue(Map<?, ?> map, String keyPath) {
		int dotIdx = keyPath.indexOf('.');
		if (dotIdx < 0) {
			Object val = map.get(keyPath);
			if (val == null)
				return null;
			if (val instanceof String)
				return (String) val;
			return val.toString();
		}

		String first = keyPath.substring(0, dotIdx);
		String rest = keyPath.substring(dotIdx + 1);
		Object val = map.get(first);
		if (val instanceof Map) {
			return resolveNestedValue((Map<?, ?>) val, rest);
		}

		// 폴백: 전체 keyPath를 단일 키로 조회
		Object direct = map.get(keyPath);
		if (direct == null)
			return null;
		if (direct instanceof String)
			return (String) direct;
		return direct.toString();
	}

    @Override
    protected String checkCustomInflow(Bucket bucket, EAIMessage eaiMsg, Properties prop) {
        String clientId = eaiMsg.getClientId();
        if (StringUtils.isBlank(clientId)) return null;
        if (bucket instanceof ClientDualBucket) {
            return ((ClientDualBucket) bucket).isClientPassDetail(clientId);
        }
        return null;
    }

    @Override
    protected InflowTargetVO resolveCustomInflowThreshold(Bucket bucket, EAIMessage eaiMsg, Properties prop) {
        if (bucket instanceof ClientDualBucket) {
            return ((ClientDualBucket) bucket).getClientInflowThreshold(eaiMsg.getClientId());
        }
        return null;
    }

    @Override
    protected String checkAdapterInflow(Bucket bucket, String adapterGroupName) {
        if (bucket instanceof DualBucket) {
            return ((DualBucket) bucket).isAdapterPassDetail(adapterGroupName);
        }
        return super.checkAdapterInflow(bucket, adapterGroupName);
    }

    @Override
    protected String checkInterfaceInflow(Bucket bucket, String eaiSvcCd) {
        if (bucket instanceof DualBucket) {
            return ((DualBucket) bucket).isInterfacePassDetail(eaiSvcCd);
        }
        return super.checkInterfaceInflow(bucket, eaiSvcCd);
    }

    /**
     * 차단 원인에 따라 에러 메시지에 한도 정보를 포함.
     * <ul>
     *   <li>PER_SECOND: "API 호출 한도 초과 [name: Nreq/sec]"</li>
     *   <li>THRESHOLD:  "API 호출 한도 초과 [name: Nreq/UNIT]"</li>
     *   <li>그 외:      "API 호출 한도 초과 [name]" (기존 동작)</li>
     * </ul>
     */
    @Override
    protected String buildInflowTargetErrorMsg(InflowTargetVO inflowTargetVO, String blockedType) {
        if (DualCustomBucket.RESULT_BLOCKED_PER_SECOND.equals(blockedType)) {
            return String.format("API 호출 한도 초과 [%s: %dreq/sec]",
                    inflowTargetVO.getName(),
                    inflowTargetVO.getThresholdPerSecond());
        }
        if (DualCustomBucket.RESULT_BLOCKED_THRESHOLD.equals(blockedType)) {
            return String.format("API 호출 한도 초과 [%s: %dreq/%s]",
                    inflowTargetVO.getName(),
                    inflowTargetVO.getThreshold(),
                    inflowTargetVO.getThresholdTimeUnit());
        }
        return super.buildInflowTargetErrorMsg(inflowTargetVO, blockedType);
    }
}
