package cokr.xit.ens.modules.common.ctgy.intgrnnoti.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.StatCd;
import cokr.xit.ens.modules.common.ctgy.intgrnnoti.code.TryStatCd;
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.IntgrnNotiAcceptReqDTO;
import cokr.xit.ens.modules.common.ctgy.intgrnnoti.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.Arrays;
import java.util.List;
import java.util.stream.Collectors;

@Slf4j
@Service
@RequiredArgsConstructor
public class IntgrnNotiService {


    private final IntgrnSendMastRepository intgrnSendMastRepository;
    private final IntgrnSendDetailRepository intgrnSendDetailRepository;

    private final IntgrnNotiAcceptor intgrnSendAcceptor;
    private final IntgrnNotiMaker intgrnNotiMaker;
   
    private final IntgrnNotiSender intgrnNotiSender;
    private final IntgrnNotiRsltFetcher intgrnNotiRsltFetcher;
    private final IntgrnNotiRsltProvider intgrnNotiRsltProvider;

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

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

        return responseVO;
    }


    /**
     * 제작 서비스
     *
     * @param intSendMastId
     * @return
     */
    @Transactional(propagation = Propagation.SUPPORTS)
    public EnsResponseVO remake(Long intSendMastId) {
        try {
            IntgrnSendMast sendMast = intgrnSendMastRepository.findById(intSendMastId)
                    .orElseThrow(() -> new EnsException(EnsErrCd.MAKE404, "일치하는 자료가 없습니다."));
            if (!(StatCd.accept.equals(sendMast.getStatCd())
                    || StatCd.makefail.equals(sendMast.getStatCd())))
                throw new EnsException(EnsErrCd.MAKE404, "접수/제작실패(accept/makefail) 단계가 아닙니다.");
        } catch (EnsException e) {
            return EnsResponseVO.errBuilder()
                    .errCode(e.getErrCd())
                    .errMsg(e.getMessage())
                    .build();
        }

        intgrnNotiMaker.statReady(Arrays.asList(intSendMastId));


        return this.make(intSendMastId);
    }

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

        intgrnNotiMaker.statReady(sendMastIds);

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

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

    private EnsResponseVO make(Long intSendMastId) {
        EnsResponseVO responseVO = intgrnNotiMaker.statBegin(intSendMastId);
        if (!EnsErrCd.OK.equals(responseVO.getErrCode()))
            return responseVO;

        try {
//           
//            responseVO = this.makeBillLink(intSendMastId);
//            if (!EnsErrCd.OK.equals(responseVO.getErrCode()))
//                throw new EnsException(responseVO.getErrCode(), responseVO.getErrMsg());
           
            responseVO = intgrnNotiMaker.execute(intSendMastId);
            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 = intgrnNotiMakerOfBillLink.execute(sendMastId);
//
//        return responseVO;
//    }


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

        try {
            if (intgrnSendDetailRepository.findAllBySendTargetAndSendMast(IntgrnSendMast.builder().intSendMastId(intSendMastId).build()).size() < 1)
                throw new EnsException(EnsErrCd.SEND404, "일치하는 자료가 없거나 전송 가능한 상태의 자료가 없습니다. (1회차:상태=makeok&try상태=stby&dtl상태=MAKING, 2~3회차:try상태:ok/fail&발송일시=기한경과&dtl상태=실패(ACPT_FAIL/FAIL/...)");
        } catch (EnsException e) {
            return EnsResponseVO.errBuilder()
                    .errCode(e.getErrCd())
                    .errMsg(e.getMessage())
                    .build();
        }

        List<Long> sendMastIds = Arrays.asList(intSendMastId);


        return this.send(sendMastIds);
    }

    /**
     * (대량)전송 서비스-일괄
     */
    @Transactional(propagation = Propagation.SUPPORTS)
    public EnsResponseVO sendBulkAll() {

        List<Long> intSendMastIds = intgrnSendDetailRepository.findIntSendMastIdsBySendTarget();
        if (intSendMastIds.size() < 1)
            return EnsResponseVO.errBuilder()
                    .errCode(EnsErrCd.SEND404)
                    .errMsg("전송 타깃 데이터가 없습니다.")
                    .build();

        return this.send(intSendMastIds);
    }

    private EnsResponseVO send(List<Long> intSendMastIds) {
        List<EnsResponseVO> resultInfo = intSendMastIds.stream()
                .map(intSendMastId -> {
                    intgrnNotiSender.statBegin(intSendMastId);
                    return intSendMastId;
                })
                .map(intSendMastId -> intgrnNotiSender.execute(intSendMastId))
                .collect(Collectors.toList());


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


    /**
     * 전송(성공/실패) 결과 가져오기
     */
    @Transactional(propagation = Propagation.SUPPORTS)
    public EnsResponseVO statBulk(Long intSendMastId) {
        IntgrnSendMast intgrnSendMast = null;
        try {
            intgrnSendMast = intgrnSendMastRepository.findById(intSendMastId)
                    .orElseThrow(() -> new EnsException(EnsErrCd.ERR404, String.format("[ intSendMastId %s ]와 일치하는 자료가 없습니다.", intSendMastId)));
            int validCnt = 0;
            IntgrnSendMast finalIntgrnSendMast = intgrnSendMast;
            if (!Arrays.asList(StatCd.sendok, StatCd.sendcmplt, StatCd.open).stream()
                    .anyMatch(statCd -> statCd.equals(finalIntgrnSendMast.getStatCd())))
                validCnt++;
            if (!Arrays.asList(TryStatCd.sndRsrv, TryStatCd.sndRtme).stream()
                    .anyMatch(statCd -> statCd.equals(finalIntgrnSendMast.getTryStatCd())))
                validCnt++;
            if (validCnt == 2)
                throw new EnsException(EnsErrCd.RSLT404, "\"진행상태\"가 전송성공/전송완료/열람중(sendok/sendcmplt/open) 또는 \"현재 회차상태\"가 예약전송/실시간전송(sndRsrv/sndRtme) 상태가 아닙니다.");
        } catch (EnsException e) {
            return EnsResponseVO.errBuilder()
                    .errCode(e.getErrCd())
                    .errMsg(e.getMessage())
                    .build();
        }

        return this.stat(Arrays.asList(intgrnSendMast));
    }

    /**
     * 전송(성공/실패) 결과 모두 가져오기
     */
    @Transactional(propagation = Propagation.SUPPORTS)
    public EnsResponseVO statBulkAll() {
//        List<IntgrnSendMast> intgrnSendMasts = intgrnSendMastRepository.findAllByStatCdIn(Arrays.asList(StatCd.sendok, StatCd.sendcmplt, StatCd.open));
        List<IntgrnSendMast> intgrnSendMasts = intgrnSendMastRepository.findAllByStatCdInOrTryStatCdIn(Arrays.asList(StatCd.sendok, StatCd.sendcmplt, StatCd.open), Arrays.asList(TryStatCd.sndRsrv, TryStatCd.sndRtme));
        if (intgrnSendMasts.size() < 1)
            return EnsResponseVO.errBuilder()
                    .errCode(EnsErrCd.RSLT404)
//                    .errMsg("\"전송성공/전송완료/열람중(sendok/sendcmplt/open)\" 상태인 자료가 없습니다")
                    .errMsg("\"상태정보가 전송성공/전송완료/열람중(sendok/sendcmplt/open)\" or 현재 회차상태가 예약전송/실시간전송(sndRsrv/sndRtme)\" 상태인 자료가 없습니다")
                    .build();

        return this.stat(intgrnSendMasts);
    }

    private EnsResponseVO stat(List<IntgrnSendMast> intgrnSendMasts) {
        List<EnsResponseVO> resultInfo = intgrnSendMasts.stream()
                .map(data -> intgrnNotiRsltFetcher.execute(data.getIntSendMastId()))
                .collect(Collectors.toList());

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

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

        return intgrnNotiRsltProvider.execute(intSendMastId);
    }

    @Transactional
    public EnsResponseVO<List<Long>> closed() {
        List<IntgrnSendMast> intgrnSendMastList = intgrnSendMastRepository.findAllByStatCdInAndCloseDtBefore(Arrays.asList(StatCd.sendcmplt, StatCd.open), LocalDateTime.now().minusDays(1));
        List<Long> intSendMastIds = intgrnSendMastList.stream()
                .map(row -> {
                    row.setStatCd(StatCd.close);
                    return row.getIntSendMastId();
                })
                .collect(Collectors.toList());

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

}
