package cokr.xit.ens.modules.common.ctgy.nicedici.support.rest.api;

import cokr.xit.ens.core.utils.CmmnUtil;
import cokr.xit.ens.core.utils.DateUtil;
import cokr.xit.ens.modules.common.ctgy.nicedici.support.rest.code.NiceCiApiCd;
import cokr.xit.ens.modules.common.ctgy.nicedici.support.rest.model.NiceCiRespDTO;
import cokr.xit.ens.modules.common.ctgy.nicedici.support.rest.model.NiceCiRespVO;
import cokr.xit.ens.modules.common.ctgy.nicedici.support.rest.model.conf.DataBodyGenerateTokenResp;
import cokr.xit.ens.modules.common.ctgy.nicedici.support.rest.model.conf.DataBodyRevokeTokenResp;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.type.TypeReference;
import lombok.experimental.SuperBuilder;
import lombok.extern.slf4j.Slf4j;
import org.apache.logging.log4j.util.Base64Util;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;

import java.nio.charset.Charset;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;

@Slf4j
@SuperBuilder
public class TokenGenerate extends NiceCiApiAbstract {

    protected final String HOST;
    protected final String API_URL_GENERATE_TOKEN;
    protected final String API_URL_REVOKE_TOKEN;


    public NiceCiRespVO<DataBodyGenerateTokenResp> execute(String clientId, String clientSecret) throws Exception {
        if (CmmnUtil.isEmpty(clientId))
            return NiceCiRespVO.<DataBodyGenerateTokenResp>errBuilder().errCode(NiceCiApiCd.FAIL).errMsg("클라이언트ID는 필수 입력값 입니다.").build();
        if (CmmnUtil.isEmpty(clientSecret))
            return NiceCiRespVO.<DataBodyGenerateTokenResp>errBuilder().errCode(NiceCiApiCd.FAIL).errMsg("클라이언트비밀번호는 필수 입력값 입니다.").build();



        NiceCiRespDTO<DataBodyGenerateTokenResp> generateResponseDTO = this.generateToken(clientId, clientSecret);


        if ("1800".equals(generateResponseDTO.getDataHeader().getGW_RSLT_CD()))
            generateResponseDTO = this.generateToken(clientId, clientSecret);
        if (!"1200".equals(generateResponseDTO.getDataHeader().getGW_RSLT_CD()))
            return NiceCiRespVO.<DataBodyGenerateTokenResp>errBuilder()
                    .errCode(NiceCiApiCd.valueOfEnum("HEAD_" + generateResponseDTO.getDataHeader().getGW_RSLT_CD()))
                    .errMsg(String.format("[%s]: %s", generateResponseDTO.getDataHeader().getGW_RSLT_CD(), generateResponseDTO.getDataHeader().getGW_RSLT_MSG()))
                    .build();
        if (DateUtil.secToDays(generateResponseDTO.getDataBody().getExpiresIn()) < 30) { 
           
            NiceCiRespDTO<DataBodyRevokeTokenResp> revokeResponseDTO = this.revokeToken(generateResponseDTO.getDataBody().getAccessToken(), clientId);
            if (!"1200".equals(revokeResponseDTO.getDataHeader().getGW_RSLT_CD()))
                return NiceCiRespVO.<DataBodyGenerateTokenResp>errBuilder()
                        .errCode(NiceCiApiCd.valueOfEnum("HEAD_" + generateResponseDTO.getDataHeader().getGW_RSLT_CD()))
                        .errMsg(String.format("[%s]: %s", generateResponseDTO.getDataHeader().getGW_RSLT_CD(), generateResponseDTO.getDataHeader().getGW_RSLT_MSG()))
                        .build();
            if(!revokeResponseDTO.getDataBody().getResult())
                return NiceCiRespVO.<DataBodyGenerateTokenResp>errBuilder().errCode(NiceCiApiCd.FAIL).errMsg(String.format("토큰(%s) 폐기에 실패 했습니다.", generateResponseDTO.getDataBody().getAccessToken())).build();
           
            generateResponseDTO = this.generateToken(clientId, clientSecret);
            if (!"1200".equals(generateResponseDTO.getDataHeader().getGW_RSLT_CD()))
                return NiceCiRespVO.<DataBodyGenerateTokenResp>errBuilder()
                        .errCode(NiceCiApiCd.valueOfEnum("HEAD_" + generateResponseDTO.getDataHeader().getGW_RSLT_CD()))
                        .errMsg(String.format("[%s]: %s", generateResponseDTO.getDataHeader().getGW_RSLT_CD(), generateResponseDTO.getDataHeader().getGW_RSLT_MSG()))
                        .build();
        }



        return NiceCiRespVO.<DataBodyGenerateTokenResp>okBuilder().resultInfo(generateResponseDTO.getDataBody()).build();
    }


    protected NiceCiRespDTO<DataBodyGenerateTokenResp> generateToken(String clientId, String clientSecret) throws Exception {
        try {


            String authorizationToken = Base64Util.encode(String.format("%s:%s", clientId, clientSecret));
            HttpHeaders headers = new HttpHeaders();
            headers.setContentType(new MediaType(MediaType.APPLICATION_FORM_URLENCODED, Charset.forName("utf-8")));
            headers.set("Authorization", String.format("Basic %s", authorizationToken));


            StringBuilder url = new StringBuilder();
            url.append(this.HOST)
                    .append(API_URL_GENERATE_TOKEN);
            MultiValueMap<String, String> body = this.createMessage();


            ResponseEntity<String> resp = this.callApi(HttpMethod.POST, url.toString(), body, headers);
            log.info("==================================================================================");
            log.info("==== 토큰발급 요청 Result Info... ====");
            log.info("[Headers]: " + resp.getHeaders().toString());
            log.info("[Body]: " + resp.getBody());
            log.info("==================================================================================");


//            NiceCiRespDTO<DataBodyGenerateTokenResp> respDTO = mapper.readValue(resp.getBody(), NiceCiRespDTO.class);
            String strRespBody = resp.getBody().replace("\"\"", "null");
            NiceCiRespDTO<DataBodyGenerateTokenResp> respDTO = mapper.readValue(strRespBody, new TypeReference<NiceCiRespDTO<DataBodyGenerateTokenResp>>(){});
            if(!(respDTO.getDataBody()==null||"".equals(respDTO.getDataBody())))
                respDTO.getDataBody().setExpiredDt(this.addSec(respDTO.getDataBody().getExpiresIn(), "yyyyMMddHHmmss"));


            return respDTO;
        } catch (Exception e) {
            e.printStackTrace();
            throw new Exception("토큰발급 요청 실패." + e.getMessage());
        }
    }


    protected NiceCiRespDTO<DataBodyRevokeTokenResp> revokeToken(String accessToken, String clientId) throws Exception {
        try {
            String bearerToken = Base64Util.encode(String.format("%s:%s:%s", accessToken, (new Date().getTime() / 1000), clientId));
            HttpHeaders headers = new HttpHeaders();
            headers.setContentType(new MediaType(MediaType.APPLICATION_FORM_URLENCODED, Charset.forName("utf-8")));
            headers.set("Authorization", String.format("Basic %s", bearerToken));


            StringBuilder url = new StringBuilder();
            url.append(this.HOST)
                    .append(API_URL_REVOKE_TOKEN);


            ResponseEntity<String> resp = this.callApi(HttpMethod.POST, url.toString(), null, headers);
            log.info("==================================================================================");
            log.info("==== 토큰폐기 요청 Result Info... ====");
            log.info("[Headers]: " + resp.getHeaders().toString());
            log.info("[Body]: " + resp.getBody());
            log.info("==================================================================================");


//            NiceCiRespDTO<DataBodyRevokeTokenResp> respDTO = mapper.readValue(resp.getBody(), NiceCiRespDTO.class);
            String strRespBody = resp.getBody().replace("\"\"", "null");
            NiceCiRespDTO<DataBodyRevokeTokenResp> respDTO = mapper.readValue(strRespBody, new TypeReference<NiceCiRespDTO<DataBodyRevokeTokenResp>>(){});


            return respDTO;
        } catch (Exception e) {
            e.printStackTrace();
            throw new Exception("토큰폐기 요청 실패." + e.getMessage());
        }
    }


    /**
     * JSON 포맷의 요청메시지 생성
     *
     * @return
     * @throws JsonProcessingException
     */
    protected MultiValueMap<String, String> createMessage() throws JsonProcessingException {
        MultiValueMap<String, String> m = new LinkedMultiValueMap<>();
        m.add("grant_type", "client_credentials");
        m.add("scope", "default");

        return m;
    }



    /**
     * 현재날짜에 초(sec)를 더한 날짜를 반환 한다.
     * @param sec
     * @param pattern
     * @return
     */
    private String addSec(Integer sec, String pattern){
        if(sec==null)
            return null;

        if("".equals(pattern)||pattern==null)
            pattern = "yyyyMMdd";

        Calendar calendar = Calendar.getInstance();
        calendar.setTimeInMillis((sec*1000L) + (new Date().getTime()));
        Date date = calendar.getTime();

        return new SimpleDateFormat(pattern).format(date);
    }

}
