package kr.xit.biz.ens.service;

import static kr.xit.core.support.utils.JsonUtils.*;

import java.util.ArrayList;
import java.util.List;

import org.apache.commons.collections4.ListUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.MDC;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpMethod;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import kr.xit.biz.common.ApiConstants;
import kr.xit.biz.common.ApiConstants.SndngSeCode;
import kr.xit.biz.ens.cmm.CmmEnsBizUtils;
import kr.xit.biz.ens.mapper.IEnsBatchMapper;
import kr.xit.biz.ens.model.EnsDTO;
import kr.xit.biz.ens.model.cmm.SndngMssageParam;
import kr.xit.biz.ens.model.cntc.CntcDTO;
import kr.xit.biz.ens.model.kakao.pay.KkopayDocAttrDTO;
import kr.xit.biz.ens.model.kakao.pay.KkopayDocBulkDTO;
import kr.xit.biz.ens.model.kakao.talk.KkotalkDTO;
import kr.xit.core.exception.BizRuntimeException;
import kr.xit.core.model.ApiResponseDTO;
import kr.xit.core.service.AbstractService;
import kr.xit.core.spring.util.ApiWebClientUtil;
import kr.xit.core.support.utils.Checks;
import lombok.RequiredArgsConstructor;

/**
 * <pre>
 * description : 모바일 전자고지 배치 서비스
 *               - 배치에서 호출되는 클래스로 배치 로그등 Transaction 분리 필요
 *               - 메소드 트랜잭션 Transactional(propagation = Propagation.REQUIRES_NEW)로 선언
 * packageName : kr.xit.biz.ens.service
 * fileName    : EnsBatchService
 * author      : limju
 * date        : 2023-08-31
 * ======================================================================
 * 변경일         변경자        변경 내용
 * ----------------------------------------------------------------------
 * 2023-08-31    limju       최초 생성
 *
 * </pre>
 */
@RequiredArgsConstructor
@Service
public class EnsBatchStatusService extends AbstractService implements IEnsBatchStatusService {
    @Value("${app.contract.host}")
    private String apiHost;

    @Value("${app.contract.kakao.api.pay.bulkstatus}")
    private String apiKkopayBulkStatus;

    @Value("${app.contract.kakao.api.talk.bulkstatus}")
    private String apiKkotalkBulkStatus;

    @Value("${app.contract.kakao.bulk-max-cnt}")
    private int bulkKkoMaxCnt;

    private static final String UNITY_SNDNG_MST_ID = "unitySndngMastrId";

    private final ApiWebClientUtil apiWebClient;
    private final IEnsBatchMapper mapper;
    private final EnsBatchExtractService extractService;

    /**
     * <pre>
     * 문서중개자 전송후 상태 조회
     *
     * @param reqDTO BatchEnsRequest
     * </pre>
     */
    @Override
    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public void statusBulks(final EnsDTO.BatchEnsRequest reqDTO) {
        final List<SndngMssageParam> tgtList = mapper.selectStatusBulkTgts(reqDTO);

        for (SndngMssageParam dto : tgtList) {
            dto.setSignguCode(reqDTO.getSignguCode());
            dto.setFfnlgCode(reqDTO.getFfnlgCode());
            dto.setProfile(ApiConstants.IS_PROFILE_LOCAL? "local" : "prod");

            MDC.put(UNITY_SNDNG_MST_ID, dto.getUnitySndngMastrId());
            MDC.put("sndngMastrId", dto.getSndngMastrId());
            //MDC.put(SNDNG_PROCESS_STTUS, "send-fail" + dto.getTrySeq());

            final SndngSeCode sndngSeCode = CmmEnsBizUtils.getSndngSeCodFromTrySeq(dto);

            // 마스터 상태 변경값을 파라메터에서 받은 상태값으로 set
            //dto.setNewSndngProcessSttus(reqDTO.getSndngProcessSttus());

            // 업무 문서 구분에 따른 분기
            switch (sndngSeCode) {
                /*
                 * 카카오페이 연계 결과 반영 : tb_ens_kakao_my_doc
                 * 모바일 페이지 생성 : tb_ens_mobile_page_manage
                 * 연계발송결과 생성 : tb_cntc_sndng_result
                 */
                case KAKAO -> statusKakaopay(dto);

                case KAKAO_NEW
                    -> statusKakaotalk(dto);
                    //break;
                case KT_BC, KT_GIBIS -> {}

                default ->
                    throw BizRuntimeException.create(String.format("정의 되지 않은 문서 중개자[%s] 입니다", sndngSeCode.getCode()));
            }
        }

    }

    //-----------------------------------------------------------------------------------------------------------------
    // status
    //-----------------------------------------------------------------------------------------------------------------

    /**
     * <pre>
     * 문서중개자 전송후 상태 조회
     *
     * @param dto BatchEnsRequest
     * </pre>
     */
    private void statusKakaopay(final SndngMssageParam dto) {
        final String url = apiHost + apiKkopayBulkStatus;

        final List<String> docsBinderUuids = mapper.selectKakaopayStatusTgts(dto);
        final List<List<String>> partitions = ListUtils.partition(docsBinderUuids, bulkKkoMaxCnt);

        @SuppressWarnings("rawtypes")
        final List<ApiResponseDTO> apiResults = partitions.stream()
            .map(uuids -> apiWebClient.exchange(
                url,
                HttpMethod.POST,
                KkopayDocBulkDTO.BulkStatusRequests.builder()
                    .signguCode(dto.getSignguCode())
                    .ffnlgCode(dto.getFfnlgCode())
                    .document_binder_uuids(uuids)
                    .build(),
                ApiResponseDTO.class,
                CmmEnsBizUtils.getHeadeMap())
            ).toList();

        final List<KkopayDocBulkDTO.BulkStatusResponses> resList = new ArrayList<>();
        //noinspection rawtypes
        for(ApiResponseDTO apiResult : apiResults) {
            if(apiResult.getData() != null) {
                resList.add(toObjByObj(apiResult.getData(), KkopayDocBulkDTO.BulkStatusResponses.class));
            }
        }

        // 결과 반영
        resList.forEach(o ->
            o.getDocuments().forEach(t -> {
                mapper.updateKakaopayStatusInfo(t);
                KkopayDocAttrDTO.DocStatus statusData = t.getStatus_data();
                if(Checks.isNotEmpty(statusData)) {
                    mapper.updateCntcSndngResultByKkopay(
                        CntcDTO.SndngResult.builder()
                            .docKeyId(t.getDocument_binder_uuid())
                            .sndngResultSttus(StringUtils.defaultString(
                                String.valueOf(statusData.getDoc_box_status()), "FAIL"))
                            .requstDt(Checks.isEmpty(statusData.getDoc_box_sent_at()) ? null
                                : statusData.getDoc_box_sent_at().toString())
                            .inqireDt(Checks.isEmpty(statusData.getDoc_box_received_at()) ? null
                                : statusData.getDoc_box_received_at().toString())
                            .readngDt(Checks.isEmpty(statusData.getDoc_box_read_at()) ? null
                                : statusData.getDoc_box_read_at().toString())
                            .errorCn(t.getError_message())
                            .build());
                }else{
                    mapper.updateCntcSndngResultByKkopay(
                        CntcDTO.SndngResult.builder()
                            .docKeyId(t.getDocument_binder_uuid())
                            .sndngResultSttus(t.getError_code())
                            .errorCn(t.getError_message())
                            .build());
                }
            })
        );
    }

    /**
     * <pre>
     * 문서중개자 전송후 상태 조회
     *
     * @param dto BatchEnsRequest
     * </pre>
     */
    private void statusKakaotalk(final SndngMssageParam dto) {
        final String url = apiHost + apiKkotalkBulkStatus;

        final List<String> envelopeIds = mapper.selectKakaotalkStatusTgts(dto);
        final List<List<String>> partitions = ListUtils.partition(envelopeIds, bulkKkoMaxCnt);

        @SuppressWarnings("rawtypes")
        final List<ApiResponseDTO> apiResults = partitions.stream()
            .map(envelopes -> apiWebClient.exchange(
                url,
                HttpMethod.POST,
                KkotalkDTO.BulkStatusRequest.builder()
                    .signguCode(dto.getSignguCode())
                    .ffnlgCode(dto.getFfnlgCode())
                    .envelopes(envelopes)
                    .build(),
                ApiResponseDTO.class,
                CmmEnsBizUtils.getHeadeMap())
            ).toList();

        final List<KkotalkDTO.BulkStatusResponse> resList = new ArrayList<>();
        //noinspection rawtypes
        for(ApiResponseDTO apiResult : apiResults) {
            if(apiResult.getData() != null) {
                resList.add(toObjByObj(apiResult.getData(), KkotalkDTO.BulkStatusResponse.class));
            }
        }

        // 결과 반영
        resList.forEach(o ->
            o.getEnvelopeStatus().forEach(t -> {
                mapper.updateKakaotalkStatusInfo(t);
                //EnvelopeStatus statusData = t;
                if(Checks.isNotEmpty(t)) {
                    mapper.updateCntcSndngResultByKkotalk(
                        CntcDTO.SndngResult.builder()
                            .docKeyId(t.getEnvelopeId())
                            .sndngResultSttus(StringUtils.defaultString(
                                String.valueOf(t.getStatus()), "FAIL"))
                            .requstDt(t.getSentAt())
                            .inqireDt(t.getReceivedAt())
                            .readngDt(t.getReadAt())
                            .errorCn(t.getErrorMessage())
                            .build());
                }else{
                    mapper.updateCntcSndngResultByKkotalk(
                        CntcDTO.SndngResult.builder()
                            .docKeyId(t.getEnvelopeId())
                            .sndngResultSttus(t.getErrorCode())
                            .errorCn(t.getErrorMessage())
                            .build());
                }
            })
        );
    }
}
