package cokr.xit.ens.modules.common.ctgy.intgrnnoti.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.modules.common.biztmplt.MakeProcTemplate;
import cokr.xit.ens.modules.common.code.IntgrnDtlStatCd;
import cokr.xit.ens.modules.common.code.PostSeCd;
import cokr.xit.ens.modules.common.code.StatCd;
import cokr.xit.ens.modules.common.ctgy.intgrnnoti.code.TryStatCd;
import cokr.xit.ens.modules.common.ctgy.intgrnnoti.domain.IntgrnSendDetail;
import cokr.xit.ens.modules.common.ctgy.intgrnnoti.domain.IntgrnSendMast;
import cokr.xit.ens.modules.common.ctgy.intgrnnoti.domain.repository.IntgrnSendDetailRepository;
import cokr.xit.ens.modules.common.ctgy.intgrnnoti.domain.repository.IntgrnSendMastRepository;
import cokr.xit.ens.modules.common.ctgy.intgrnnoti.model.TmpltMngIntgrnDTO;
import cokr.xit.ens.modules.common.ctgy.intgrnnoti.service.strategy.TmpltMngStrategyIntegration;
import cokr.xit.ens.modules.common.domain.support.FieldError;
import cokr.xit.ens.modules.common.monitor.MessageByPhase;
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.List;
import java.util.Optional;
import java.util.stream.Collectors;

@Slf4j
@Component
@RequiredArgsConstructor
public class IntgrnNotiMaker extends MakeProcTemplate {
    private final IntgrnSendMastRepository intgrnSendMastRepository;
    private final IntgrnSendDetailRepository intgrnSendDetailRepository;
    private final TmpltMngStrategyIntegration tmpltMngService;
    private final ApplicationEventPublisher applicationEventPublisher;


    /**
     * 제작준비
     *
     * @param intSendMastIds
     * @return
     */
    @Override
    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public EnsResponseVO statReady(List<Long> intSendMastIds) {
        EnsResponseVO respVO = null;
        try {
            /**
             * 필수값 확인
             */
            if (CmmnUtil.isEmpty(intSendMastIds))
                throw new EnsException(EnsErrCd.MAKE410, "발송마스터ID(은)는 필수조건 입니다.");

            /**
             * 저장
             *  -. 발송마스터의 상태를 "제작준비(makerdy)" 으로 변경 한다.
             */
            List<Long> resultInfo = intgrnSendMastRepository.findAllByIntSendMastIdIn(intSendMastIds).stream()
                    .map(row -> {
                        row.setStatCd(StatCd.makerdy);
                        return row.getIntSendMastId();
                    })
                    .collect(Collectors.toList());
            ;

            respVO = EnsResponseVO.okBuilder().resultInfo(resultInfo).build();
        } 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();
        }

        return respVO;
    }


    /**
     * 제작시작
     *
     * @param intSendMastId
     * @return
     */
    @Override
    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public EnsResponseVO statBegin(Long intSendMastId) {

        EnsResponseVO respVO = null;
        try {
            /**
             * 필수값 확인
             */
            if (CmmnUtil.isEmpty(intSendMastId))
                throw new EnsException(EnsErrCd.MAKE410, "발송마스터ID(은)는 필수조건 입니다.");

            /**
             * 저장
             *  -. 발송마스터의 상태를 "제작중(making)" 으로 변경 한다.
             */
            IntgrnSendMast intgrnSendMast = intgrnSendMastRepository.findById(intSendMastId).orElseThrow(() -> new EnsException(EnsErrCd.MAKE404, String.format("일치하는 발송마스터 자료가 없습니다. [ intSendMastId %s ]", intSendMastId)));
            intgrnSendMast.setStatCd(StatCd.making);


            respVO = EnsResponseVO.okBuilder().resultInfo(intSendMastId).build();
        } 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();
        }

        return respVO;
    }


    @Override
    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public EnsResponseVO execute(Long intSendMastId) {
        IntgrnSendMast intgrnSendMast = null;
        List<IntgrnSendDetail> intgrnSendDetails = null;
        EnsResponseVO respVO = null;
        try {
            if (CmmnUtil.isEmpty(intSendMastId))
                throw new EnsException(EnsErrCd.MAKE410, "발송마스터ID(은)는 필수조건 입니다.");

            intgrnSendMast = intgrnSendMastRepository.findById(intSendMastId)
                    .orElseThrow(() -> new EnsException(EnsErrCd.MAKE404, String.format("일치하는 발송마스터 자료가 없습니다. [ intSendMastId %s ]", intSendMastId)));
            intgrnSendDetails = intgrnSendDetailRepository.findAllByIntgrnSendMast(intgrnSendMast);
            IntgrnSendMast finalIntgrnSendMast = intgrnSendMast;
            TmpltMngIntgrnDTO tmpltMngDTO = tmpltMngService.find(intgrnSendMast.getOrgCd(), intgrnSendMast.getTmpltCd())
                    .orElseThrow(() -> new EnsException(EnsErrCd.MAKE404, String.format("일치하는 템플릿 자료가 없습니다. [ intSendMastId %s orgCd %s TmpltCd %s ]", intSendMastId, finalIntgrnSendMast.getOrgCd(), finalIntgrnSendMast.getTmpltCd())));
            if (!"Y".equals(tmpltMngDTO.getUseYn()))
                throw new EnsException(EnsErrCd.MAKE521, String.format("미사용 상태의 템플릿 입니다. [ intSendMastId %s orgCd %s TmpltCd %s ]", intSendMastId, finalIntgrnSendMast.getOrgCd(), finalIntgrnSendMast.getTmpltCd()));


            Optional<Integer> cntSuccess = intgrnSendDetails.stream()
                    .map(row -> {
                        try {
                            row.setCurStatCd(IntgrnDtlStatCd.MAKING);

                            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건 제작 성공. [ intSendMastId %s ]", intgrnSendDetails.size(), cntSuccess.get(), intSendMastId)).build();
            else
                throw new EnsException(EnsErrCd.MAKE500, String.format("총 %d 건 제작 실패", intgrnSendDetails.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(intgrnSendMast))
                if (EnsErrCd.OK.equals(respVO.getErrCode())) {
                    intgrnSendMast.setStatCd(StatCd.makeok);
                    intgrnSendMast.setTryStatCd(TryStatCd.stby);
                    intgrnSendMast.setError(FieldError.initBuilder().build());
                } else {
                    intgrnSendMast.setStatCd(StatCd.makefail);
                    intgrnSendMast.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.intgrnNoti)
                                    .statCd(StatCd.makefail)
                                    .errCd(respVO.getErrCode())
                                    .message("-.IntSendMastId: " + intSendMastId + "\n" + respVO.getErrMsg())
                                    .build())
                            .build());
                }
        }


        return respVO;
    }


}
