package cokr.xit.ens.modules.kkoalimtalk.service.support;

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.CmmnUtil;
import cokr.xit.ens.modules.common.biztmplt.EnsPhaseProcSupport;
import cokr.xit.ens.modules.common.code.StatCd;
import cokr.xit.ens.modules.common.domain.SendMast;
import cokr.xit.ens.modules.common.domain.repository.SendMastRepository;
import cokr.xit.ens.modules.common.domain.support.FieldError;
import cokr.xit.ens.modules.common.event.SendMastStatUpdateEvent;
import cokr.xit.ens.modules.kkoalimtalk.code.AlimTalkSendTypeCd;
import cokr.xit.ens.modules.kkoalimtalk.code.AlimtalkResultCd;
import cokr.xit.ens.modules.kkoalimtalk.domain.SendDetailKkoAlimtalk;
import cokr.xit.ens.modules.kkoalimtalk.domain.SendDetailKkoAlimtalkRsltHist;
import cokr.xit.ens.modules.kkoalimtalk.domain.repository.SendDetailKkoAlimtalkRepository;
import cokr.xit.ens.modules.kkoalimtalk.domain.repository.SendDetailKkoAlimtalkRsltHistRepository;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.json.JsonParseException;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.stream.Collectors;

@Slf4j
@Component
@RequiredArgsConstructor
public class KkoAlimtalkRsltFetcher implements EnsPhaseProcSupport<EnsResponseVO, Map<String, Object>> {

    private final ApplicationEventPublisher applicationEventPublisher;

    private final KkoAlimtalkApi kkoAlimtalkApi;
    private final SendMastRepository sendMastRepository;
    private final SendDetailKkoAlimtalkRepository sendDetailKkoAlimtalkRepository;
    private final SendDetailKkoAlimtalkRsltHistRepository sendDetailKkoAlimtalkRsltHistRepository;

    @Override
    public EnsResponseVO statReady(List<Map<String, Object>> args) {
        log.info("no process");
        return null;
    }

    @Override
    public EnsResponseVO statBegin(Map<String, Object> arg) {
        log.info("no process");
        return null;
    }


    /**
     * 전송결과 Polling
     * -. 1회 최대 500건 까지 조회가 가능 하다.
     * -. 처리된 결과는 반드시 "전송결과 Polling ACK" API를 호출하여 수신완료 해야한다.
     * -. Ack 처리를 하지 않은 결과는 요청 시 다시 내려오게 된다.
     *
     * @param mArg
     */
    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public EnsResponseVO execute(Map<String, Object> mArg) {

        Long sendMastId = (Long) mArg.get("sendMastId");
        String btToken = (String) mArg.get("btToken");

        SendMast sendMast = null;
        EnsResponseVO respVO = null;
        try {
            Gson gson = new GsonBuilder().disableHtmlEscaping().create();

            sendMast = sendMastRepository.findById(sendMastId)
                    .orElseThrow(() -> new EnsException(EnsErrCd.SEND404, String.format("일치하는 발송마스터 자료가 없습니다. [ sendMastId %s ]", sendMastId)));
            if (CmmnUtil.isEmpty(btToken))
                throw new EnsException(EnsErrCd.RSLT410, String.format("전송요청토큰(은)는 필수조건 입니다. [ btToken %s ]", btToken));


            final int cntTry = 3;
            int cntResponseIsNull = 0;
            while (cntResponseIsNull < cntTry) {


                ResponseEntity<String> resp = kkoAlimtalkApi.getResultPoll(btToken);
                if (resp.getStatusCode() != HttpStatus.OK)
                    throw new EnsException(EnsErrCd.RSLT620, String.format("전송결과요청 중.. %s %s", resp.getStatusCode().toString(), resp.getBody()));
                Map<String, Object> mResponse = gson.fromJson(resp.getBody(), Map.class);
                if (!"1000".equals(mResponse.get("responseCode")))
                    throw new EnsException(EnsErrCd.RSLT601, String.format("%s %s", mResponse.get("responseCode"), mResponse.get("msg")));
                Map<String, Object> mResp = gson.fromJson(resp.getBody(), Map.class);
                if (CmmnUtil.isEmpty(mResp.get("response"))) {
                    cntResponseIsNull++;
                    if (cntTry == cntResponseIsNull)
                        throw new EnsException(EnsErrCd.RSLT601, "응답된 결과데이터가 없습니다. 비즈톡 문의가 필요 합니다.");


                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }

                    continue;
                }

                String pk = String.valueOf(mResp.get("pk"));

                List<SendDetailKkoAlimtalkRsltHist> sendDetailKkoAlimtalkRsltHists = this.convertRespToSendDetailAlimtalkRsltHist(mResp);
                sendDetailKkoAlimtalkRsltHistRepository.saveAll(sendDetailKkoAlimtalkRsltHists);

                kkoAlimtalkApi.ackResultPoll(btToken, pk);

                sendDetailKkoAlimtalkRsltHists.stream()
                        .forEach(row -> {
                            Optional<SendDetailKkoAlimtalk> sendDetail = sendDetailKkoAlimtalkRepository.findByReqMsgIdx(row.getReqMsgIdx());
                            if (sendDetail.isPresent()) {
                                sendDetail.get().setKkoSendType(AlimTalkSendTypeCd.valueOfEnum(row.getSendType()));
                                sendDetail.get().setKkoResultCd(AlimtalkResultCd.valueOfEnum("RS_" + row.getResultCode()));
                                sendDetail.get().setKkoRequestDt(CmmnUtil.isEmpty(row.getRequestAt()) ? sendDetail.get().getKkoRequestDt() : row.getRequestAt().replaceAll("[^0-9]", ""));
                                sendDetail.get().setKkoReceivedDt(CmmnUtil.isEmpty(row.getReceivedAt()) ? sendDetail.get().getKkoReceivedDt() : row.getReceivedAt().replaceAll("[^0-9]", ""));
                            }

                        });

                if (log.isDebugEnabled()) {
                    StringBuffer sb = new StringBuffer();
                    sb.append("\n==============================================================================")
                            .append("\n[ Kakao Alimtalk - Result Poll ]")
                            .append("\n### Request Info...")
                            .append("\nbt-token: " + btToken)
                            .append("\npk: " + pk)
                            .append("\n### Response Info...")
                            .append("\n" + resp.getBody())
                            .append("\n==============================================================================");
                    log.debug(sb.toString());
                }

                break;

            }


            respVO = EnsResponseVO.okBuilder().resultInfo("").build();
        } catch (JsonParseException e) {

            respVO = EnsResponseVO.errBuilder().errCode(EnsErrCd.RSLT411).errMsg(e.getMessage()).build();
        } catch (EnsException e) {

            respVO = EnsResponseVO.errBuilder().errCode(e.getErrCd()).errMsg(e.getMessage()).build();
        } catch (Exception e) {

            respVO = EnsResponseVO.errBuilder().errCode(EnsErrCd.RSLT500).errMsg(e.getMessage()).build();
        } finally {
            if (!CmmnUtil.isEmpty(sendMast))
                if (EnsErrCd.OK.equals(respVO.getErrCode())) {
                    sendMast.setStatCd(StatCd.sendcmplt);
                    sendMast.setError(FieldError.initBuilder().build());
                } else {
                    sendMast.setStatCd(StatCd.sendfail);
                    sendMast.setError(FieldError.initBuilder()
                            .errorCode(respVO.getErrCode().getCode())
                            .errorMessage(respVO.getErrMsg())
                            .build());
                }

            applicationEventPublisher.publishEvent(SendMastStatUpdateEvent.builder().sendMastId(sendMastId).build());
        }


        return respVO;
    }


    private List<SendDetailKkoAlimtalkRsltHist> convertRespToSendDetailAlimtalkRsltHist(Map<String, Object> mResp) {
        List<Map<String, String>> msgList = (List<Map<String, String>>) mResp.get("response");

        return msgList.stream()
                .map(row -> SendDetailKkoAlimtalkRsltHist.builder()
                        .pk(String.valueOf(mResp.get("pk")))
                        .responseCode(String.valueOf(mResp.get("responseCode")))
                        .msg(row.get("msg"))
                        .uuid(row.get("uid"))
                        .reqMsgIdx(row.get("msgIdx"))
                        .resultCode(row.get("resultCode"))
                        .receivedAt(row.get("receivedAt"))
                        .requestAt(row.get("requestAt"))
                        .bsid(row.get("bsid"))
                        .sendType(row.get("sendType"))
                        .build())
                .collect(Collectors.toList());
    }


}
