package cokr.xit.ens.biz.traffic.cheonan.signtalk.service;

import cokr.xit.ens.biz.traffic.cheonan.code.SndngProcessSttusCd;
import cokr.xit.ens.biz.traffic.cheonan.signtalk.domain.TcaElctrnNticSndng;
import cokr.xit.ens.biz.traffic.cheonan.signtalk.domain.repository.TcaElctrnNticSndngRepository;
import cokr.xit.ens.biz.traffic.cheonan.signtalk.service.support.TrafficCaSigntalkAcceptor;
import cokr.xit.ens.biz.traffic.cheonan.signtalk.service.support.TrafficCaSigntalkFetcher;
import cokr.xit.ens.biz.traffic.cheonan.signtalk.service.support.TrafficCaSigntalkMaker;
import cokr.xit.ens.biz.traffic.cheonan.signtalk.service.support.TrafficCaSigntalkSender;
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.modules.common.ctgy.intgrnnoti.service.event.IntgrnNotiSendReserveEvent;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

@Slf4j
@Service
@RequiredArgsConstructor
@Transactional
public class TrafficCaSigntalkService {

    private final TcaElctrnNticSndngRepository tcaElctrnNticSndngRepository;

    private final TrafficCaSigntalkAcceptor trafficCaSigntalkAcceptor;
    private final TrafficCaSigntalkMaker trafficCaSigntalkMaker;
    private final TrafficCaSigntalkSender trafficCaSigntalkSender;
    private final TrafficCaSigntalkFetcher trafficCaSigntalkFetcher;

    private final ApplicationEventPublisher applicationEventPublisher;


    /**
     * 인증톡 접수(TRAFFIC -> ENS)
     */
    public EnsResponseVO accept(String elctrnNticSndngId) {

        try {
           
            TcaElctrnNticSndng elctrnNticSndng = tcaElctrnNticSndngRepository.findById(elctrnNticSndngId)
                    .orElseThrow(() -> new EnsException(EnsErrCd.ACPT404, "일치하는 자료가 없습니다."));
            if (!SndngProcessSttusCd.accept.equals(elctrnNticSndng.getSndngProcessSttus()))
                throw new EnsException(EnsErrCd.ACPT405, String.format("\"%s(%s)\" 상태가 아닙니다.", SndngProcessSttusCd.accept.getCodeNm(), SndngProcessSttusCd.accept));

            return this.accept(Arrays.asList(elctrnNticSndng)).getResultInfo().get(0);
        } catch (EnsException e) {
            return EnsResponseVO.errBuilder()
                    .errCode(e.getErrCd())
                    .errMsg(e.getMessage())
                    .build();
        }
    }

    /**
     * 인증톡 접수(TRAFFIC -> ENS)
     */
    public EnsResponseVO<List<EnsResponseVO>> acceptAll() {
        try {
           
            List<TcaElctrnNticSndng> tcaElctrnNticSndngs = tcaElctrnNticSndngRepository.findAllBySndngProcessSttus(SndngProcessSttusCd.accept);
            if (CmmnUtil.isEmpty(tcaElctrnNticSndngs))
                throw new EnsException(EnsErrCd.ACPT404, String.format("\"%s(%s)\" 상태의 자료가 없습니다.", SndngProcessSttusCd.accept.getCodeNm(), SndngProcessSttusCd.accept));

            return this.accept(tcaElctrnNticSndngs);
        } catch (EnsException e) {
            return EnsResponseVO.<List<EnsResponseVO>>errBuilder()
                    .errCode(e.getErrCd())
                    .errMsg(e.getMessage())
                    .build();
        }
    }

    private EnsResponseVO<List<EnsResponseVO>> accept(List<TcaElctrnNticSndng> tcaElctrnNticSndngs) {
        EnsResponseVO responseVO = trafficCaSigntalkAcceptor.statReady(tcaElctrnNticSndngs.stream()
                .map(row -> row.getElctrnNticSndngId())
                .collect(Collectors.toList()));
        if (!EnsErrCd.OK.equals(responseVO.getErrCode()))
            return responseVO;

        List<EnsResponseVO> resultInfo = tcaElctrnNticSndngs.stream()
                .map(row -> trafficCaSigntalkAcceptor.execute(row.getElctrnNticSndngId()))
                .collect(Collectors.toList());


        List<Long> sendMastIds = resultInfo.stream()
                .filter(ensResponseVO -> EnsErrCd.OK.equals(ensResponseVO.getErrCode()))
                .map(ensResponseVO -> (Long) ((Map<String, Object>) ensResponseVO.getResultInfo()).get("intSendMastId"))
                .collect(Collectors.toList());
        IntgrnNotiSendReserveEvent event = IntgrnNotiSendReserveEvent.builder()
                .sendMastIds(sendMastIds)
                .callback(() -> this.fetch(tcaElctrnNticSndngs))
                .build();
        applicationEventPublisher.publishEvent(event);

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


    /**
     * 인증톡 제작 재요청
     *
     * @param elctrnNticSndngId
     * @return
     */
    public EnsResponseVO reMake(String elctrnNticSndngId) {
        return trafficCaSigntalkMaker.execute(elctrnNticSndngId);
    }


    /**
     * 인증톡 전송 재요청
     *
     * @param elctrnNticSndngId
     * @return
     */
    public EnsResponseVO reSend(String elctrnNticSndngId) {
        return trafficCaSigntalkSender.execute(elctrnNticSndngId);
    }


    /**
     * 인증톡 전송(성공/실패) 결과 가져오기
     */
    public EnsResponseVO fetch(String elctrnNticSndngId) {
        TcaElctrnNticSndng elctrnNticSndng = null;
        try {
            elctrnNticSndng = tcaElctrnNticSndngRepository.findById(elctrnNticSndngId)
                    .orElseThrow(() -> new EnsException(EnsErrCd.ERR404, String.format("[ elctrnNticSndngId %s ]와 일치하는 자료가 없습니다.", elctrnNticSndngId)));
        } catch (EnsException e) {
            return EnsResponseVO.errBuilder()
                    .errCode(e.getErrCd())
                    .errMsg(e.getMessage())
                    .build();
        }

        return this.fetch(Arrays.asList(elctrnNticSndng));
    }

    /**
     * 인증톡 전송(성공/실패) 결과 모두 가져오기
     */
    public EnsResponseVO fetchAll() {
        List<TcaElctrnNticSndng> tcaElctrnNticSndngs = tcaElctrnNticSndngRepository.findAllBySndngProcessSttusIn(Arrays.asList(SndngProcessSttusCd.acptok, SndngProcessSttusCd.ensmake, SndngProcessSttusCd.ensstart, SndngProcessSttusCd.ensok, SndngProcessSttusCd.ensopen));

        return this.fetch(tcaElctrnNticSndngs);
    }

    private EnsResponseVO fetch(List<TcaElctrnNticSndng> tcaElctrnNticSndngs) {

        return EnsResponseVO.okBuilder()
                .resultInfo(
                        tcaElctrnNticSndngs.stream()
                                .map(row -> trafficCaSigntalkFetcher.execute(row.getElctrnNticSndngId()))
                                .collect(Collectors.toList())
                )
                .build();
    }


}
