package cokr.xit.ens.modules.kkoalimtalk.service;

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.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.kkoalimtalk.model.KkoAlimtalkAcceptReqDTO;
import cokr.xit.ens.modules.kkoalimtalk.service.support.*;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

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

@Slf4j
@Service
@RequiredArgsConstructor
public class KkoAlimtalkService {

    private final KkoAlimtalkAcceptor kkoAlimtalkAcceptor;
//    private final KkoAlimtalkMakerOfBillLink kkoAlimtalkMakerOfBillLink;
    private final KkoAlimtalkMaker kkoAlimtalkMaker;
    private final KkoAlimtalkSender kkoAlimtalkSender;
    private final KkoAlimtalkRsltFetcher kkoAlimtalkRsltFetcher;
    private final KkoAlimtalkRsltProvider kkoAlimtalkRsltProvider;

    private final SendMastRepository sendMastRepository;



    /**
     * 접수 서비스
     * @param reqDTO
     * @return
     */
    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public EnsResponseVO accept(KkoAlimtalkAcceptReqDTO reqDTO){
        EnsResponseVO responseVO = null;
        try {
            responseVO = kkoAlimtalkAcceptor.statBegin(reqDTO);
            if(!EnsErrCd.OK.equals(responseVO.getErrCode()))
                return responseVO;

            responseVO = kkoAlimtalkAcceptor.execute(reqDTO);
        } catch (EnsException e){
            responseVO = EnsResponseVO.errBuilder()
                    .errCode(e.getErrCd())
                    .errMsg(e.getMessage())
                    .build();
        }

        return responseVO;
    }



    /**
     * (일괄)제작 서비스
     * @return
     */
    @Transactional(propagation = Propagation.SUPPORTS)
    public EnsResponseVO makeAll(){
        List<Long> sendMastIds = sendMastRepository.findAllByPostSeAndStatCdIn(PostSeCd.kkoAlimtalk, Arrays.asList(StatCd.accept))
                .stream()
                .map(row -> row.getSendMastId())
                .collect(Collectors.toList());
        if(sendMastIds.size()<1)
            return EnsResponseVO.errBuilder()
                    .errCode(EnsErrCd.MAKE404)
                    .errMsg("\"접수(accept)\" 상태인 자료가 없습니다")
                    .build();

        kkoAlimtalkMaker.statReady(sendMastIds);

        List<EnsResponseVO> resultInfo = sendMastIds.stream()
                .map(sendMastId -> this.make(sendMastId))
                .collect(Collectors.toList());

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

    }

    /**
     * 제작 서비스
     * @param sendMastId
     * @return
     */
    @Transactional(propagation = Propagation.SUPPORTS)
    public EnsResponseVO make(Long sendMastId){

        EnsResponseVO responseVO = kkoAlimtalkMaker.statBegin(sendMastId);
        if(!EnsErrCd.OK.equals(responseVO.getErrCode()))
            return responseVO;

        Optional<SendMast> sendMast = sendMastRepository.findById(sendMastId);
        try {
//
//            responseVO = this.makeBillLink(sendMastId);
//            if(!EnsErrCd.OK.equals(responseVO.getErrCode()))
//                throw new EnsException(responseVO.getErrCode(), responseVO.getErrMsg());

            responseVO = this.makeTmpltMsg(sendMastId);
            if(!EnsErrCd.OK.equals(responseVO.getErrCode()))
                throw new EnsException(responseVO.getErrCode(), responseVO.getErrMsg());
        } catch (EnsException e){
            return EnsResponseVO.errBuilder()
                    .errCode(e.getErrCd())
                    .errMsg(e.getMessage())
                    .build();
        }


        return responseVO;
    }

//    /**
//     * 제작(청구서링크) 서비스
//     * @param sendMastId
//     * @return
//     */
//    @Transactional(propagation = Propagation.SUPPORTS)
//    public EnsResponseVO makeBillLink(Long sendMastId){
//        EnsResponseVO responseVO = kkoAlimtalkMakerOfBillLink.execute(sendMastId);
//
//        return responseVO;
//    }

    /**
     * 제작(템플릿메시지) 서비스
     * @param sendMastId
     * @return
     */
    @Transactional(propagation = Propagation.SUPPORTS)
    public EnsResponseVO makeTmpltMsg(Long sendMastId){
        EnsResponseVO responseVO = kkoAlimtalkMaker.execute(sendMastId);

        return responseVO;
    }



    /**
     * (대량)전송 서비스
     * @return
     */
    @Transactional(propagation = Propagation.SUPPORTS)
    public EnsResponseVO sendBulk(Long sendMastId){

        try {
            SendMast sendMast = sendMastRepository.findById(sendMastId)
                    .orElseThrow(() -> new EnsException(EnsErrCd.SEND404, "일치하는 자료가 없습니다."));
            if(!PostSeCd.kkoAlimtalk.equals(sendMast.getPostSe()))
                throw new EnsException(EnsErrCd.SEND404, String.format("%s 자료가 아닙니다.", PostSeCd.kkoAlimtalk.getCodeNm()));
            if(!StatCd.makeok.equals(sendMast.getStatCd()))
                throw new EnsException(EnsErrCd.SEND404, "제작성공(makeok) 단계가 아닙니다.");
        } catch (EnsException e){
            return EnsResponseVO.errBuilder()
                    .errCode(e.getErrCd())
                    .errMsg(e.getMessage())
                    .build();
        }

        List<Long> sendMastIds = Arrays.asList(sendMastId);
        EnsResponseVO responseVO = kkoAlimtalkSender.statReady(sendMastIds);
        if(!EnsErrCd.OK.equals(responseVO.getErrCode()))
            return responseVO;

        return this.send(sendMastIds).getResultInfo().get(0);
    }
    /**
     * (대량)전송 서비스-일괄
     * @return
     */
    @Transactional(propagation = Propagation.SUPPORTS)
    public EnsResponseVO sendBulkAll(){
        List<Long> sendMastIds = sendMastRepository.findAllByPostSeAndStatCdInAndSendDtBefore(PostSeCd.kkoAlimtalk, Arrays.asList(StatCd.makeok), LocalDateTime.now())
                .stream()
                .map(row -> row.getSendMastId())
                .collect(Collectors.toList());
        if(sendMastIds.size()<1)
            return EnsResponseVO.errBuilder()
                    .errCode(EnsErrCd.SEND404)
                    .errMsg("발송시간이 경과한 자료 중 \"제작성공(makeok)\" 상태인 자료가 없습니다")
                    .build();

        EnsResponseVO responseVO = kkoAlimtalkSender.statReady(sendMastIds);
        if(!EnsErrCd.OK.equals(responseVO.getErrCode()))
            return responseVO;

        return this.send(sendMastIds);
    }
    private EnsResponseVO<List<EnsResponseVO>> send(List<Long> sendMastIds){
        List<EnsResponseVO> resultInfo = sendMastIds.stream()
                .map(sendMastId -> {
                    kkoAlimtalkSender.statBegin(sendMastId);
                    return sendMastId;
                })
                .map(sendMastId -> kkoAlimtalkSender.execute(sendMastId))
                .map(responseVO -> {
                    if(!EnsErrCd.OK.equals(responseVO.getErrCode()))
                        return responseVO;

                    return this.statBulk((String) responseVO.getResultInfo().get("btToken"), (Long) responseVO.getResultInfo().get("sendMastId"));

                })
                .collect(Collectors.toList());


        return EnsResponseVO.<List<EnsResponseVO>>okBuilder().resultInfo(resultInfo).build();
    }






    /**
     * (대량)문서상태조회 서비스
     * @return
     */
    @Transactional(propagation = Propagation.SUPPORTS)
    public EnsResponseVO statBulk(String btToken, Long sendMastId){
        try {
            Map<String, Object> mArg = new HashMap<>();
            mArg.put("btToken", btToken);
            mArg.put("sendMastId", sendMastId);
            return kkoAlimtalkRsltFetcher.execute(mArg);
        } catch (Exception e){
            return EnsResponseVO.errBuilder()
                    .errCode(EnsErrCd.RSLT500)
                    .errMsg("전송에 사용한 토큰정보(bt-token) 값이 없습니다. " + e.getMessage())
                    .build();
        }
    }




    /**
     * 전송결과 제공
     * @param sendMastId
     * @return
     */
    @Transactional(propagation = Propagation.SUPPORTS)
    public EnsResponseVO sendResultProvide(Long sendMastId){

        return kkoAlimtalkRsltProvider.execute(sendMastId);
    }

}
