package com.dbs.mplus.service.api;

import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;

import net.sf.json.JSONObject;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import com.dbs.mplus.exception.mobile.ApiCallException;
import com.dbs.mplus.framework.FrameworkConstants;
import com.dbs.mplus.framework.api.executor.ApiExecuteDelegator;
import com.dbs.mplus.framework.exception.MPlusApiServiceException;
import com.dbs.mplus.framework.service.ApiCallService;
import com.dbs.mplus.framework.util.ConvertUtil;
import com.dbs.mplus.framework.util.ResponseUtil;
//import com.dbs.mplus.service.api.util.MplusBaseResParser;
import com.dbs.mplus.service.api.util.MplusBaseQApiResParser;
import com.dbs.mplus.service.api.util.ParamMappingUtil;
import com.dbs.mplus.service.api.util.TestUserDataGenerator;

/**
 * Api 호출 모음 
 * @author hyeeun 
 *
 */
@Service 
public class MPlusApiCallService extends ApiCallService {
	private final Logger logger = LoggerFactory.getLogger(this.getClass());
	// 
	@Autowired
	@Qualifier("apiExecuteDelegator")
	private ApiExecuteDelegator apiDelegator;
	@Autowired 
	private TestUserDataGenerator testDataGenerator; 
	
	/**
	 * 생성자 
	 */
	public MPlusApiCallService() { 
		super(ParamMappingUtil.class); 
	}
	
	/**
	 * 내부 서비스용, api를 호출하고, 결과를 전달함 
	 * @param api
	 * @param reqMap
	 * @return
	 * @throws ApiCallException
	 */
	public Object execute(MPlusApi api, Map<String, Object> reqMap) throws ApiCallException {
		Map<String, Object> resultMap = this.call(api, reqMap); 
		// 결과 꺼내주기 
		return resultMap.get(FrameworkConstants.UIRESPONSE.RESULT.getKey()); 
	}
	/**
	 * 내부 서비스용, api를 호출하고, 결과를 전달함 
	 * @param api
	 * @param key
	 * @param value
	 * @return
	 * @throws ApiCallException
	 */
	public Object execute(MPlusApi api, String key, Object value) throws ApiCallException {
		if(StringUtils.isEmpty(key) || value == null) {
			throw new ApiCallException("mplus.error.param", "파라미터를 확인해주세요", null); 
		}		
		
		Map<String, Object> map = new HashMap<String, Object>(); 
		map.put(key, value); 
		
		
		return this.execute(api, map); 
	}

	/**
	 * UI용, api를 호출하고, 결과를 전달함 
	 * @param api 호출할 API 
	 * @param reqMap 요청 파라미터 맵 
	 * @return {@link FrameworkConstatns.UIRESPONSE} 참고 
	 * @throws ApiCallException 
	 */
	@SuppressWarnings("unchecked")
	public Map<String, Object> call(MPlusApi api, Map<String, Object> reqMap) throws ApiCallException {
		// 연산자 확인 
		if(api == null) { 
			throw new ApiCallException("mplus.error.param", "파라미터를 확인해주세요", null); 
		}
		
		// 파라미터 확인 
		if(reqMap == null) { 
			reqMap = Collections.emptyMap(); 
		}
		
		//2018.12.28 get_API 파라미터 "" -> "%"로 일괄 수정		
		if(api.toString().indexOf("_GET") != -1){
			Iterator<String> keys = reqMap.keySet().iterator();
			while(keys.hasNext()){
				String key = keys.next();
				if(reqMap.get(key).equals("")){
					reqMap.put(key, "%");		
				}
			}
		}
		
		// 디버깅을 위한 메시지 추가 
		if(this.logger.isDebugEnabled()) { 
			StringBuilder builder = new StringBuilder(FrameworkConstants.NEW_LINE); 
			
			builder.append("Api 호출 =====").append(FrameworkConstants.NEW_LINE); 
			builder.append("- api : ").append(api).append(FrameworkConstants.NEW_LINE);  
			builder.append("- request parameters ").append(FrameworkConstants.NEW_LINE); 
			builder.append(ConvertUtil.convertStringForDebug(reqMap)).append(FrameworkConstants.NEW_LINE); 
			
			this.logger.debug(builder.toString());
		}
		
		// 테스트 메소드 호출하고 
		Object obj = this.testDataGenerator.call(api, reqMap); 
		// 결과가 있으면 반환 
		if(obj != null) { 
			//2016-05-25 서영일 이미 result로 담아서 오는데 한번더 담길래 그대로 보내도록 함
			return (Map<String, Object>)obj;
		}

		
		// 없으면 실제 api 호출 
		if(api.isList()) {
			return this.callList(api, reqMap); 
		}

		return this.callObject(api, reqMap); 
	}

	/**
	 * UI용, 파라미터가 1개인 api를 호출하고, 결과를 전달함 
	 * @param api 호출할 API 
	 * @param key 요청 파라미터 key 
	 * @param value 요청 파라미터의 값 
	 * @return {@link FrameworkConstatns.UIRESPONSE}  참고 
	 * @throws ApiCallException 
	 */
	public Map<String, Object> call(MPlusApi api, String key, Object value) throws ApiCallException {
		if(StringUtils.isEmpty(key) || value == null) {
			throw new ApiCallException("mplus.error.param", "파라미터를 확인해주세요", null); 
		}
		
		// 맵 만들고 
		Map<String, Object> reqMap = new HashMap<String, Object>(); 
		reqMap.put(key, value);
		
		// 호출 
		return this.call(api, reqMap); 
	}
	
	/**
	 * list형 호출  
	 * @param api 호출할 API 
	 * @param reqMap 요청 파라미터 
	 * @return UI단에 전달할 수 있도록 정리된 형태의 결과 맵 
	 * @throws ApiCallException
	 */
	private Map<String, Object> callList(MPlusApi api, Map<String, Object> reqMap) throws ApiCallException { 
		try { 
			switch(api.getApiType()) {
			case WEB_SERVICE : 
				return super.callHttpForList(this.apiDelegator, api.getApiUrl(), reqMap); 
				
			case SQL : 
			case PROCEDURE : 			
				return super.callSqlForList(this.apiDelegator, api.getApiUrl(), reqMap); 	
			}
		}
		catch(MPlusApiServiceException e) {
			throw e;
		}
		catch(final Exception ex) { 
			if(this.logger.isDebugEnabled()) { 
				this.logger.debug("api호출오류. " + api + ", reqMap=" + reqMap, ex);
			} 
			
			throw new ApiCallException("mplus.error.if.system", "시스템 호출 오류가 발생했습니다 (IF)", api);
		}
		
		// 여기에 왔으면 개발단계에서 못잡은 오류 
		throw new ApiCallException("mplus.error.system", "시스템 호출 오류가 발생했습니다", api); 
	}

	/**
	 * object형 호출 
	 * @param api
	 * @param reqMap
	 * @return
	 * @throws ApiCallException
	 */
	private Map<String, Object> callObject(MPlusApi api, Map<String, Object> reqMap) throws ApiCallException { 
		try { 
			switch(api.getApiType()) {
			case WEB_SERVICE : 
				return super.callHttpForObject(this.apiDelegator, api.getApiUrl(), reqMap); 
				
			case SQL :  	
			case PROCEDURE : 			
				return super.callSqlForObject(this.apiDelegator, api.getApiUrl(), reqMap); 	
			}
		}
		catch(MPlusApiServiceException e) {
			throw e;
		}
		catch(final Exception ex) { 
			if(this.logger.isDebugEnabled()) { 
				this.logger.debug("api호출오류. " + api + ", reqMap=" + reqMap, ex);
			} 
			
			throw new ApiCallException("mplus.error.if.system", "시스템 호출 오류가 발생했습니다 (IF)", api);
		}
		
		// 여기에 왔으면 개발단계에서 못잡은 오류 
		throw new ApiCallException("mplus.error.system", "시스템 호출 오류가 발생했습니다", api); 
	}

	@Override
	protected Map<String, Object> callHttp(ApiExecuteDelegator apiDelegator, String apiUrl, Map<String, Object> reqMap) throws MPlusApiServiceException {
		// 결과 
		Object resObj = null; 
		MplusBaseQApiResParser parser = null; 
		
		try { 
			// API 실행 
			resObj = apiDelegator.execute(apiUrl, reqMap); 
			// 결과분석 
			//parser = new HyumcResParser(JSONObject.fromObject(resObj)); 
			//parser = new MplusBaseResParser(JSONObject.fromObject(resObj)); 
			parser = new MplusBaseQApiResParser(JSONObject.fromObject(resObj)); 
			
		}
		catch(MPlusApiServiceException ex) { 
			// 데이터 읽는 시간이 오래걸려서 소켓 끊겼을 때는 '사용자가 많습니다. 잠시 후 사용하시기 바랍니다.' 메시지 출력
			// 한양대학교병원 요구사항.
			Throwable t = ex.getCause().getCause();
			if( t != null ){
				if( t.getMessage().contains("SocketTimeout") ) {
					throw new MPlusApiServiceException("mplus.error.if.timeout", "사용자가 많습니다. 잠시 후 사용하시기 바랍니다."); 
				}
			}
			
			throw ex;
		}
		catch(Exception ex) { 
			throw new MPlusApiServiceException(ex); 
		}
		
		
		// 실패인가 
		if(parser.isError()) {
			if(this.logger.isDebugEnabled()) {
				StringBuilder builder = new StringBuilder(); 
				
				builder.append("기간계 시스템에서 요청 실행중에 에러가 발생했다고 응답 받았음 ==== ").append(FrameworkConstants.NEW_LINE); 
				builder.append(parser.getErrorMessage()).append(FrameworkConstants.NEW_LINE); 
				
				this.logger.debug(builder.toString());
			}
			
			throw new ApiCallException("mplus.error.if.system", "시스템 호출 오류가 발생했습니다 (IF)", null);  
		}
		
		// 추가 메시지 
		String extraMsg = parser.getExtraMessage().replaceAll("[.*!?]", ""); 
		// 추가 메시지가 없는 경우, 기본 메시지를 심어주면 좋겠지만 다국어까지 생각해야 해서 패스 
		Object resultObj = parser.getResult(); 
		
		// 결과정리 
		return ResponseUtil.wrapExtraMessageResultMap(extraMsg, resultObj); 
	}
//	
//	/**
//	 * 예외출력용 
//	 * @param api
//	 * @param reqMap
//	 * @param ex
//	 */
//	private void printApiCallException(KnuhApi api, Map<String, Object> reqMap, ApiCallException ex) { 
//		final StringBuilder builder = new StringBuilder(); 
//		
//		// 
//		builder.append(FrameworkConstants.NEW_LINE).append("API 호출 에러 ---------------------"); 
//		builder.append(FrameworkConstants.NEW_LINE).append("- api : ").append(api); 
//		builder.append(FrameworkConstants.NEW_LINE).append("- parameters "); 
//		
//		final Iterator<String> keyIter = reqMap.keySet().iterator(); 
//		String key = null; 
//		
//		while(keyIter.hasNext()) {
//			key = keyIter.next(); 
//			builder.append(FrameworkConstants.NEW_LINE).append("  * ").append(key).append(" : ").append(reqMap.get(key)); 
//		}
//		builder.append(FrameworkConstants.NEW_LINE).append("- 예외 메시지 : ").append(ex.getMessage()); 
//		builder.append(FrameworkConstants.NEW_LINE).append("---------------------"); 
//		
//		this.logger.error(builder.toString());		
//	}
}
