package itn.let.uat.uia.web;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Random;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.json.simple.JSONObject;
import org.json.simple.parser.JSONParser;
import org.json.simple.parser.ParseException;
import org.springframework.stereotype.Component;
import org.springframework.ui.ModelMap;
import org.springframework.web.servlet.FlashMap;
import org.springframework.web.servlet.FlashMapManager;
import org.springframework.web.servlet.support.RequestContextUtils;

import com.icert.comm.secu.IcertSecuManager;

import itn.let.cert.phone.service.CertPhoneService;
import itn.let.cert.phone.service.MberCertPhoneVO;
import itn.let.mjo.pay.service.KmcVO;
import itn.let.uat.uia.service.AuthCertVO;
import itn.let.uss.umt.service.EgovMberManageService;

@Component("KmcCertChecker")
public class KmcCertChecker {
	
	@Resource(name = "CertPhoneService")
	private CertPhoneService certPhoneService;
	
	@Resource(name = "mberManageService")
	private EgovMberManageService mberManageService;

	//회원가입 시 인증수단을 휴대폰 본인인증만 했을 경우 사용
	public AuthCertVO authCertCheck(
								MberCertPhoneVO mberCertPhoneVO
								, HttpServletRequest request
							) {
		//url 세팅
		mberCertPhoneVO = setMberCertPhoneVO(request, mberCertPhoneVO);
		
		//kmc step 01 데이터
		//reqNum은 최대 40byte 까지 사용 가능
        String reqNum = getDay() + getRanNum(); //요청번호
        
        String tr_cert       = "";
        String cpId          = "MJOM1001";        // 회원사ID
        String urlCode       = mberCertPhoneVO.getUrlCode();     // URL코드
        String certNum       = reqNum;     // 요청번호 ( 본인인증 요청시 중복되지 않게 생성해야함. (예-시퀀스번호) )
        String date          = getDay();        // 요청일시
        String certMet       = "M";     // 본인인증방법 - M:휴대폰 본인인증, C:신용카드인증, P:공인인증서 인증 
        
        
//        String name          = "";        // 성명
        String name          = mberCertPhoneVO.getName();        // 성명
        String phoneNo	     = mberCertPhoneVO.getPhoneNo();	    // 휴대폰번호
        String phoneCorp     = mberCertPhoneVO.getPhoneCorp();   // 이동통신사
    	if(phoneCorp == null) phoneCorp = "";
    	String birthDay	     = mberCertPhoneVO.getBirthDay();	// 생년월일
    	String gender	     = mberCertPhoneVO.getGender();		// 성별
    	if(gender == null) gender = "";
        String nation        = mberCertPhoneVO.getNation();      // 내외국인 구분 - 0:내국인, 1:외국인 
    	String plusInfo      = mberCertPhoneVO.getPlusInfo();	// 추가DATA정보
    	
    	
    	String extendVar     = "0000000000000000";                  // 확장변수
        //End-tr_cert 데이터 변수 선언 ---------------------------------------------------------------

    	String tr_url     = getDomain(request) + mberCertPhoneVO.getTrUrl();// // 본인인증서비스 결과수신 POPUP URL
    	String tr_add     = "N";         // IFrame사용여부
        
    	//01. 한국모바일인증(주) 암호화 모듈 선언
//    	IcertSecuManager seed  = new IcertSecuManager();
    	com.icert.comm.secu.IcertSecuManager seed  = new com.icert.comm.secu.IcertSecuManager();

		//02. 1차 암호화 (tr_cert 데이터변수 조합 후 암호화)
		String enc_tr_cert = "";
		tr_cert            = cpId +"/"+ urlCode +"/"+ certNum +"/"+ date +"/"+ certMet +"/"+ birthDay +"/"+ gender +"/"+ name +"/"+ phoneNo +"/"+ phoneCorp +"/"+ nation +"/"+ plusInfo +"/"+ extendVar;
//		tr_cert = cpId +"/"+ urlCode +"/"+ certNum +"/"+ date +"/"+ certMet +"///////"+ plusInfo +"/"+ extendVar;
		enc_tr_cert        = seed.getEnc(tr_cert, "");

		//03. 1차 암호화 데이터에 대한 위변조 검증값 생성 (HMAC)
		String hmacMsg = "";
		hmacMsg = seed.getMsg(enc_tr_cert);

		//04. 2차 암호화 (1차 암호화 데이터, HMAC 데이터, extendVar 조합 후 암호화)
		tr_cert  = seed.getEnc(enc_tr_cert + "/" + hmacMsg + "/" + extendVar, "");
		
		AuthCertVO authCertVO = new AuthCertVO();
		
		authCertVO.setTr_cert(tr_cert);
		authCertVO.setTr_url(tr_url);
		authCertVO.setTr_add(tr_add);
		
		return authCertVO;
	}
	
	public KmcVO authCertResult(
								HttpServletRequest request
								, HttpServletResponse response
								, ModelMap model
							) throws IOException {
		
		//크롬 SameSite정책 방지 - 도메인이 다른 타사로 이동 시 크롬 정책에 의해 세션 유실이 일어나는 경우가 있는데, 이를 방지하기 위해 samesite 보안을 none처리
		response.setHeader("Set-Cookie", "mberSession=mberSession; Secure; SameSite=None");
		
		KmcVO kmcVO = new KmcVO(); //return VO
		
		// 변수 -------------------------------------------------------------------------------------------------------------
	    String api_token    = "";		    // 토큰값(암호화)
	    String api_certNum    = "";		    // 요청번호(암호화)

		String message      = "";		    // JSON 전문
		String result_cd    = "";		    // JSON 결과코드
		String result_msg   = "";		    // JSON 결과-상세
		String strResult    = "";		    // JSON 결과
		String apiRecCert   = "";		    // JSON 전송 데이터
		String apiCertNum   = "";		    // JSON 전송 데이터

	    String rec_cert		= "";           // 결과수신DATA
		String k_certNum 	= "";			// 파라미터로 수신한 요청번호
		String certNum		= "";			// 요청번호
	    String date			= "";			// 요청일시
		String CI	    	= "";			// 연계정보(CI)
		String DI	    	= "";			// 중복가입확인정보(DI)
	    String phoneNo		= "";			// 휴대폰번호
		String phoneCorp	= "";			// 이동통신사
		String birth		= "";			// 생년월일
		String gender		= "";			// 성별
		String nation		= "";			// 내국인
		String name			= "";			// 성명
		String reserve1		= "";			// 예비필드
		String reserve2		= "";			// 예비필드
		String reserve3		= "";			// 예비필드
		String reserve4		= "";			// 예비필드
	    String result		= "";			// 결과값

	    String certMet		= "";			// 인증방법
	    String ip			= "";			// ip주소
		String plusInfo		= "";

		String encPara		= "";
		String encMsg1		= "";
		String encMsg2		= "";
		String msgChk       = "";
	    //-----------------------------------------------------------------------------------------------------------------
		try{
			// Parameter 수신 --------------------------------------------------------------------
			api_token	   = request.getParameter("apiToken").trim();
			api_certNum = request.getParameter("certNum");

			// 파라미터 유효성 검증
			if( api_token.length() == 0 ){
				goErrorPage("토큰값 비정상", request, response);
				return kmcVO;
			}
			if( api_certNum.length() == 0 ){
				goErrorPage("요청번호 비정상", request, response);
				return kmcVO;
			}

			//현재시각 세팅(YYYYMMDDHI24MISS)
			Calendar today = Calendar.getInstance();
			SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmss");
			String api_date = sdf.format(today.getTime());

			//01. 암호화 모듈 (jar) Loading
			com.icert.comm.secu.IcertSecuManager seed    = new com.icert.comm.secu.IcertSecuManager();

			//api_token, api_certNum 복호화
			api_token  = seed.getDec(api_token, "");
			api_certNum  = seed.getDec(api_certNum, "");

			// 파라미터 유효성 검증
			if( api_token.length() == 0 ){
				goErrorPage("토큰값 비정상(복호화 후)", request, response);
				return kmcVO;
			}
			if( api_certNum.length() == 0 ){
				goErrorPage("요청번호 비정상(복호화 후)", request, response);
				return kmcVO;
			}

			// 1. URL 설정
			String serverURL = "https://www.kmcert.com/kmcis/api/kmcisToken_api.jsp";

			// 2. 연결 생성
			URL url = new URL(serverURL);
			
			// 3. HttpURLConnection 객체 생성.
			HttpURLConnection con = null;
			OutputStream wr = null;
			BufferedReader bufferedReader = null;

			// 4. URL 연결 (웹페이지 URL 연결.)
			con = (HttpURLConnection)url.openConnection();

			con.setConnectTimeout(20000);					// TimeOut 시간 (서버 접속시 연결 시간 - 20초)
			con.setReadTimeout(20000);						// TimeOut 시간 (Read시 연결 시간 - 20초)
			con.setDoOutput(true);							// OutputStream으로 POST 데이터를 넘겨주겠다는 옵션.
			
			con.setRequestProperty("Content-Type", "application/json;charset=utf-8");	// 타입설정(application/json) 형식으로 전송 (Request Body 전달시 application/json로 서버에 전달.)
			con.setRequestProperty("Accept", "application/json");						// 서버 Response Data를 JSON 형식의 타입으로 요청.
			con.setRequestMethod("POST");												// 요청 방식 선택 (POST)

			// 5. JSON 전문 구성
			
			JSONObject jsonData = new JSONObject();

			jsonData.put("apiToken",	api_token);
			jsonData.put("apiDate",		api_date);

			message = jsonData.toString();
			
			// 6. 전송
			// Request Body에 Data를 담기위해 OutputStream 객체를 생성.
			wr = con.getOutputStream();

			// Request Body에 Data 셋팅.(한글깨짐 방지를 위해 utf-8인코딩 처리
			wr.write(message.getBytes("utf-8"));
			wr.flush();
			wr.close();

			// 실제 서버로 Request 요청 하는 부분. (응답 코드를 받는다. 200 성공, 나머지 에러)
			int responseCode = con.getResponseCode();

			// 4. 결과 수신
			if(responseCode == 200){
				bufferedReader = new BufferedReader(new InputStreamReader(con.getInputStream(), "UTF-8"));
			}else{
				bufferedReader = new BufferedReader(new InputStreamReader(con.getErrorStream(), "UTF-8"));				
			}
			
			if(bufferedReader != null){
			
				StringBuilder stringBuilder = new StringBuilder();
				String line = "";
			
				while((line = bufferedReader.readLine()) != null){
					stringBuilder.append(line);
				}
				bufferedReader.close();
			
				String stringRet = stringBuilder.toString();
			
				// 5. 넘어온 문자열을 JSON 객체로 변환
				JSONParser jsonParser = new JSONParser();
			
				//JSON데이터를 넣어 JSON Object 로 만들어 준다.
				JSONObject jsonObj = (JSONObject)jsonParser.parse(stringRet);
			
				// 6. JSON 객체에서 데이터 가져오기
					
				if(jsonObj.get("result_cd") != null){
					result_cd = jsonObj.get("result_cd").toString();
					if("APR01".equals(result_cd)){
						//통신성공
						strResult = "Y";
						rec_cert  = jsonObj.get("apiRecCert").toString();
						k_certNum = jsonObj.get("apiCertNum").toString();
					}else if("APR02".equals(result_cd)){
						//실패 - Token Expire
						strResult = "N";
						result_msg = "실패 - Token Expire";
					}else if("APR03".equals(result_cd)){
						//실패 - Token Not Found
						strResult = "N";
						result_msg = "실패 - Token Not Found";
					}else if("APR04".equals(result_cd)){
						//실패 - API 요청일시 길이 오류
						strResult = "N";
						result_msg = "실패 - API 요청일시 길이 오류";
					}else if("APR05".equals(result_cd)){
						//실패 - API 토큰 길이 오류
						strResult = "N";
						result_msg = "실패 - API 토큰 길이 오류";
					}else if("APR06".equals(result_cd)){
						//실패 - 결과전송 재요청(3회 제한)
						strResult = "N";
						result_msg = "실패 - 결과전송 재요청(3회 제한)";
					}
				}else{
					//JSON 결과코드 에러
					strResult = "F";
				}
			}else{  //timeout except 처리
				strResult = "F";
			}

			// 파라미터 유효성 검증
			if(!strResult.equals("Y")){
				goErrorPage("결과값 비정상, 결과코드["+result_cd+"], "+"상세내용["+result_msg+"]", request, response);
				return kmcVO;
			}
			
			//02. 1차 복호화
	        rec_cert  = seed.getDec(rec_cert, "");

	        //03. 1차 파싱
	        int inf1 = rec_cert.indexOf("/",0);
	        int inf2 = rec_cert.indexOf("/",inf1+1);

			encPara  = rec_cert.substring(0,inf1);         //암호화된 통합 파라미터
	        encMsg1  = rec_cert.substring(inf1+1,inf2);    //암호화된 통합 파라미터의 Hash값

			//04. 위변조 검증
			encMsg2  = seed.getMsg(encPara);

	        if(encMsg2.equals(encMsg1)){
	            msgChk="Y";
	        }

			if(msgChk.equals("N")){
				goErrorPage("비정상적인 접근입니다.!!", request, response);
				return kmcVO;
			}
			
			//05. 2차 복호화
			rec_cert  = seed.getDec(encPara, "");

	        //06. 2차 파싱
	        int info1  = rec_cert.indexOf("/",0);
	        int info2  = rec_cert.indexOf("/",info1+1);
	        int info3  = rec_cert.indexOf("/",info2+1);
	        int info4  = rec_cert.indexOf("/",info3+1);
	        int info5  = rec_cert.indexOf("/",info4+1);
	        int info6  = rec_cert.indexOf("/",info5+1);
	        int info7  = rec_cert.indexOf("/",info6+1);
	        int info8  = rec_cert.indexOf("/",info7+1);
			int info9  = rec_cert.indexOf("/",info8+1);
			int info10 = rec_cert.indexOf("/",info9+1);
			int info11 = rec_cert.indexOf("/",info10+1);
			int info12 = rec_cert.indexOf("/",info11+1);
			int info13 = rec_cert.indexOf("/",info12+1);
			int info14 = rec_cert.indexOf("/",info13+1);
			int info15 = rec_cert.indexOf("/",info14+1);
			int info16 = rec_cert.indexOf("/",info15+1);
			int info17 = rec_cert.indexOf("/",info16+1);
			int info18 = rec_cert.indexOf("/",info17+1);

	        kmcVO.setCertNum	(rec_cert.substring(0,info1));
	        kmcVO.setDate		(rec_cert.substring(info1+1,info2));
	        //CI 복호화
	        kmcVO.setCI			(seed.getDec(rec_cert.substring(info2+1,info3), ""));
	        kmcVO.setPhoneNo	(rec_cert.substring(info3+1,info4));
	        kmcVO.setPhoneCorp	(rec_cert.substring(info4+1,info5));
	        kmcVO.setBirthDay	(rec_cert.substring(info5+1,info6));
	        kmcVO.setGender		(rec_cert.substring(info6+1,info7));
	        kmcVO.setNation		(rec_cert.substring(info7+1,info8));
			kmcVO.setName		(rec_cert.substring(info8+1,info9));
			kmcVO.setResult		(rec_cert.substring(info9+1,info10));
			kmcVO.setCertMet	(rec_cert.substring(info10+1,info11));
			kmcVO.setIp			(rec_cert.substring(info11+1,info12));
			kmcVO.setReserve1	(rec_cert.substring(info12+1,info13));
			kmcVO.setReserve2	(rec_cert.substring(info13+1,info14));
			kmcVO.setReserve3	(rec_cert.substring(info14+1,info15));
			kmcVO.setReserve4	(rec_cert.substring(info15+1,info16));
			kmcVO.setPlusInfo	(rec_cert.substring(info16+1,info17));
			//DI 복호화
			kmcVO.setDI      	(seed.getDec(rec_cert.substring(info17+1,info18), ""));

	        //07. CI, DI 복호화
//	        CI  = seed.getDec(CI, "");
//	        DI  = seed.getDec(DI, "");
	        
	     // ----------------------------------------------------------------------------------

	    }catch(StringIndexOutOfBoundsException ex){
	    	goErrorPage("StringIndexOutOfBoundsException", request, response);
	    }catch(NullPointerException ex){
	    	goErrorPage("NullPointerException", request, response);
	    }catch(NumberFormatException ex){
	    	goErrorPage("NumberFormatException", request, response);
	    }catch(IllegalStateException ex){
	    	goErrorPage("IllegalStateException", request, response);
	    }catch(IndexOutOfBoundsException ex){
	    	goErrorPage("IndexOutOfBoundsException", request, response);
	    } catch (IOException e) {
	    	goErrorPage("IOException", request, response);
		} catch (ParseException e) {
			goErrorPage("ParseException", request, response);
		}
		return kmcVO;
	}
	
	public AuthCertVO insertCertLog(KmcVO kmcVO, String msg) throws Exception {
		//KMC 본인인증 로그 insert
		AuthCertVO certVO = new AuthCertVO();
		certVO.setMberId(kmcVO.getPlusInfo());
		certVO.setCertNum(kmcVO.getCertNum());
		certVO.setCertDate(kmcVO.getDate());
		certVO.setCertDi(kmcVO.getDI());
		certVO.setCertPhone(kmcVO.getPhoneNo());
		certVO.setCertNation(kmcVO.getNation());
		certVO.setCertName(kmcVO.getName());
		certVO.setCertResult(kmcVO.getResult());
		certVO.setCertType(msg);
		certVO.setCertIpaddr(kmcVO.getIp());
		certVO.setBirthDay(kmcVO.getBirthDay());
		certVO.setSexdstnCode(kmcVO.getGender());

		//디비 테이블에 저장하기
		mberManageService.insertCertInfoLog(certVO);
		
		return certVO;
	}
	
	private String getDomain(HttpServletRequest request) {
		String serverNm = request.getScheme() + "://" + request.getServerName();
		if(request.getServerPort() != 80
			&& request.getServerPort() != 443) {
			serverNm += ":" + request.getServerPort();
		}
		
		return serverNm;
	}
	
	private String getDay() {
		Calendar today = Calendar.getInstance();
        SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmss");
        String day = sdf.format(today.getTime());
        
        return day;
	}
	
	private String getRanNum() {
		Random ran = new Random();
        //랜덤 문자 길이
        int numLength = 6;
        String randomStr = "";

        for (int i = 0; i < numLength; i++) {
            //0 ~ 9 랜덤 숫자 생성
            randomStr += ran.nextInt(10);
        }
        
        return randomStr;
	}
	
	private MberCertPhoneVO setMberCertPhoneVO(
													HttpServletRequest request
													, MberCertPhoneVO mberCertPhoneVO
												) {
		mberCertPhoneVO.setUrl(mberCertPhoneVO.getTrUrl());
		mberCertPhoneVO.setHost(getDomain(request));
		
		MberCertPhoneVO tmpMberCertPhoneVO = new MberCertPhoneVO();
		try {
			tmpMberCertPhoneVO = certPhoneService.selectCertUrlCode(mberCertPhoneVO);
		} catch (Exception e) {
			System.out.println(e.getMessage());
		}
		
		if(tmpMberCertPhoneVO != null) {
			mberCertPhoneVO.setUrl(tmpMberCertPhoneVO.getUrl());
			mberCertPhoneVO.setHost(tmpMberCertPhoneVO.getHost());
			mberCertPhoneVO.setUrlCode(tmpMberCertPhoneVO.getUrlCode());
		}
		
		return mberCertPhoneVO;
	}
	
	private void goErrorPage(String msg, HttpServletRequest request, HttpServletResponse response) throws IOException {
		FlashMap flashMap = RequestContextUtils.getOutputFlashMap(request);
		flashMap.put("msg", msg);
		FlashMapManager flashMapManager = RequestContextUtils.getFlashMapManager(request);
		flashMapManager.saveOutputFlashMap(flashMap, request, response);
		response.sendRedirect("/web/cert/log/kmcErrorPage.do");
	}
	
}
