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.core.utils.DateUtil;
import cokr.xit.ens.core.utils.IdGenerator;
import cokr.xit.ens.core.utils.MapDeserailizer;
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.intgrnbill.support.code.PaidTypeCd;
import cokr.xit.ens.modules.common.ctgy.sys.mng.domain.OrgMng;
import cokr.xit.ens.modules.common.ctgy.sys.mng.domain.ids.TmpltMngIds;
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.kkoalimtalk.code.AlimtalkResultCd;
import cokr.xit.ens.modules.kkoalimtalk.domain.SendDetailKkoAlimtalk;
import cokr.xit.ens.modules.kkoalimtalk.domain.TmpltMngKkoAlimtalk;
import cokr.xit.ens.modules.kkoalimtalk.domain.repository.SendDetailKkoAlimtalkRepository;
import cokr.xit.ens.modules.kkoalimtalk.domain.repository.TmpltMngKkoAlimtalkRepository;
import cokr.xit.ens.modules.kkoalimtalk.model.KkoAlimtalkAcceptReqDTO;
import cokr.xit.ens.modules.kkoalimtalk.model.config.Attachment;
import cokr.xit.ens.modules.kkoalimtalk.model.config.Button;
import cokr.xit.ens.modules.kkoalimtalk.model.config.Document;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
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 KkoAlimtalkAcceptor implements EnsPhaseProcSupport<EnsResponseVO, KkoAlimtalkAcceptReqDTO> {
    private final SendMastRepository sendMastRepository;
    private final SendDetailKkoAlimtalkRepository sendDetailKkoAlimtalkRepository;
    private final BillRepository billRepository;
    private final TmpltMngKkoAlimtalkRepository tmpltMngKkoAlimtalkRepository;
    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<KkoAlimtalkAcceptReqDTO> reqDTOs) {
        log.info("no process");
        return null;
    }

    @Override
    public EnsResponseVO statBegin(KkoAlimtalkAcceptReqDTO reqDTO) {
        final Validator validator = Validation.buildDefaultValidatorFactory().getValidator();
        Set<ConstraintViolation<KkoAlimtalkAcceptReqDTO>> list = validator.validate(reqDTO);
        if (list.size() > 0) {
            return EnsResponseVO.errRsltBuilder()
                    .errCode(EnsErrCd.ACPT410)
                    .errMsg("유효하지 않은 요청값 입니다.")
                    .resultInfo(list.stream()
                            .map(row -> String.format("%s [ %s ]", row.getMessageTemplate(), row.getPropertyPath()))
                            .collect(Collectors.toList()))
                    .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(), "kkoat");
        if (!EnsErrCd.OK.equals(tmpltMng.getErrCode()))
            return EnsResponseVO.errRsltBuilder()
                    .errCode(EnsErrCd.ACPT404)
                    .errMsg("일치하는 템플릿정보가 없습니다.")
                    .resultInfo(tmpltMng.getErrMsg())
                    .build();

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

    @Override
    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public EnsResponseVO execute(KkoAlimtalkAcceptReqDTO reqDTO) throws EnsException {
        EnsResponseVO responseVO = null;

        try {
            TmpltMngKkoAlimtalk tmpltMngKkoAlimtalk = tmpltMngKkoAlimtalkRepository.findById(TmpltMngIds.builder().orgMng(reqDTO.getOrg_cd()).tmpltCd(reqDTO.getTmplt_cd()).build())
                    .orElseThrow(() -> new EnsException(EnsErrCd.SEND404, String.format("일치하는 템플릿 자료가 없습니다. [ orgCd %s tmpltCd %s ]", reqDTO.getOrg_cd(), reqDTO.getTmplt_cd())));
            final String biztalkOrgCd = tmpltMngKkoAlimtalk.getBiztalkOrgCd();
            final String biztalkTmpltCode = tmpltMngKkoAlimtalk.getBiztalkTmpltCode();


            SendMast sendMast = SendMast.builder()
                    .vender(VenderCd.valueOfEnum(reqDTO.getVender()))
                    .postSe(PostSeCd.kkoAlimtalk)
                    .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<SendDetailKkoAlimtalk> sendDetails = new ArrayList<>();
            List<Bill> bills = new ArrayList<>();
            final String prefixBillUid = sendMast.getPostSe().getCode() + "-" + IdGenerator.getCurrentTimeSec();
            AtomicInteger i = new AtomicInteger();
            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);
                    }

                    Attachment attach = this.createAttach(document);


                    SendDetailKkoAlimtalk sendDetail = SendDetailKkoAlimtalk.builder()
                            .sendMast(sendMast)
                            .msgIdx(document.getMsg_idx())
                            .countryCode(document.getCountry_code())
                            .recipient(document.getRecipient())
//                                    .senderKey(document.getSender_key())
                            .appUserId(document.getApp_user_id())
                            .orgCode(biztalkOrgCd)
                            .message(null)
                            .tmpltCode(biztalkTmpltCode)
                            .title(document.getTitle())
                            .resMethod(document.getRes_method())
                            .attach(CmmnUtil.isEmpty(attach) ? null : gson.toJson(attach))
                            .supplement(CmmnUtil.isEmpty(document.getSupplement()) ? null : gson.toJson(document.getSupplement()))
                            .messageType(document.getMessage_type())
                            .useFailback(document.getUse_failback())
                            .mmsAttach(CmmnUtil.isEmpty(document.getMms_attach()) ? null : gson.toJson(document.getMms_attach()))
                            .mkBillUseYn(CmmnUtil.isEmpty(bill) ? "N" : "Y")
//                            .mkBillUid(bill == null ? null : bill.getBillUid())
                            .bill(bill)
                            .mkTmpltMsgJsonData(CmmnUtil.isEmpty(document.getXit_property().getTmplt_msg_data()) ? null : gson.toJson(document.getXit_property().getTmplt_msg_data()))
                            .kkoResultCd(AlimtalkResultCd.NULL)
                            .build();

                    sendDetails.add(sendDetail);

                } 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())
            );
            sendDetailKkoAlimtalkRepository.saveAll(sendDetails);


            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;
    }


    /**
     * 버튼정보 생성
     *
     * @param document
     * @return
     */
    private Attachment createAttach(Document document) {
        if (CmmnUtil.isEmpty(document.getXit_property())) return document.getAttach();
        if (CmmnUtil.isEmpty(document.getXit_property().getBill_acpt_data())) return document.getAttach();


        if (CmmnUtil.isEmpty(document.getAttach()) || CmmnUtil.isEmpty(document.getAttach().getButton())) {
            if (CmmnUtil.isEmpty(document.getAttach()))
                document.setAttach(Attachment.builder().build());
            if (CmmnUtil.isEmpty(document.getAttach().getButton())) {
                List<Button> buttons = new ArrayList<>();
//                if (!CmmnUtil.isEmpty(document.getXit_property().getBill_acpt_data().getBill_kko()))
//                    buttons.add(this.createBillButton("납부하기(카카오페이)", String.format("#{billlink_url_%s}", BillSeCd.bpKko.getCode())));
                if (!CmmnUtil.isEmpty(document.getXit_property().getBill_acpt_data())) {
                    switch (document.getXit_property().getBill_acpt_data().getBillSe()) {
                        case privt:
                        case bpKko:
                            buttons.add(this.createBillButton(String.format("납부하기(%s)", PaidTypeCd.kakaoPay.getCodeNm()), String.format("#{billlink_url_%s}", PaidTypeCd.kakaoPay.getCode())));
                            break;
                        case bpNv:
                            buttons.add(this.createBillButton(String.format("납부하기(%s)", PaidTypeCd.naverPay.getCodeNm()), String.format("#{billlink_url_%s}", PaidTypeCd.naverPay.getCode())));
                            break;
                        default:
                            Arrays.stream(PaidTypeCd.values()).forEach(paidTypeCd -> this.createBillButton(String.format("납부하기(%s)", paidTypeCd.getCodeNm()), String.format("#{billlink_url_%s}", paidTypeCd.getCode())));
                            break;
                    }
                }

                document.getAttach().setButton(buttons);
            }

        } else {
            List<Button> buttons = document.getAttach().getButton();
            if (!CmmnUtil.isEmpty(document.getXit_property().getBill_acpt_data())) {
                switch (document.getXit_property().getBill_acpt_data().getBillSe()) {
                    case privt:
                    case bpKko:
                        buttons.add(this.createBillButton(String.format("납부하기(%s)", PaidTypeCd.kakaoPay.getCodeNm()), String.format("#{billlink_url_%s}", PaidTypeCd.kakaoPay.getCode())));
                        break;
                    case bpNv:
                        buttons.add(this.createBillButton(String.format("납부하기(%s)", PaidTypeCd.naverPay.getCodeNm()), String.format("#{billlink_url_%s}", PaidTypeCd.naverPay.getCode())));
                        break;
                    default:
                        Arrays.stream(PaidTypeCd.values()).forEach(paidTypeCd -> this.createBillButton(String.format("납부하기(%s)", paidTypeCd.getCodeNm()), String.format("#{billlink_url_%s}", paidTypeCd.getCode())));
                        break;
                }
            }
        }


        return document.getAttach();

    }

    /**
     * 청구서링크 버튼 생성
     *
     * @param name
     * @param urlKey
     * @return
     */
    private Button createBillButton(String name, String urlKey) {
        return Button.builder()
                .name(name)
                .type("WL")
                .url_mobile(urlKey)
                .url_pc(urlKey)
                .build();
    }

}
