package cokr.xit.ens.biz.iup.signtalk.service.support;

import cokr.xit.ens.biz.iup.code.IupPrcsCd;
import cokr.xit.ens.biz.iup.code.IupSendSttusCd;
import cokr.xit.ens.biz.iup.code.IupSendTrySttusCd;
import cokr.xit.ens.biz.iup.code.IupSendTypeCd;
import cokr.xit.ens.biz.iup.domain.TbInputXit;
import cokr.xit.ens.biz.iup.domain.TbSendResult;
import cokr.xit.ens.biz.iup.domain.repository.TbInputDataXitRepository;
import cokr.xit.ens.biz.iup.domain.repository.TbInputXitRepository;
import cokr.xit.ens.biz.iup.domain.repository.TbSendResultRepository;
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.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.ctgy.intgrnnoti.code.TryStatCd;
import cokr.xit.ens.modules.common.ctgy.intgrnnoti.model.IntgrnDetailStatVO;
import cokr.xit.ens.modules.common.ctgy.intgrnnoti.model.IntgrnNotiRsltRespDTO;
import cokr.xit.ens.modules.common.ctgy.intgrnnoti.service.IntgrnNotiService;
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 java.time.LocalDateTime;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

@Slf4j
@Component
@RequiredArgsConstructor
public class IupSigntalkFetcher implements EnsPhaseProcSupport<EnsResponseVO, Long> {

    private final TbInputXitRepository tbInputXitRepository;
    private final TbInputDataXitRepository tbInputDataXitRepository;

    private final TbSendResultRepository tbSendResultRepository;

    private final IntgrnNotiService intgrnNotiService;


    /**
     * Fetch 준비
     *
     * @param lnkInputIds
     * @return
     */
    @Override
    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public EnsResponseVO statReady(List<Long> lnkInputIds) {

        EnsResponseVO respVO = null;
        try {
            /**
             * 필수값 확인
             */
            if (CmmnUtil.isEmpty(lnkInputIds))
                throw new EnsException(EnsErrCd.RSLT410, "연계입수아이디(은)는 필수조건 입니다.");

            /**
             * 저장
             *  -. 발송마스터의 상태를 "입수중(INCM)" 으로 변경 한다.
             */

            List<Long> resultInfo = tbInputXitRepository.findAllByLnkInputIdIn(lnkInputIds).stream()
                    .map(row -> {
                        row.setUpdId("ENS_SYS");
                        row.setPrcsCd(IupPrcsCd.INCM);
                        return row.getLnkInputId();
                    })
                    .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.RSLT999).errMsg(e.getMessage()).build();
        }

        return respVO;
    }


    @Override
    public EnsResponseVO statBegin(Long arg) {
        log.info("no process");
        return null;
    }


    /**
     * 인증톡 발송(성공/실패) 자료에 대한 결과 가져오기
     *
     * @param lnkInputId
     */
    @Override
    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public EnsResponseVO execute(Long lnkInputId) {
        EnsResponseVO responseVO = null;
        Map<String, Object> resultInfo = new HashMap<>();
        resultInfo.put("lnkInputId", lnkInputId);
        IntgrnNotiRsltRespDTO respDTO = null;
        try {
            Long intSendMastId = tbInputDataXitRepository.findLastSendMastIdByLnkInputIdAndSendType(lnkInputId, IupSendTypeCd.KP)
                    .orElseThrow(() -> new EnsException(EnsErrCd.RSLT404, "일치하는 ENS 접수자료가 없습니다."));
            resultInfo.put("intSendMastId", intSendMastId);


            EnsResponseVO apiRespVO = intgrnNotiService.sendResultProvide(intSendMastId);
            respDTO = (IntgrnNotiRsltRespDTO) apiRespVO.getResultInfo();


            IntgrnNotiRsltRespDTO finalRespDTO = respDTO;
            List<TbSendResult> tbSendResults = respDTO.getDocuments().stream()
                    .map(row -> {
                        return TbSendResult.builder()
                                .lnkInputId(lnkInputId)
                                .dataId(row.getLinkedUuid())     
//                                        .dataId(tbInputDataXitRepository.findByLinkedUuid(row.getLinkedUuid())
//                                                .orElse(new TbInputDataXit())
//                                                .getDataId())     
//                                .prcsOdr(StatCd.close.equals(finalRespDTO.getStatCd()) ? "2" : "1") 
                                .prcsOdr(finalRespDTO.getCloseAt() ? "2" : "1") 
//                                        .sendSttusCd(iupSendSttusCd)               
                                .sendSttusCd(this.sendSttusCd(row, finalRespDTO))              
                                .sendTrySttusCd(TryStatCd.cmplt.equals(finalRespDTO.getTryStatCd()) ? IupSendTrySttusCd.CMPLT : IupSendTrySttusCd.TRYING)
                                .sendType(IupSendTypeCd.KP)                                    
                                .runDt(DateUtil.toLocalDateTime(finalRespDTO.getSendDt()))     
                                .bizSendDt(DateUtil.toLocalDateTime(row.getDocSentDt()))       
                                .bizRecvDt(DateUtil.toLocalDateTime(row.getDocReceivedDt()))   
                                .bizReadDt(DateUtil.toLocalDateTime(row.getDocReadFrstDt()))   
                                .bizErrMsg(CmmnUtil.isEmpty(row.getErrorMessage()) ? null : String.format("%s %s", row.getErrorCode(), row.getErrorMessage())) 
                                .regId("ENS_SYS")            
                                .regDt(LocalDateTime.now())            
                                .expiresDt(DateUtil.toLocalDateTime(finalRespDTO.getCloseDt()))   
                                .prcsYn("N")           
                                .build();
                    })
                    .collect(Collectors.toList());
//            if (tbSendResultRepository.findExistsByLnkInputIdAndPrcsOdr(lnkInputId, StatCd.close.equals(finalRespDTO.getStatCd()) ? "2" : "1"))
            if (tbSendResultRepository.findExistsByLnkInputIdAndPrcsOdr(lnkInputId, finalRespDTO.getCloseAt() ? "2" : "1"))
                tbSendResultRepository.modifyByTbSendResults(tbSendResults);
            else
                tbSendResultRepository.saveAll(tbSendResults);

            responseVO = EnsResponseVO.okBuilder().resultInfo(resultInfo).build();
        } catch (EnsException e) {
            responseVO = EnsResponseVO.errBuilder()
                    .errCode(e.getErrCd())
                    .errMsg(e.getMessage())
                    .build();
        } catch (Exception e) {
            responseVO = EnsResponseVO.errBuilder()
                    .errCode(EnsErrCd.RSLT999)
                    .errMsg(String.format("[lnkInputId %s]에 대한 전송결과 FETCH 처리 실패. %s", lnkInputId, e.getMessage()))
                    .build();
        } finally {
            TbInputXit tbInputXit = tbInputXitRepository.findById(lnkInputId).orElseThrow(() -> new EnsException(EnsErrCd.RSLT404, "일치하는 tbInputXit 자료가 없습니다."));
            if (EnsErrCd.OK.equals(responseVO.getErrCode())) {
                tbInputXit.setUpdId("ENS_SYS");
//                tbInputXit.setPrcsCd(IupPrcsCd.IPCP);
                tbInputXit.setPrcsCd(this.prcsCd(respDTO, tbInputXit.getPrcsCd()));
                tbInputXit.setErrMsg(null);
            } else {
                tbInputXit.setUpdId("ENS_SYS");
                tbInputXit.setPrcsCd(IupPrcsCd.FAIL);
                tbInputXit.setErrMsg(responseVO.getErrMsg());
            }
        }

        return responseVO;
    }


    private IupPrcsCd prcsCd(IntgrnNotiRsltRespDTO respDTO, IupPrcsCd prcsCd) {
        switch (respDTO.getStatCd()) {
            case makefail: 
                return IupPrcsCd.FAIL;   
            case sendfail: 
                if (TryStatCd.cmplt.equals(respDTO.getTryStatCd()))   
                    return IupPrcsCd.FAIL;   
            case makerdy:  
                return IupPrcsCd.CICV;   
            case making:   
                return IupPrcsCd.CICC;   
            case makeok:   
                return IupPrcsCd.COMP;   
            case sendok:   
            case sendcmplt:
            case open: 
                return IupPrcsCd.INCM;   
            case close:
                return IupPrcsCd.IPCP;   
            default:
                return prcsCd;
        }
    }


    private IupSendSttusCd sendSttusCd(IntgrnDetailStatVO row, IntgrnNotiRsltRespDTO respDTO) {

       
        IupSendSttusCd iupSendSttusCd = null;
        try {
            switch (row.getCurStatCd().getIntgrnDtlStatCd()) {
                case UNIDENTIFIED_USER:
                    if (PostSeCd.nvSigntalk.equals(row.getCurPostSe()))
                        iupSendSttusCd = IupSendSttusCd.NV_NOMB;  
                    else
                        iupSendSttusCd = IupSendSttusCd.NOMB;  
                    break;
                case FORBIDDEN:
                    if (PostSeCd.nvSigntalk.equals(row.getCurPostSe()))
                        iupSendSttusCd = IupSendSttusCd.NV_DENY;  
                    else
                        iupSendSttusCd = IupSendSttusCd.DENY;  
                    break;
                case SENT: 
                    if (PostSeCd.nvSigntalk.equals(row.getCurPostSe()))
                        iupSendSttusCd = IupSendSttusCd.NV_SEND;  
                    else
                        iupSendSttusCd = IupSendSttusCd.SEND;  
                    break;
                case RECEIVED: 
                case EXPIRED:  
                    if (PostSeCd.nvSigntalk.equals(row.getCurPostSe()))
                        iupSendSttusCd = IupSendSttusCd.NV_RECV;  
                    else
                        iupSendSttusCd = IupSendSttusCd.RECV;  
                    break;
                case READ: 
                    if (PostSeCd.nvSigntalk.equals(row.getCurPostSe()))
                        iupSendSttusCd = IupSendSttusCd.NV_SIGN;  
                    else
                        iupSendSttusCd = IupSendSttusCd.SIGN;  
                    break;
                case REG:  
                case MAKING:   
                case ACPT_OK:  
                case ACPT_CMPLT:   
                    if (StatCd.makefail.equals(respDTO.getStatCd())
                            || StatCd.sendfail.equals(respDTO.getStatCd()))
                        iupSendSttusCd = IupSendSttusCd.FAIL;
                    else
                        iupSendSttusCd = IupSendSttusCd.READY;
                    break;
                case ACPT_FAIL:
                case INVALID_VALUE:
                case INTERNAL_ERROR:
                case INVALID_REQUEST:
                case FORBIDDEN_USER:
                case FAIL: 
                    iupSendSttusCd = IupSendSttusCd.FAIL;  
                    break;
                default:
                    break;
            }
        } catch (Exception e) {
            log.info("IupSendSttusCd와 일치하는 값 없음. [ dataId {} IntgrnDtlStatCd {} ]", row.getLinkedUuid(), row.getCurStatCd().getIntgrnDtlStatCd());
        }
        return iupSendSttusCd;


    }

}
