package cokr.xit.ens.modules.ktsigntalk.direct.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.monitor.slack.event.MonitorEvent;
import cokr.xit.ens.core.utils.CmmnUtil;
import cokr.xit.ens.core.utils.MapDeserailizer;
import cokr.xit.ens.core.utils.crypto.AES256;
import cokr.xit.ens.core.utils.crypto.Crypto;
import cokr.xit.ens.core.utils.crypto.SHA256;
import cokr.xit.ens.modules.common.biztmplt.MakeProcTemplate;
import cokr.xit.ens.modules.common.code.PostSeCd;
import cokr.xit.ens.modules.common.code.StatCd;
import cokr.xit.ens.modules.common.ctgy.nicedici.service.NiceDiCiService;
import cokr.xit.ens.modules.common.ctgy.sys.mng.domain.OrgMng;
import cokr.xit.ens.modules.common.ctgy.sys.mng.service.OrgMngService;
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.common.monitor.MessageByPhase;
import cokr.xit.ens.modules.ktsigntalk.direct.domain.SendDetailKtSigntalk;
import cokr.xit.ens.modules.ktsigntalk.direct.domain.repository.SendDetailKtSigntalkRepository;
import cokr.xit.ens.modules.ktsigntalk.direct.model.TmpltMngKtSigntalkDTO;
import cokr.xit.ens.modules.ktsigntalk.direct.service.strategy.TmpltMngStrategyKtSigntalk;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;
import java.util.stream.Collectors;

@Slf4j
@Component
@RequiredArgsConstructor
public class KtSigntalkMaker extends MakeProcTemplate {

    private final ApplicationEventPublisher applicationEventPublisher;
    private final SendMastRepository sendMastRepository;
    private final SendDetailKtSigntalkRepository sendDetailKtSigntalkRepository;
    private final OrgMngService orgMngService;
    private final TmpltMngStrategyKtSigntalk tmpltMngService;
    private final NiceDiCiService niceDiCiService;


    @Override
    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public EnsResponseVO execute(Long sendMastId) {
        AES256 aes256 = new AES256(Crypto.AES256.getKey());
        SendMast sendMast = null;
        List<SendDetailKtSigntalk> sendDetails = null;
        EnsResponseVO respVO = null;
        try {
            if (CmmnUtil.isEmpty(sendMastId))
                throw new EnsException(EnsErrCd.MAKE410, "발송마스터ID(은)는 필수조건 입니다.");

            sendMast = sendMastRepository.findById(sendMastId)
                    .orElseThrow(() -> new EnsException(EnsErrCd.MAKE404, String.format("일치하는 발송마스터 자료가 없습니다. [ sendMastId %s ]", sendMastId)));
            sendDetails = sendDetailKtSigntalkRepository.findAllBySendMast(sendMast);
            SendMast finalSendMast = sendMast;
            OrgMng orgMng = orgMngService.find(sendMast.getOrgCd()).getResultInfo();
            TmpltMngKtSigntalkDTO tmpltMngDTO = tmpltMngService.find(sendMast.getOrgCd(), sendMast.getTmpltCd())
                    .orElseThrow(() -> new EnsException(EnsErrCd.MAKE404, String.format("일치하는 템플릿 자료가 없습니다. [ sendMastId %s orgCd %s TmpltCd %s ]", sendMastId, finalSendMast.getOrgCd(), finalSendMast.getTmpltCd())));
            if (!"Y".equals(tmpltMngDTO.getUseYn()))
                throw new EnsException(EnsErrCd.MAKE521, String.format("미사용 상태의 템플릿 입니다. [ sendMastId %s orgCd %s TmpltCd %s ]", sendMastId, finalSendMast.getOrgCd(), finalSendMast.getTmpltCd()));



            Map<String, String> mCi = new HashMap<>();
            if ("Y".equals(tmpltMngDTO.getCiTransUseYn())) {
                List<String> jids = sendDetails.stream()

                        .filter(row -> !CmmnUtil.isEmpty(row.getMkJid()))
//                        .map(row -> row.getMkJid())
                        .map(row -> aes256.decrypt(row.getMkJid()))
                        .collect(Collectors.toList());
                if (!CmmnUtil.isEmpty(jids)) {
                    EnsResponseVO ciResponseVO = niceDiCiService.ci(orgMng.getNiceCdSiteCode(), orgMng.getNiceCdSitePw(), orgMng.getNiceCdClientId(), orgMng.getNiceCdClientSercet(), jids);
                    if (!EnsErrCd.OK.equals(ciResponseVO.getErrCode()))
                        throw new EnsException(EnsErrCd.MAKE513, String.format("%s %s", ciResponseVO.getErrCode(), ciResponseVO.getErrMsg()));

                    List<EnsResponseVO> responseVOList = (List<EnsResponseVO>) ciResponseVO.getResultInfo();
                    mCi = responseVOList.stream()
                            .map(vo -> {
                                Map<String, String> resultInfo = (Map<String, String>) vo.getResultInfo();
                                Map<String, String> m = new HashMap<>();
                                m.put("key", resultInfo.get("jid"));
                                m.put("value", CmmnUtil.isEmpty(resultInfo.get("ci")) ? "" : resultInfo.get("ci"));
                                return m;
                            })
                            .collect(Collectors.toMap(m -> m.get("key"), m -> m.get("value"), (k1, k2) -> k1));
                }
            }

            Map<String, String> finalMCi = mCi;
            Optional<Integer> cntSuccess = sendDetails.stream()
                    .map(row -> {
                        try {

                            row.setMmsTitle(Optional.ofNullable(tmpltMngDTO.getTitle())
                                    .orElseThrow(() -> new EnsException(EnsErrCd.MAKE521, String.format("mmsTitle 생성에 실패 했습니다. [ orgCd %s tmpltCd %s]", tmpltMngDTO.getOrgCd(), tmpltMngDTO.getTmpltCd()))));

                            if ("Y".equals(tmpltMngDTO.getTmpltCsInfoUseYn()))
                                row.setSndTelNo(Optional.ofNullable(tmpltMngDTO.getSndTelNo())
                                        .orElseThrow(() -> new EnsException(EnsErrCd.MAKE521, String.format("sndTelNo 생성에 실패 했습니다. [ orgCd %s tmpltCd %s]", tmpltMngDTO.getOrgCd(), tmpltMngDTO.getTmpltCd()))));

                            if ("Y".equals(tmpltMngDTO.getTmpltCsInfoUseYn()))
                                row.setSendTel(Optional.ofNullable(tmpltMngDTO.getSendTel())
                                        .orElseThrow(() -> new EnsException(EnsErrCd.MAKE521, String.format("sendTel 생성에 실패 했습니다. [ orgCd %s tmpltCd %s]", tmpltMngDTO.getOrgCd(), tmpltMngDTO.getTmpltCd()))));

                            if ("Y".equals(tmpltMngDTO.getTmpltMsgUseYn())) {
                                boolean usePryMsg = CmmnUtil.isEmpty(tmpltMngDTO.getPryMessage()) ? false : true;
                                row.setMmsDtlCnts(Optional.ofNullable(this.msgTmplateToMessage(usePryMsg, tmpltMngDTO, row.getMkTmpltMsgJsonData()))
                                        .orElseThrow(() -> new EnsException(EnsErrCd.MAKE521, String.format("mmsDtlCnts 생성에 실패 했습니다. [ orgCd %s tmpltCd %s tmpltMsgJsonData %s ]", tmpltMngDTO.getOrgCd(), tmpltMngDTO.getTmpltCd(), row.getMkTmpltMsgJsonData()))));
                            }

                            if (CmmnUtil.isEmpty(row.getHash()))
                                row.setHash(Optional.ofNullable(this.createHash(row))
                                        .orElseThrow(() -> new EnsException(EnsErrCd.MAKE521, String.format("hash 생성에 실패 했습니다."))));

                            if ("Y".equals(tmpltMngDTO.getCiTransUseYn()) && !CmmnUtil.isEmpty(row.getMkJid()))
//                                row.setCi(Optional.ofNullable(finalMCi.get(row.getMkJid().replaceAll("[^0-9]", "")))
                                row.setCi(Optional.ofNullable(finalMCi.get(aes256.decrypt(row.getMkJid()).replaceAll("[^0-9]", "")))
                                        .map(ci -> CmmnUtil.isEmpty(ci) ? null : ci)
                                        .orElseThrow(() -> new EnsException(EnsErrCd.MAKE610, String.format("ci 생성에 실패 했습니다. 주민번호에 대한 CI 값이 없습니다. [ jid %s ]", aes256.decrypt(row.getMkJid())))));

                            row.setError(FieldError.initBuilder().build());
                            return 1;
                        } catch (EnsException e) {
                            row.setError(FieldError.initBuilder()
                                    .errorCode(e.getErrCd().getCode())
                                    .errorMessage(e.getMessage())
                                    .build());
                            return 0;
                        }
                    })
                    .reduce(Integer::sum);



            if (cntSuccess.get() > 0)
                respVO = EnsResponseVO.okBuilder().resultInfo(String.format("총 %d건 중 %d건 제작 성공. [ sendMastId %s ]", sendDetails.size(), cntSuccess.get(), sendMastId)).build();
            else
                throw new EnsException(EnsErrCd.MAKE500, String.format("총 %d 건 제작 실패", sendDetails.size()));
        } catch (EnsException e) {

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

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

                    applicationEventPublisher.publishEvent(MonitorEvent.builder()
                            .message(MessageByPhase.builder()
                                    .oClass(getClass().getSimpleName() + "." + new Throwable().getStackTrace()[0].getMethodName())
                                    .postSeCd(PostSeCd.ktSigntalk)
                                    .statCd(StatCd.makefail)
                                    .errCd(respVO.getErrCode())
                                    .message("-.SendMastId: " + sendMastId + "\n" + respVO.getErrMsg())
                                    .build())
                            .build());
                }


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


        return respVO;
    }

    /**
     * Hash값을 생성 한다.
     *
     * @param vo
     * @return
     */
    private String createHash(SendDetailKtSigntalk vo) {
        String hash = null;
        try {
            String text = new StringBuilder()
                    .append(vo.getClientSrcKey())
                    .append(vo.getUrl())
                    .append(vo.getMmsDtlCnts())
                    .toString();
            hash = SHA256.encrypt(text);
        } catch (Exception e) {
            log.error("hash 값 생성 실패. [ sendDetailId {} ]", vo.getSendDetailId());
        }

        return hash;
    }


    private String msgTmplateToMessage(boolean usePryMsg, TmpltMngKtSigntalkDTO tmpltMngDTO, String tmplateMsgData) {
        String message = usePryMsg ? tmpltMngDTO.getPryMessage() : tmpltMngDTO.getMessage();
        if (CmmnUtil.isEmpty(tmplateMsgData))
            return message;

        Gson gson = new GsonBuilder().registerTypeAdapter(Map.class, new MapDeserailizer()).serializeNulls().create();
        try {
            Map<String, Object> mMsgData = gson.fromJson(tmplateMsgData, Map.class);
            Iterator<String> it = mMsgData.keySet().iterator();
            while (it.hasNext()) {
                String key = it.next();
                message = message.replace(key, String.valueOf(mMsgData.get(key)));
            }
        } catch (Exception e) {
            log.error("template Message 변환 실패. [ orgCd {} tmpltCd {} msgData {} ]", tmpltMngDTO.getOrgCd(), tmpltMngDTO.getTmpltCd(), tmplateMsgData, e);
            return null;
        }
        return message;
    }
}
