package cokr.xit.ens.modules.common.ctgy.nicedici.support.socket;


import KISINFO.VNO.VNOInterop;
import cokr.xit.ens.core.aop.EnsResponseVO;
import cokr.xit.ens.core.exception.EnsException;
import cokr.xit.ens.core.exception.code.EnsErrCd;
import cokr.xit.ens.core.utils.MapDeserailizer;
import cokr.xit.ens.modules.common.ctgy.nicedici.model.NiceDiCiRespDTO;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.*;
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
import org.springframework.web.client.HttpClientErrorException;
import org.springframework.web.client.HttpServerErrorException;
import org.springframework.web.client.RestClientException;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.util.UriComponents;
import org.springframework.web.util.UriComponentsBuilder;

import java.net.URI;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.Map;

@Slf4j
public class Interop
{
    public Interop()
	{

	}

	public static EnsResponseVO<String> getCI(String siteCode, String sitePw, String jumin) {

		final String sSiteCode	= siteCode;	// NICE평가정보에서 발급한 서비스 사이트코드
		final String sSitePw    = sitePw;
		final String sJumin		= jumin.replaceAll("[^0-9]", "");
		final String sFlag		= "JID";



		int iRtnCI = -1;
		EnsErrCd errCode = EnsErrCd.ERR999;
		String errMsg = "API 호출 오류. 방화벽을 확인해 보시기 바랍니다.";
		String ci = null;
		try {
			// 모듈 객체 생성
			VNOInterop vnoInterop = new VNOInterop();

			/* ──── CI 값을 추출하기 위한 부분 Start */
			// 인증요청처리
			iRtnCI = vnoInterop.fnRequestConnInfo(sSiteCode, sSitePw, sJumin, sFlag);
			log.info("=======================================================================");
			log.info("JID=" + sJumin);
			log.info("iRtnCI=" + iRtnCI);

			// 인증결과코드에 따른 처리
			if (iRtnCI == 1) {
				// CI 값 추출 (연계정보 확인값, 88Byte)
				String sConnInfo = vnoInterop.getConnInfo();
				log.info("CONNINFO=[" + sConnInfo + "]");

				// 결과설정
				errCode = EnsErrCd.OK;
				errMsg = String.format("[%s] (응답코드 %s)", EnsErrCd.OK.getCodeNm(), iRtnCI);
				ci = sConnInfo;
			} else if (iRtnCI == 3) {
				log.info("[사용자 정보와 서비스 구분값 매핑 오류]");
				log.info("사용자 정보와 서비스 구분값이 서로 일치하도록 매핑하여 주시기 바랍니다.");

				// 결과설정
				errCode = EnsErrCd.ERR405;
				errMsg = String.format("[사용자 정보와 서비스 구분값 매핑 오류] 사용자 정보와 서비스 구분값이 서로 일치하도록 매핑하여 주시기 바랍니다. (응답코드 %s)", iRtnCI);
			} else if (iRtnCI == -9) {
				log.info("[입력값 오류]");
				log.info("fnRequestConnInfo 함수 처리시, 필요한 4개의 파라미터값의 정보를 정확하게 입력해 주시기 바랍니다.");

				// 결과설정
				errCode = EnsErrCd.ERR403;
				errMsg = String.format("[입력값 오류] fnRequestConnInfo 함수 처리시, 필요한 4개의 파라미터값의 정보를 정확하게 입력해 주시기 바랍니다.(응답코드 %s)", iRtnCI);
			} else if (iRtnCI == -21 || iRtnCI == -31 || iRtnCI == -34) {
				log.info("[통신오류]");
				log.info("방화벽 이용 시 아래 IP와 Port(총 5개)를 등록해주셔야 합니다.");
				log.info("IP : 203.234.219.72 / Port : 81, 82, 83, 84, 85");

				// 결과설정
				errCode = EnsErrCd.ERR521;
				errMsg = String.format("[통신오류] 방화벽 이용 시 아래 IP와 Port(총 5개)를 등록해주셔야 합니다. IP : 203.234.219.72 / Port : 81, 82, 83, 84, 85.(응답코드 %s)", iRtnCI);
			} else {
				log.info("[기타오류]");
				log.info("iRtnCI 값 확인 후 NICE평가정보 전산 담당자에게 문의");

				// 결과설정
				errCode = EnsErrCd.ERR999;
				errMsg = String.format("[기타오류] iRtnCI 값 확인 후 NICE평가정보 전산 담당자에게 문의. (응답코드 %s)", iRtnCI);
			}
			/* ──── CI 값을 추출하기 위한 부분 End */

		} catch (Exception e) {
			// 결과설정
			errCode = EnsErrCd.ERR602;
			errMsg = String.format("[나이스API 오류] %s (응답코드 %s)", e.getMessage(), iRtnCI);
		} finally {
			if(iRtnCI == 1)
				return EnsResponseVO.<String>okBuilder()
						.resultInfo(ci)
						.build();
			else
				return EnsResponseVO.<String>errBuilder()
						.errCode(errCode)
						.errMsg(errMsg)
						.build();
		}
	}





	public static EnsResponseVO<String> getCIProxy(String origin, String pathname, String siteCode, String sitePw, String jumin) {

		final String sSiteCode	= siteCode;	// NICE평가정보에서 발급한 서비스 사이트코드
		final String sSitePw    = sitePw;
		final String sJumin		= jumin.replaceAll("[^0-9]", "");



		int iRtnCI = -1;
		EnsErrCd errCode = EnsErrCd.ERR999;
		String errMsg = "API 호출 오류. 방화벽을 확인해 보시기 바랍니다.";
		String ci = null;
		try {

			//header 설정
			HttpHeaders headers = new HttpHeaders();
			headers.setContentType(new MediaType(MediaType.APPLICATION_JSON, Charset.forName("utf-8")));

			//url 설정
			StringBuilder url = new StringBuilder();
			url.append(origin)
					.append(pathname);

			//body 설정
			Map<String, String> mParam = new HashMap<>();
			mParam.put("site_code", sSiteCode);
			mParam.put("site_pw", sSitePw);
			mParam.put("jid", sJumin);
			ObjectMapper mapper = new ObjectMapper();
			String jsonStr = mapper.writeValueAsString(mParam);


			//api 호출
			ResponseEntity<String> resp = callApi(HttpMethod.POST, url.toString(), jsonStr, headers);

			try {
				Gson gson = new GsonBuilder().disableHtmlEscaping().registerTypeAdapter(Map.class, new MapDeserailizer()).serializeNulls().create();
				Map<String, Object> mResponse = gson.fromJson(resp.getBody(), Map.class);

				if(!"OK".equals(mResponse.get("errCode"))){
					throw new EnsException(EnsErrCd.valueOfEnum((String) mResponse.get("errCode")), (String) mResponse.get("errMsg"));
				}

				// 결과설정
				errCode = EnsErrCd.OK;
				errMsg = String.format("[%s] (응답코드 %s)", EnsErrCd.OK.getCodeNm(), iRtnCI);
				ci = (String) mResponse.get("resultInfo");
			} catch (Exception e) {
				throw new EnsException(EnsErrCd.ERR505, String.format("CI변환 응답데이터 파싱 실패. %s", resp.getBody()));
			}

		} catch (EnsException e){
			errCode = e.getErrCd();
			errMsg = e.getMessage();
		} catch (Exception e) {
			// 결과설정
			errCode = EnsErrCd.ERR602;
			errMsg = String.format("[나이스API 오류] %s (응답코드 %s)", e.getMessage(), iRtnCI);
		} finally {
			if(EnsErrCd.OK.equals(errCode))
				return EnsResponseVO.<String>okBuilder()
						.resultInfo(ci)
						.build();
			else
				return EnsResponseVO.<String>errBuilder()
						.errCode(errCode)
						.errMsg(errMsg)
						.build();
		}
	}





	public static NiceDiCiRespDTO getDI(String siteCode, String sitePw, String jumin) {

		String sSiteCode	= siteCode;		// NICE평가정보에서 발급한 서비스 사이트코드
		String sSitePw		= sitePw;		// NICE평가정보에서 발급한 서비스 사이트패스워드

		String sJumin		= jumin;		// 주민등록번호 13자리
		String sFlag		= "JID";	// 서비스 구분값 (JID:주민번호 이용)


		// 모듈 객체 생성
		VNOInterop vnoInterop = new VNOInterop();


		NiceDiCiRespDTO diCiRespDTO = new NiceDiCiRespDTO();
		try {
			/* ──── DI 값을 추출하기 위한 부분 Start */
			// 인증요청처리
			int iRtnDI = vnoInterop.fnRequestDupInfo(sSiteCode, sSitePw, sJumin, sFlag);
			System.out.println("iRtnDI=" + iRtnDI);

			// 인증결과코드에 따른 처리
			if (iRtnDI == 1) {
				// DI 값 추출 (중복가입 확인값, 64Byte)
				String sDupInfo = vnoInterop.getDupInfo();
				System.out.println("DUPINFO=[" + sDupInfo + "]");

				// DI 설정
				diCiRespDTO.setDici(sDupInfo);
			} else if (iRtnDI == 3) {
				System.out.println("[사용자 정보와 서비스 구분값 매핑 오류]");
				System.out.println("사용자 정보와 서비스 구분값이 서로 일치하도록 매핑하여 주시기 바랍니다.");

				// Error 설정
				diCiRespDTO.setError_code(iRtnDI+"");
				diCiRespDTO.setError_message("[사용자 정보와 서비스 구분값 매핑 오류] 사용자 정보와 서비스 구분값이 서로 일치하도록 매핑하여 주시기 바랍니다.");
			} else if (iRtnDI == -9) {
				System.out.println("[입력값 오류]");
				System.out.println("fnRequestDupInfo 함수 처리 시 필요한 4개의 파라미터값의 정보를 정확하게 입력해 주시기 바랍니다.");

				// Error 설정
				diCiRespDTO.setError_code(iRtnDI+"");
				diCiRespDTO.setError_message("[입력값 오류] fnRequestDupInfo 함수 처리 시 필요한 4개의 파라미터값의 정보를 정확하게 입력해 주시기 바랍니다.");
			} else if (iRtnDI == -21 || iRtnDI == -31 || iRtnDI == -34) {
				System.out.println("[통신오류]");
				System.out.println("방화벽 이용 시 아래 IP와 Port(총 5개)를 등록해주셔야 합니다.");
				System.out.println("IP : 203.234.219.72 / Port : 81, 82, 83, 84, 85");

				// Error 설정
				diCiRespDTO.setError_code(iRtnDI+"");
				diCiRespDTO.setError_message("[통신오류 오류] 방화벽 이용 시 아래 IP와 Port(총 5개)를 등록해주셔야 합니다. IP : 203.234.219.72 / Port : 81, 82, 83, 84, 85");
			} else {
				System.out.println("[기타오류]");
				System.out.println("iRtnDI 값 확인 후 NICE평가정보 전산 담당자에게 문의");

				// Error 설정
				diCiRespDTO.setError_code(iRtnDI+"");
				diCiRespDTO.setError_message("[기타오류] iRtnDI 값 확인 후 NICE평가정보 전산 담당자에게 문의");
			}
			/* ──── DI 값을 추출하기 위한 부분 End */
		} catch (Exception e) {
			diCiRespDTO.setError_code(String.format("%s %s", HttpStatus.INTERNAL_SERVER_ERROR.value(), HttpStatus.INTERNAL_SERVER_ERROR.name()));
			diCiRespDTO.setError_message(e.getMessage());
		}

		return diCiRespDTO;
	}



	/**
	 * <pre>메소드 설명: API 호출
	 * </pre>
	 * @param method
	 * @param url
	 * @param body
	 * @param headers
	 * @return ResponseEntity 요청처리 후 응답객체
	 * @author: 박민규
	 * @date: 2021. 8. 4.
	 * @apiNote: 사이트 참조 https://e2e2e2.tistory.com/15
	 */
	private static ResponseEntity<String> callApi(HttpMethod method, String url, String body, HttpHeaders headers) {
		log.debug("param =======================");
		log.debug(body);


		ResponseEntity<String> responseEntity = null;
		try {
			//uri 및 entity(param) 설정
			HttpEntity<?> entity = null;
			UriComponents uri = null;
			switch (method) {
				case GET:
					entity = new HttpEntity<>(headers);
					uri = UriComponentsBuilder
							.fromHttpUrl(String.format("%s?%s", url, body==null?"":body))
//							.encode(StandardCharsets.UTF_8)	//"%"기호가 "%25"로 인코딩 발생하여 주석처리 함.
							.build(false);
					break;
				case POST:
					entity = new HttpEntity<>(body, headers);
					uri = UriComponentsBuilder
							.fromHttpUrl(url)
							.encode(StandardCharsets.UTF_8)
							.build();
					break;

				default:
					break;
			}


			//api 호출
			HttpComponentsClientHttpRequestFactory factory = new HttpComponentsClientHttpRequestFactory();
			factory.setConnectTimeout(3000);
//			factory.setReadTimeout(3000);
			factory.setReadTimeout(10000);
			RestTemplate restTemplate = new RestTemplate(factory);
			System.out.println("  url => " + uri.toString());
			System.out.println("  method => " + method);
			System.out.println("  headers => "+ entity.getHeaders().toString());
			System.out.println("  body => "+ entity.getBody());
			responseEntity = restTemplate.exchange(URI.create(uri.toString()), method, entity, String.class);

			/*
			 * HttpStatus 정보 확인 방법
			 * 	-.코드: responseEntity.getStatusCodeValue()
			 * 	-.메시지: responseEntity.getStatusCode()
			 */

		} catch (HttpServerErrorException e) {
			responseEntity = new ResponseEntity<String>(e.getResponseBodyAsString(), e.getStatusCode());
			log.error(String.format("call API 서버오류[url =>%s param => %s error => %s]", url, body, e.getMessage()));
		} catch (HttpClientErrorException e) {
			responseEntity = new ResponseEntity<String>(e.getResponseBodyAsString(), e.getStatusCode());
			log.error(String.format("call API 클라이언트오류[url =>%s param => %s error => %s]", url, body, e.getMessage()));
		} catch (RestClientException e) {	//timeout 발생 또는 기타 오류...
			responseEntity = new ResponseEntity<String>(e.getMessage(), HttpStatus.REQUEST_TIMEOUT);
			log.error(String.format("RestAPI 호출 오류[url =>%s param => %s error => %s]", url, body, e.getMessage()));
		} catch (Exception e) {
			responseEntity = new ResponseEntity<String>(e.getMessage(), HttpStatus.INTERNAL_SERVER_ERROR);
			log.error(String.format("call API 기타오류[url =>%s param => %s error => %s]", url, body, e.getMessage()));
		}

		
		return responseEntity;
	}


}