package nlib.restful.service;

import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpMethod;
import org.springframework.http.ResponseEntity;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.util.UriComponentsBuilder;

import nlib.util.StringUtil;

/**
 * <pre>
 * @Class Name : DataApiInterface.java
 * 
 * @Description : DAO에서 상속할 DataApi의 부모 클래스들을 위한 인터페이스 
 * 
 * 
 * @프로젝트명: 지방문화원 통합자료관리시스템 구축사업 (2021)
 *
 * </pre>
 *
 * @ ------------    --------    ---------------------------
 * @ 수정일                           수정자                  수정내용
 * @ ------------    --------    ---------------------------
 * @ 2021. 7. 9.     KNKIM       최초 생성
 *
 * 
 * @author 이씨플라자 * DIGITALSHIP KNKIM
 * @since 2021. 7. 9.
 * @version 1.0
 *
 */
public interface DataApiInterface {

	static final Logger log = LoggerFactory.getLogger(DataApiInterface.class);

	/**
	 * 실제 RESTful API 서버로 요청을 전송하고, 응답받은 자료를 응답VO에 담아서 리턴한다.
	 * 
	 * @param reqVO
	 * @param method
	 * @return
	 */
	public DataApiResVO request(DataApiReqVO reqVO);


    /**
     * [POST용] 요청 VO의 입력변수값을 API 요청을 위한 MultiValueMap 형태로 변환한다.
     * 
     * @param reqVO
     * @return
     */
    public static MultiValueMap<String, String> makeRequestParamMultiValueMap(DataApiReqVO reqVO) {
    	
    	MultiValueMap<String, String> map = new LinkedMultiValueMap<String, String>();
    	
		// 추가 정보 
		if(reqVO.hasInfo()) {
			HashMap<String, String> info = reqVO.getInfo();
			for(String key : info.keySet()) {
				String val = null;
				if(DataApiVO.XML_EL_NAME_MB_INFO_ID.equals(key)) {
					val = reqVO.getEncMbInfoId();
				} else {
					val = info.get(key);
				}
				
				/*
				String encVal = null;
				try {
					encVal = URLEncoder.encode((String)val, "UTF-8");
				} catch(Exception e) {
					log.error("makeRequestParamUrl > " + e.toString());
					continue;
				}
				*/
				
				map.add(key, val);
			}
		}

		if(reqVO.hasList()) {
			HashMap<String,List<HashMap<String,String>>> listMap = reqVO.getListMap();
			if(listMap != null) {
				for(String listName : listMap.keySet() ) {
					List<HashMap<String,String>> list = listMap.get(listName);
					
					// 목록변수명은 있으나, 값이 없는 경우, 빈 배열 JSON값 설정 
					if(list == null || list.size() < 1) {
						map.add(listName, "[]");
						continue;
					} 
					
					// 목록변수명에 대한 목록 자료 존재하는 경우, JSON 생성 처리
					map.add(listName, makeJsonStrFromListMap(list));
					
				} // for 
			} // if(listMap != null)
		} // if(reqVO.hasList())
		
		// 요청 파라메타 확인 
		for(String key : map.keySet()) {
			log.debug("MultiValueMap > " + key + "=" + map.get(key));
		}
		
		return map;
    }
    
    /**
     * [GET용] 요청 VO의 입력변수값을 API 요청을 위한 URL 쿼리 형태의 문자열로 리턴한다.
     * 
     * @param url
     * @param reqVO
     * @return
     */
    public static String makeRequestParamUrl(String url, DataApiReqVO reqVO) {

    	UriComponentsBuilder builder = UriComponentsBuilder.fromHttpUrl(url);
    	
		// 추가 정보 
		if(reqVO.hasInfo()) {
			HashMap<String, String> info = reqVO.getInfo();
			for(String key : info.keySet()) {
				Object val = null;
				if(DataApiVO.XML_EL_NAME_MB_INFO_ID.equals(key)) {
					val = reqVO.getEncMbInfoId();
				} else {
					val = info.get(key);
				}
				/*
				String encVal = null;
				try {
					encVal = URLEncoder.encode((String)val, "UTF-8");
				} catch(Exception e) {
					log.error("makeRequestParamUrl > " + e.toString());
					continue;
				}*/
				builder.queryParam(key, (String)val);
			}
		}
		
		if(reqVO.hasList()) {
			HashMap<String,List<HashMap<String,String>>> listMap = reqVO.getListMap();
			if(listMap != null) {
				for(String listName : listMap.keySet() ) {
					List<HashMap<String,String>> list = listMap.get(listName);
					
					// 목록변수명은 있으나, 값이 없는 경우, 빈 배열 JSON값 설정 
					if(list == null || list.size() < 1) {
						builder.queryParam(listName, "[]");
						continue;
					} 
					
					// 목록변수명에 대한 목록 자료 존재하는 경우, JSON 생성 처리
					builder.queryParam(listName, makeJsonStrFromListMap(list));
					
				} // for 
			} // if(listMap != null)
		} // if(reqVO.hasList())

		String retUrl = builder.toUriString();
		log.debug("makeRequestParamUrl > queryUrl = " + retUrl);
		
		return retUrl;
    }
    
    /**
     * HashMap를 목록의 요소로 하는 리스트를 받아서 해당 요소들의 배열을 가진 JSON 객체 문자열을 생성하여 리턴한다.
     * 예 : [{"name":"James", "city":"Miracle", ...}, ..., {"name":"James", "city":"Miracle", ...}]
     * 
     * @param list
     * @return
     */
    public static String makeJsonStrFromListMap(List<HashMap<String,String>> list) {
		
		// 목록변수명은 있으나, 값이 없는 경우, 빈 배열 JSON값 설정 
		if(list == null || list.size() < 1) {
			return "[]";
		} 
		
		// 목록변수명에 대한 목록 자료 존재하는 경우, JSON 생성 처리
		String jsonListStr = "";
		for(int i=0; i<list.size(); i++) {
			HashMap<String, String> record = list.get(i);
			
			String jsonRecordStr = makeJsonStrFromMap(record);
			if(StringUtil.isEmpty(jsonRecordStr)) continue;

			if(jsonListStr.length() > 0) jsonListStr += ",";
			jsonListStr += jsonRecordStr;
		}
		return "[" + jsonListStr + "]";
    }
    
    /**
     * HashMap을 받아서 해당 요소들을 맴버변수로 하는 JSON객체 문자열을 생성하여 리턴한다. 
     * 예 : {"name":"James", "city":"Miracle", ...}
     * 
     * @param record
     * @return
     */
    public static String makeJsonStrFromMap(HashMap<String,String> record) {
    	if(record == null || record.size() < 1) return null;
    	
    	String jsonRecordStr = "";
		for(String subKey : record.keySet()) {
			String subVal = record.get(subKey);
			if(jsonRecordStr.length() > 0) jsonRecordStr += ",";
			jsonRecordStr += "\"" + subKey + ":" + (subVal == null? "null" : "\"" + subVal + "\"");
		}
		
		return "{" + jsonRecordStr + "}";
    }
    	    
    /**
     * 요청전 공통 필수 입력 매개변수를 체크하여 결과를 리턴한다.
     * 
     * @param reqVO
     * @return
     * @throws Exception : 
     */
    public static DataApiResVO checkReqCommonParams(DataApiReqVO reqVO) throws Exception {
    	
    	if(reqVO == null) {
    		return new DataApiResVO("ERR_NO_REQ", "데이터송수신 요청객체가 존재하지 않습니다.");
    	}
    	
    	if(StringUtil.isEmpty(reqVO.getReqUrl())) {
    		return new DataApiResVO("ERR_NO_RURL", "데이터 요청 업무ID에 대한 URL/RUI 정보가 존재하지 않습니다.");
    	}

    	if(reqVO.getReqMethod() == null) {
    		return new DataApiResVO("ERR_NO_METHOD", "데이터 요청 방식 정보가 존재하지 않습니다.");
    	}
    	
    	return null;
    }
    
    /**
     * 처리결과 정보를 입력받아 DataApiResVO를 생성한다.
     * 
     * @param resultCode
     * @param resultMessage
     * @return
     */
    public static DataApiResVO makeErrorRes(String resultCode, String resultMessage) {
    	return new DataApiResVO(resultCode, resultMessage);
    }
    
    /**
     * 응답받은 XML 문자열을 DataApiResVO (내부 Map객체 포함) 객체로 변환하여 리턴한다. 
     * 
     * @param xmlStr
     * @return
     */
    public static DataApiResVO convertXmlToResVO(String xmlStr) {
    	
    	DataApiResVO resVO = new DataApiResVO();
        Map<String, Object> map = null;
        
        try {
        	map = XmlConverter.convert(xmlStr, DataApiVO.XML_EL_NAME_ROOT_NAME);
        } catch(Exception e) {
        	resVO.setResultCode("ERR_RES_XML_FORMAT");
        	resVO.setResultMessage(String.format("올바르지 않은 XML 형식의 응답입니다. : [%s]", xmlStr));
        	log.error("convertXmlToResVO > " + resVO.getResultMessage());
        	e.printStackTrace();
        	return resVO;
        }
    	
    	// 처리결과 확인 
    	List<HashMap<String, String>> recordList = null;
    	for( String key : map.keySet()) {
			if(key.equalsIgnoreCase(DataApiVO.XML_EL_NAME_RESULT_CODE)) resVO.setResultCode((String)map.get(key));
			else if(key.equalsIgnoreCase(DataApiVO.XML_EL_NAME_RESULT_MESSAGE)) resVO.setResultMessage((String)map.get(key));
			else if(key.equalsIgnoreCase(DataApiVO.XML_EL_NAME_RECORD)) {
				// 다건 목록 데이터인 경우
				Object obj = map.get(DataApiVO.XML_EL_NAME_RECORD);
				if(obj instanceof HashMap) {
					// 맵인 경우, 리스트형식으로 저장 처리 
					List<HashMap<String,String>> list = new ArrayList<HashMap<String,String>>();
					list.add((HashMap)obj);
					resVO.setList(DataApiVO.XML_EL_NAME_RECORD, list);
				} else if(obj instanceof List) {
					resVO.setList(DataApiVO.XML_EL_NAME_RECORD, (List)obj);
				} else {
		        	resVO.setResultCode("ERR_RES_XML_FORMAT_RECORD");
		        	resVO.setResultMessage(DataApiVO.XML_EL_NAME_RECORD + " 요소가 알수 없는 형식으로 객체화되었습니다. 확인바랍니다. : " + obj.toString());
		        	log.error("convertXmlToResVO > " + resVO.getResultMessage());
		        	return resVO;
				}
			} else resVO.putInfoItem(key, (String)map.get(key));
    	}
    	
    	return resVO;
    }
}
