package kr.xit.core.spring.util;

import java.net.*;
import java.nio.charset.*;
import java.util.*;

import org.apache.commons.lang3.*;
import org.springframework.http.*;
import org.springframework.stereotype.*;
import org.springframework.web.client.*;
import org.springframework.web.util.*;

import kr.xit.biz.ens.model.cmm.*;
import kr.xit.core.consts.*;
import kr.xit.core.exception.*;
import kr.xit.core.model.*;
import kr.xit.core.spring.config.support.*;
import kr.xit.core.support.utils.*;
import lombok.*;
import lombok.extern.slf4j.*;

/**
 * <pre>
 * description : react Restfull Config
 * packageName : kr.xit.core.spring.support
 * fileName    : ApiWebClientUtil
 * author      : julim
 * date        : 2023-09-06
 * ======================================================================
 * 변경일         변경자        변경 내용
 * ----------------------------------------------------------------------
 * 2023-09-06    julim       최초 생성
 *
 * </pre>
 */
@Slf4j
@Component
@RequiredArgsConstructor
public class ApiRestTemplateUtil {
    private static final String AUTH_TYPE_BEARER = "bearer";
    private final RestTemplate restTemplate;
    
    /**
     * kakao WebClient 호출 처리
     * 에러(.onStatus status.is4xxClientError() || status.is5xxServerError())
     * -> {@link WebClientConfig responseFilter} 에서 처리
     * @param url String
     * @param method HttpMethod
     * @param body Object
     * @param rtnClzz Class<T>
     * @param ensDTO CmmEnsRlaybsnmDTO
     * @return rtnClzz<T>
     */
    public <T> T exchangeKkopay(final String url, final HttpMethod method, final Object body, final Class<T> rtnClzz, final CmmEnsRlaybsnmDTO ensDTO) {

        Map<String, String> map = new HashMap<>();
        map.put(HttpHeaders.ACCEPT_CHARSET, StandardCharsets.UTF_8.name());
        map.put(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE + ";charset=UTF-8");
        map.put(HttpHeaders.AUTHORIZATION,
            String.format("%s %s", Constants.JwtToken.GRANT_TYPE.getCode(), ensDTO.getKakaoAccessToken()));
        map.put(Constants.HeaderName.UUID.getCode(), ensDTO.getKakaoContractUuid());

        return exchange(url, method, body, rtnClzz, map);
    }

    public <T> T exchangeKkopayAsIsCan(final String url, final HttpMethod method, final Object body, final Class<T> rtnClzz, Map<String, String> headerMap) {
        return exchange(url, method, body, rtnClzz, headerMap);
    }

    /**
     * kakaotalk WebClient 호출 처리
     * 에러(.onStatus status.is4xxClientError() || status.is5xxServerError())
     * -> {@link WebClientConfig responseFilter} 에서 처리
     * @param url String
     * @param method HttpMethod
     * @param body Object
     * @param rtnClzz Class<T>
     * @param ensDTO CmmEnsRlaybsnmDTO
     * @return rtnClzz<T>
     */
    public <T> T exchangeKkotalk(final String url, final HttpMethod method, final Object body, final Class<T> rtnClzz, final CmmEnsRlaybsnmDTO ensDTO) {

        Map<String, String> map = new HashMap<>();
        map.put(HttpHeaders.ACCEPT_CHARSET, StandardCharsets.UTF_8.name());
        map.put(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE + ";charset=UTF-8");
        map.put(HttpHeaders.AUTHORIZATION, String.format("KakaoAK %s", ensDTO.getKakaoDealerRestApiKey()));
        map.put(Constants.JwtToken.PARTNER_REST_API_KEY.getCode(), String.format("KakaoAK %s", ensDTO.getKakaoPartnerRestApiKey()));
        map.put(Constants.JwtToken.SETTLE_ID.getCode(), ensDTO.getKakaoSettleId());

        return exchange(url, method, body, rtnClzz, map);
    }

    /**
     * KT-BC WebClient 호출 처리
     * 에러(.onStatus status.is4xxClientError() || status.is5xxServerError())
     * -> {@link WebClientConfig responseFilter} 에서 처리
     * @param url String
     * @param method HttpMethod
     * @param body Object
     * @param rtnClzz Class<T>
     * @param ensDTO CmmEnsRlaybsnmDTO
     * @return rtnClzz<T>
     */
    public <T> T exchangeKt(final String url, final HttpMethod method, final Object body, final Class<T> rtnClzz, final CmmEnsRlaybsnmDTO ensDTO) {
        final Map<String,String> headerMap = new HashMap<>();
        headerMap.put(HttpHeaders.AUTHORIZATION, String.format("%s %s", AUTH_TYPE_BEARER, ensDTO.getKtAccessToken()));
        headerMap.put("client-id", ensDTO.getKtClientId());
        headerMap.put("client-tp", ensDTO.getKtClientTp());

        return exchange(url, method, body, rtnClzz, headerMap);
    }

    /**
     * KT-GIBIS WebClient 호출 처리
     * 에러(.onStatus status.is4xxClientError() || status.is5xxServerError())
     * -> {@link WebClientConfig responseFilter} 에서 처리
     * @param url String
     * @param method HttpMethod
     * @param body Object
     * @param rtnClzz Class<T>
     * @param ensDTO CmmEnsRlaybsnmDTO
     * @return rtnClzz<T>
     */
    public <T> T exchangeKtGbs(final String url, final HttpMethod method, final Object body, final Class<T> rtnClzz, final CmmEnsRlaybsnmDTO ensDTO) {
        final Map<String,String> headerMap = new HashMap<>();
        headerMap.put(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE);
        headerMap.put(HttpHeaders.AUTHORIZATION, String.format("Bearer %s", ensDTO.getKtAccessToken()));

        return exchange(url, method, body, rtnClzz, headerMap);
    }

    /**
     * <pre>
     * WebClient 호출 처리
     * GET 요청시 url에 파라메터를 포함해야 함(?key=value&key2=value2)
     * 에러(.onStatus status.is4xxClientError() || status.is5xxServerError())
     * -> {@link WebClientConfig responseFilter} 에서 처리
     * @param url call url
     * @param method POST|GET
     * @param body JSON String type
     * @param rtnClzz rtnClzz return type class
     *        (ex: new KkopayDocDTO.DocStatusResponse().getClass())
     * @return T rtnClzz return DTO
     * </pre>
     */
    public <T> T exchange(final String url, final HttpMethod method, final Object body, final Class<T> rtnClzz, final Map<String,String> headerMap) {
        
        if(HttpMethod.POST.equals(method)) {

            return restTemplate.exchange(
                url,
                method,
                new HttpEntity<>(ObjectUtils.isEmpty(body) ? null: body, getHeaders(headerMap)),
                rtnClzz
            ).getBody();
        }else{
            return restTemplate.exchange(
                url,
                //createUrl(url),
                method,
                new HttpEntity<>(getHeaders(headerMap)),
                rtnClzz,
                body
            ).getBody();
        }
    }

    /**
     * <pre>
     * WebClient form data 호출 처리
     * -> application/x-www-form-urlencoded 전송시
     * GET 요청시 url에 파라메터를 포함해야 함(?key=value&key2=value2)
     * 에러(.onStatus status.is4xxClientError() || status.is5xxServerError())
     * -> {@link WebClientConfig responseFilter} 에서 처리
     * @param url call url
     * @param method POST|GET
     * @param body JSON String type
     * @param rtnClzz rtnClzz return type class
     *        (ex: new KkopayDocDTO.DocStatusResponse().getClass())
     * @return T rtnClzz return DTO
     * </pre>
     */
    public <T> T exchangeFormData(final String url, final HttpMethod method, final Object body, final Class<T> rtnClzz, final Map<String,String> headerMap) {

        return exchange(url, method, JsonUtils.toMultiValue(body), rtnClzz, headerMap);
    }

    public <T> ApiResponseDTO<T> sendError(final Throwable e) {

        return ErrorParse.extractError(e.getCause());
    }
    private URI createUrl(final String endPoint, final String... value) {
        return UriComponentsBuilder.fromUriString(endPoint)
            .build(value);
    }

    private HttpHeaders getHeaders(final HttpHeaders headers, final Map<String, String> map) {
        if(ObjectUtils.isEmpty(map))    return headers;
        for(Map.Entry<String, String> e : map.entrySet()){
            headers.add(e.getKey(), e.getValue());
        }
        return headers;
    }

    private HttpHeaders getHeaders(final Map<String, String> map) {
        HttpHeaders headers = new HttpHeaders();
        if(ObjectUtils.isEmpty(map))    return headers;
        for(Map.Entry<String, String> e : map.entrySet()){
            headers.add(e.getKey(), e.getValue());
        }
        return headers;
    }
}
