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.utils.CmmnUtil;
import cokr.xit.ens.core.utils.DateUtil;
import cokr.xit.ens.core.utils.IdGenerator;
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.modules.common.biztmplt.EnsPhaseProcSupport;
import cokr.xit.ens.modules.common.code.PostSeCd;
import cokr.xit.ens.modules.common.code.StatCd;
import cokr.xit.ens.modules.common.code.VenderCd;
import cokr.xit.ens.modules.common.ctgy.intgrnbill.support.entity.Bill;
import cokr.xit.ens.modules.common.ctgy.intgrnbill.support.entity.repository.BillRepository;
import cokr.xit.ens.modules.common.ctgy.mblpage.domain.SendDetailMblPage;
import cokr.xit.ens.modules.common.ctgy.mblpage.domain.repository.SendDetailMblPageRepository;
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.ctgy.sys.mng.service.TmpltMngService;
import cokr.xit.ens.modules.common.domain.SendMast;
import cokr.xit.ens.modules.common.domain.repository.SendMastRepository;
import cokr.xit.ens.modules.ktsigntalk.direct.code.KtSigntalkStatusCd;
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.KtSigntalkAcceptReqDTO;
import cokr.xit.ens.modules.ktsigntalk.direct.model.config.XitProperty;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import net.bytebuddy.utility.RandomString;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import javax.validation.ConstraintViolation;
import javax.validation.Validation;
import javax.validation.Validator;
import java.util.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;

@Slf4j
@Component
@RequiredArgsConstructor
public class KtSigntalkAcceptor implements EnsPhaseProcSupport<EnsResponseVO, KtSigntalkAcceptReqDTO> {

    private final SendMastRepository sendMastRepository;
    private final SendDetailKtSigntalkRepository sendDetailKtSigntalkRepository;
    private final SendDetailMblPageRepository sendDetailMblPageRepository;
    private final BillRepository billRepository;
    private final OrgMngService orgMngService;
    private final TmpltMngService tmpltMngService;

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

    @Override
    public EnsResponseVO statReady(List<KtSigntalkAcceptReqDTO> reqDTOs) {
        log.info("no process");
        return null;
    }

    @Override
    public EnsResponseVO statBegin(KtSigntalkAcceptReqDTO reqDTO) {
        final Validator validator = Validation.buildDefaultValidatorFactory().getValidator();
        Set<ConstraintViolation<KtSigntalkAcceptReqDTO>> list = validator.validate(reqDTO);
        if (list.size() > 0) {
            return EnsResponseVO.errRsltBuilder()
                    .errCode(EnsErrCd.ACPT402)
                    .errMsg("유효하지 않은 요청값 입니다.")
                    .resultInfo(list.stream()
                            .map(row -> String.format("%s [ %s ]", row.getMessageTemplate(), row.getPropertyPath()))
                            .collect(Collectors.toList()))
                    .build();
        }
        List<String> expiredDateValidateList = expiredDateVaildate(reqDTO);
        if (expiredDateValidateList.size() > 0) {
            return EnsResponseVO.errRsltBuilder()
                    .errCode(EnsErrCd.ACPT402)
                    .errMsg("유효하지 않은 유효기한 입니다.")
                    .resultInfo(expiredDateValidateList)
                    .build();
        }
        EnsResponseVO orgMng = orgMngService.find(reqDTO.getOrg_cd());
        if (!EnsErrCd.OK.equals(orgMng.getErrCode()))
            return EnsResponseVO.errRsltBuilder()
                    .errCode(EnsErrCd.ACPT404)
                    .errMsg("일치하는 기관정보가 없습니다.")
                    .resultInfo(orgMng.getErrMsg())
                    .build();
        EnsResponseVO tmpltMng = tmpltMngService.find(reqDTO.getOrg_cd(), reqDTO.getTmplt_cd(), "ktst");
        if (!EnsErrCd.OK.equals(tmpltMng.getErrCode()))
            return EnsResponseVO.errRsltBuilder()
                    .errCode(EnsErrCd.ACPT404)
                    .errMsg("일치하는 템플릿정보가 없습니다.")
                    .resultInfo(tmpltMng.getErrMsg())
                    .build();

        return EnsResponseVO.okBuilder().build();
    }

    private List<String> expiredDateVaildate(KtSigntalkAcceptReqDTO reqDTO) {
        List<String> result = new ArrayList<>();

        AtomicInteger i = new AtomicInteger();
        reqDTO.getDocuments().forEach(document -> {
            if (!CmmnUtil.isEmpty(document)) {
                if (!CmmnUtil.isEmpty(document.getExTime())) {
                    String expDate = document.getExTime();
                    int sec = DateUtil.secByFromBetweenTo(DateUtil.toLocalDateTime(expDate), DateUtil.toLocalDateTime(reqDTO.getClose_dt()));
                    if (sec < 0)
                        result.add(String.format("마감일시보다 \"처리마감시간\"이 느립니다. [ document[%d].acpt_data.kt_st.extTime ]", i.get()));
                }

            }
            i.getAndIncrement();
        });

        return result;
    }

    @Override
    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public EnsResponseVO execute(KtSigntalkAcceptReqDTO reqDTO) throws EnsException {
        AES256 aes256 = new AES256(Crypto.AES256.getKey());
        EnsResponseVO responseVO = null;
        try {
            SendMast sendMast = SendMast.builder()
                    .vender(VenderCd.valueOfEnum(reqDTO.getVender()))
                    .postSe(PostSeCd.ktSigntalk)
                    .orgCd(reqDTO.getOrg_cd())
                    .tmpltCd(reqDTO.getTmplt_cd())
                    .postBundleTitle(reqDTO.getPost_bundle_title())
                    .statCd(StatCd.accept)
                    .sendDt(DateUtil.toLocalDateTime(reqDTO.getSend_dt()))
                    .sendCnt(reqDTO.getDocuments().size())
                    .closeDt(DateUtil.toLocalDateTime(reqDTO.getClose_dt()))
                    .build();
            sendMastRepository.save(sendMast);


            Gson gson = new GsonBuilder().disableHtmlEscaping().create();
            List<SendDetailKtSigntalk> sendDetails = new ArrayList<>();
            List<Map<String, Object>> sendDetailMblPages = new ArrayList<>();
            List<Bill> bills = new ArrayList<>();
            final String prefixBillUid = sendMast.getPostSe().getCode() + "-" + IdGenerator.getCurrentTimeSec();
            AtomicInteger i = new AtomicInteger();
            final String prefixSrcKey = RandomString.make(10);
            reqDTO.getDocuments().forEach(document -> {
                i.getAndIncrement();
                try {

                    Bill bill = null;
                    if (!CmmnUtil.isEmpty(document.getXit_property())
                            && !CmmnUtil.isEmpty(document.getXit_property().getBill_acpt_data())) {
//                        if (document.getXit_property().getBill_acpt_data().getUse_bill_uid()) {
                        if (!CmmnUtil.isEmpty(document.getXit_property().getBill_acpt_data().getBillUid())) {
                            bill = billRepository.findByBillUid(document.getXit_property().getBill_acpt_data().getBillUid())
                                    .orElseThrow(() -> new RuntimeException("등록된 청구서가 없습니다."));
                        } else {
                            bill = Bill.builder()
//                                    .billId(document.getXit_property().getBill_acpt_data().getBill_id())
                                    .billId(null)
                                    .billUid(IdGenerator.getShortUUID(prefixBillUid))
                                    .billerUserKey(document.getXit_property().getBill_acpt_data().getBillerUserKey())
                                    .billSeCd(document.getXit_property().getBill_acpt_data().getBillSe())
                                    .orgMng(OrgMng.builder().orgCd(reqDTO.getOrg_cd()).build())
//                                    .docBillKko(CmmnUtil.isEmpty(document.getXit_property().getBill_acpt_data().getBill_kko()) ? null : gson.toJson(document.getXit_property().getBill_acpt_data().getBill_kko()))
//                                    .docBillNv(CmmnUtil.isEmpty(document.getXit_property().getBill_acpt_data().getBill_nv()) ? null : gson.toJson(document.getXit_property().getBill_acpt_data().getBill_nv()))
                                    .build();
                        }
                        bills.add(bill);
                    }
                    XitProperty xitProperty = Optional.ofNullable(document.getXit_property()).orElse(XitProperty.builder().build());


                    SendDetailKtSigntalk sendDetail = SendDetailKtSigntalk.builder()
                            .sendMast(sendMast)
//                                    .title(document.getTitle())
                            .clientSrcKey(document.getClientSrcKey())
                            .srcKey(IdGenerator.getUUID(prefixSrcKey))
                            .hash(document.getHash())
                            .srcSeq(document.getSrcSeq())
                            .dBirth(document.getDBirth())
                            .juminFirstNo(document.getJuminFirstNo())
                            .cnPerson(document.getCnPerson())
                            .cZip(document.getCZip())
                            .addr(document.getAddr())
                            .rcvTelNo(document.getRcvTelNo())
                            .sndTelNo(document.getSndTelNo())
                            .rcvPwdUseYn(document.getRcvPwdUseYn())
                            .ci(document.getCi())
                            .mmsDtlCnts(document.getMmsDtlCnts())
                            .mmsTitle(document.getMmsTitle())
                            .sendTel(document.getSendTel())
                            .url(document.getUrl())
                            .exTime(document.getExTime())
                            .mType(document.getMType())
                            .rcvType(document.getRcvType())
//                            .mkBillUseYn(CmmnUtil.isEmpty(kkoBillMast) ? "N" : "Y")
                            .mkBillUseYn(CmmnUtil.isEmpty(bill) ? "N" : "Y")
//                            .kkoBillMast(kkoBillMast)
                            .bill(bill)
//                            .mkJid(xitProperty.getJid())
                            .mkJid(aes256.encrypt(xitProperty.getJid()))
                            .mkTmpltMsgJsonData(CmmnUtil.isEmpty(xitProperty.getTmplt_msg_data()) ? null : gson.toJson(xitProperty.getTmplt_msg_data()))
                            .docStat(KtSigntalkStatusCd.NULL)
                            .build();
                    sendDetails.add(sendDetail);

                    if (!(CmmnUtil.isEmpty(document.getXit_property()) || CmmnUtil.isEmpty(document.getXit_property().getMobile_page()))) {
                        Map<String, Object> mMblPage = new HashMap<>();
                        mMblPage.put("sendDetail", sendDetail);
                        mMblPage.put("details", gson.toJson(document.getXit_property().getMobile_page()));
                        sendDetailMblPages.add(mMblPage);
                    }

                } catch (Exception e) {
                    throw new EnsException(EnsErrCd.ACPT500, String.format("%d번째 데이터 접수처리 중 오류 발생. 실패사유: %s", i.get(), e.getMessage()));
                }
            });
            if (CmmnUtil.isEmpty(sendDetails))
                throw new EnsException(EnsErrCd.ACPT410, "발송상세 자료가 없습니다.");


            billRepository.saveAll(bills.stream()
                    .filter(row -> CmmnUtil.isEmpty(row.getBillId()))
                    .collect(Collectors.toList())
            );
            sendDetailKtSigntalkRepository.saveAll(sendDetails);
            if (sendDetailMblPages.size() > 0)
                sendDetailMblPageRepository.saveAll(sendDetailMblPages.stream()
                        .map(data -> {
                            SendDetailKtSigntalk sendDetail = (SendDetailKtSigntalk) data.get("sendDetail");
                            return SendDetailMblPage.builder()
                                    .postSe(sendDetail.getSendMast().getPostSe().getCode())
                                    .sendDetailId(sendDetail.getSendDetailId())
                                    .details((String) data.get("details"))
                                    .build();
                        })
                        .collect(Collectors.toList())
                );


            Map<String, Object> resultInfo = new HashMap<>();
            resultInfo.put("sendMastId", sendMast.getSendMastId());
            resultInfo.put("sendDetails", sendDetails);
            responseVO = EnsResponseVO.okBuilder().resultInfo(resultInfo).build();
        } catch (EnsException e) {
            throw e;
        } catch (Exception e) {
            throw new EnsException(EnsErrCd.ACPT500, String.format("오류가 발생하여 접수에 실패 했습니다. 실패사유: %s", e.getMessage()));
        }

        return responseVO;
    }

}
