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.monitor.slack.event.MonitorEvent;
import cokr.xit.ens.core.utils.CmmnUtil;
import cokr.xit.ens.core.utils.IdGenerator;
import cokr.xit.ens.core.utils.MapDeserailizer;
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.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.kkoalimtalk.domain.SendDetailKkoAlimtalk;
import cokr.xit.ens.modules.kkoalimtalk.domain.repository.SendDetailKkoAlimtalkRepository;
import cokr.xit.ens.modules.kkoalimtalk.model.TmpltMngKkoAlimtalkDTO;
import cokr.xit.ens.modules.kkoalimtalk.service.strategy.TmpltMngStrategyKkoAlimtalk;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ForkJoinPool;

@Slf4j
@Component
@RequiredArgsConstructor
public class KkoAlimtalkMaker extends MakeProcTemplate {
    private final ApplicationEventPublisher applicationEventPublisher;
    private final SendMastRepository sendMastRepository;
    private final SendDetailKkoAlimtalkRepository sendDetailKkoAlimtalkRepository;
    private final TmpltMngStrategyKkoAlimtalk tmpltMngService;


    @Value("${app.pallel.thread.basic.cnt}")
    private int CNT_THREAD;


    private Gson gson = new GsonBuilder().registerTypeAdapter(Map.class, new MapDeserailizer()).serializeNulls().create();


    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public EnsResponseVO execute(Long sendMastId) {

        SendMast sendMast = null;
        EnsResponseVO respVO = null;
        try {

            if (CmmnUtil.isEmpty(sendMastId))
                throw new EnsException(EnsErrCd.MAKE410, "발송마스터ID(은)는 필수조건 입니다.");



            sendMast = sendMastRepository.findById(sendMastId)
                    .orElseThrow(() -> new EnsException(EnsErrCd.MAKE500, String.format("no exists 발송마스터 data. [ sendMastId %s ]", sendMastId)));

            List<SendDetailKkoAlimtalk> sendDetails = sendDetailKkoAlimtalkRepository.findAllFetchBySendMastId(sendMastId);
            if (CmmnUtil.isEmpty(sendDetails))
                throw new EnsException(EnsErrCd.MAKE404, String.format("발송상세 자료가 없습니다. [ sendMastId %s ]", sendMastId));

            SendMast finalSendMast = sendMast;
//            TmpltMng tmpltMngDTO = sendTmpltMngRepository.findFetchByOrgCdAndTmpltCdAndUseYn(sendMast.getOrgCd(), sendMast.getTmpltCd(), "Y")
//                    .orElseThrow(() -> new EnsException(EnsErrCd.MAKE500, String.format("no exists 메시지 템플릿. [ orgCd %s tmpltCd %s ]", finalSendMast.getOrgCd(), finalSendMast.getTmpltCd())));
            TmpltMngKkoAlimtalkDTO 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()));



//            String prefixReqMsgIdx = String.format("MD%s%s", LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMdd")), RandomString.make(5));
            String prefixReqMsgIdx = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy"));
            ForkJoinPool forkJoinPool = new ForkJoinPool(CNT_THREAD);
            Optional<Integer> cntSuccess = Optional.of(0);
            try {
                cntSuccess = forkJoinPool.submit(() -> {
                    return sendDetails.parallelStream()
                            .map(row -> {
                                try {

                                    row.setError(FieldError.initBuilder().build());


                                    Map<String, Object> mTmpltData = null;
                                    try {
                                        mTmpltData = gson.fromJson(row.getMkTmpltMsgJsonData(), Map.class);
                                    } catch (Exception e) {
                                        throw new EnsException(EnsErrCd.MAKE411, String.format("템플릿 데이터 mkTmpltJsonData 변환에 실패 했습니다. %s", row.getMkTmpltMsgJsonData()));
                                    }
                                    boolean usePryMsg = CmmnUtil.isEmpty(tmpltMngDTO.getPryMessage()) ? false : true;
                                    String msg = this.makeMessage(usePryMsg ? tmpltMngDTO.getPryMessage() : tmpltMngDTO.getMessage(), mTmpltData);

                                    row.setMessage(msg);

                                    row.setReqMsgIdx(IdGenerator.getUUID(prefixReqMsgIdx));
//                                    if (!CmmnUtil.isEmpty(row.getBill().getBillUid()) && !CmmnUtil.isEmpty(row.getAttach()) && row.getAttach().contains("billlink_url")) {
//                                        Map<String, String> urlMap = new HashMap<>();
//                                        billSeRepositoryMap.keySet()
//                                                .forEach(billSeCd -> {
//                                                    urlMap.put(billSeCd, billSeRepositoryMap.get(billSeCd).findUrlByBillUid(row.getBill().getBillUid()));
//                                                });
//                                        urlMap.entrySet()
//                                                .forEach(entry -> {
//                                                    row.setAttach(row.getAttach().replace("#{billlink_url_" + entry.getKey() + "}", entry.getValue()));
//                                                });
//                                    }

                                    log.info("==============================================================================");
                                    log.info("### Kakao Alimtalk - makingTmpltMsg");
                                    log.info("[ Request Info... ]");
                                    log.info(String.format("SEND_DETAIL_ID: %s ORG_CD: %s TMPLT_CD: %s TITLE: %s", row.getSendDetailId(), finalSendMast.getOrgCd(), finalSendMast.getTmpltCd(), tmpltMngDTO.getTitle()));
                                    log.info(row.getMkMblJsonData());
                                    log.info("[ Response Info... ]");
                                    log.info(msg);
                                    log.info("==============================================================================");
                                } catch (EnsException e) {

                                    row.setError(FieldError.initBuilder()
                                            .errorCode(e.getErrCd().getCode())
                                            .errorMessage(e.getMessage())
                                            .build());
                                } catch (Exception e) {

                                    row.setError(FieldError.initBuilder()
                                            .errorCode(EnsErrCd.MAKE500.getCode())
                                            .errorMessage("템플릿메시지 생성 오류. " + e.getMessage())
                                            .build());

                                } finally {
                                    return row;
                                }
                            })
                            .map(row -> CmmnUtil.isEmpty(row.getError()) || CmmnUtil.isEmpty(row.getError().getErrorCode()) || EnsErrCd.OK.getCode().equals(row.getError().getErrorCode()) ? 1 : 0)
                            .reduce(Integer::sum);

                }).get();

            } catch (InterruptedException e) {

                forkJoinPool.shutdown();
                throw new EnsException(EnsErrCd.MAKE500, "템플릿메시지 생성에 실패 했습니다. Interrupted => " + e.getMessage());
            } catch (ExecutionException e) {

                forkJoinPool.shutdown();
                throw new EnsException(EnsErrCd.MAKE500, "템플릿메시지 생성에 실패 했습니다. Execution => " + e.getMessage());
            }



            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, "템플릿메시지 생성에 실패 했습니다.");

        } 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.kkoAlimtalk)
                                    .statCd(StatCd.makefail)
                                    .errCd(respVO.getErrCode())
                                    .message("-.SendMastId: " + sendMastId + "\n" + respVO.getErrMsg())
                                    .build())
                            .build());
                }

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

        return respVO;
    }


    private String makeMessage(String tmplt, Map<String, Object> mTmpltData) {
        if (CmmnUtil.isEmpty(tmplt)) return tmplt;
        if (CmmnUtil.isEmpty(mTmpltData)) return tmplt;

        Iterator<String> it = mTmpltData.keySet().iterator();
        while (it.hasNext()) {
            String key = it.next();
            tmplt = tmplt.replace(key, String.valueOf(mTmpltData.get(key)));
        }

        return tmplt;
    }


}
