package kr.xit.biz.ens.service;

import java.io.*;
import java.util.*;

import org.apache.commons.lang3.*;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.*;
import org.springframework.transaction.annotation.*;

import kr.xit.batch.ens.task.cmm.*;
import kr.xit.biz.common.*;
import kr.xit.biz.common.ApiConstants.*;
import kr.xit.biz.ens.cmm.*;
import kr.xit.biz.ens.mapper.*;
import kr.xit.biz.ens.model.*;
import kr.xit.biz.ens.model.cmm.*;
import kr.xit.biz.ens.model.cntc.*;
import kr.xit.biz.ens.model.kakao.pay.*;
import kr.xit.biz.ens.model.kakao.talk.*;
import kr.xit.biz.ens.model.kt.KtMmsSendDTO.*;
import kr.xit.biz.ens.model.ktgbs.*;
import kr.xit.core.exception.*;
import kr.xit.core.service.*;
import kr.xit.core.support.utils.*;
import lombok.*;

/**
 * <pre>
 * description : 모바일 전자고지 배치 서비스 class
 * - {@code @Transactional(propagation = Propagation.REQUIRES_NEW)} 적용 서비스 분리
 * - 동일 class 서비스 메소드 호출시 Propagation.REQUIRES_NEW 미 동작
 * packageName : kr.xit.biz.ens.service
 * fileName    : EnsBatchRequireNewService
 * author      : limju
 * date        : 2023-08-31
 * ======================================================================
 * 변경일         변경자        변경 내용
 * ----------------------------------------------------------------------
 * 2023-08-31    limju       최초 생성
 *
 * </pre>
 */
@RequiredArgsConstructor
@Service
public class EnsBatchExtractService extends AbstractService implements
    IEnsBatchExtractService {
    @Value("${app.file.cmm.upload.root}")
    private String fileRoot;
    @Value("${app.file.cmm.upload.post}")
    private String filePost;

    private final IEnsBatchMapper mapper;

    //-----------------------------------------------------------------------------------------------------------------
    // REQUIRES_NEW service method
    //-----------------------------------------------------------------------------------------------------------------
    /**
     * <pre>
     * 카카오문서 요청 결과 반영
     * 1. 카카오페이 문서요청 결과 반영 : tb_ens_kakao_my_doc
     * 2. 모바일 페이지 생성 : tb_ens_mobile_page_manage
     * 3. 연계발송결과 생성 : tb_cntc_sndng_result
     *    -> 성공시 'SENT' 로 코드값 생성
     * @param mstId         발송마스터 ID
     * @param resList       List<KkopayDocBulkDTO.BulkSendResponses> 카카오내문서함 발송요청 결과 목록
     * @param unitySndMstId String 통합발송 마스터 ID
     * </pre>
     */
    @Override
    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public void saveKkopaySendResult(final String mstId, String unitySndMstId, final List<KkopayDocBulkDTO.BulkSendResponses> resList) {
        // 결과 반영
        resList.forEach(o ->
            o.getDocuments().forEach(
                t -> {
                    // 카카오페이 문서요청 결과 반영
                    mapper.updateKakaopaySendBulksResult(t);
                    String code = null;
                    // 모바일 페이지 컨텐트 생성
                    if (Checks.isNotEmpty(t.getDocument_binder_uuid())) {
                        mapper.insertKkopayMobilePageManage(t.getExternal_document_uuid());
                        code = ApiConstants.KkopayDocStatus.SENT.getCode();
                    }else{
                        code = t.getError_code();
                    }
                    // FIXME:연계발송결과 생성 - skip 확인 필요 :: 테스트 완료후 코멘트 제거
                    insertCntcSndngResult(SndngSeCode.KAKAO.getCode(),
                        t.getExternal_document_uuid(), code, t.getError_message());
                })
        );

        // 마스터 상태 변경
        updateSendSndngMstStatus(mstId, unitySndMstId, SndngSeCode.KAKAO, "카카오 문서 발송요청 실패(발송마스터 데이타 오류)");
    }

    /**
     * <pre>
     * 카카오문서 요청 결과 반영
     * 1. 카카오페이 문서요청 결과 반영 : tb_ens_kakao_my_doc
     * 2. 모바일 페이지 생성 : tb_ens_mobile_page_manage
     * 3. 연계발송결과 생성 : tb_cntc_sndng_result
     *    -> 성공시 'SENT' 로 코드값 생성
     * @param mstId         발송마스터 ID
     * @param resList       List<KkopayDocBulkDTO.BulkSendResponses> 카카오내문서함 발송요청 결과 목록
     * @param unitySndMstId String 통합발송 마스터 ID
     * </pre>
     */
    @Override
    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public void saveKkotalkSendResult(final String mstId, String unitySndMstId, final List<KkotalkDTO.BulkSendResponse> resList) {
        // 결과 반영
        resList.forEach(o ->
            o.getEnvelopes().forEach(
                t -> {
                    // 카카오톡 전자고지 요청 결과 반영
                    mapper.updateKakaotalkSendBulksResult(t);
                    String code = null;
                    // 모바일 페이지 컨텐트 생성
                    if (Checks.isNotEmpty(t.getEnvelopeId())) {
                        mapper.insertKkotalkMobilePageManage(t.getExternalId());
                    }else{
                        code = t.getErrorCode();
                    }
                    // 연계발송결과 생성
                    insertCntcSndngResult(SndngSeCode.KAKAO_NEW.getCode(),
                        t.getExternalId(), code, t.getErrorMessage());
                })
        );

        // 마스터 상태 변경
        updateSendSndngMstStatus(mstId, unitySndMstId, SndngSeCode.KAKAO_NEW, "카카오톡 전자고지 발송요청 실패(발송마스터 데이타 오류)");
    }

    @Override
    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public void saveKtBcResult(final SndngMssageParam dto, final List<KtMainSendReqData> sendReqs) {
        // 성공처리
        // mobile page create
        if(Checks.isNotEmpty(sendReqs))     sendReqs.forEach(d -> mapper.insertKtMobilePageManage(d.getSrcKey()));
        mapper.updateKtBcMst(dto);
    }

    @Override
    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public void saveKtGbsResult(final SndngMssageParam dto, final List<KtGbsDTO.MsgSendReqsData> sendReqs) {
        // 성공처리
        // mobile page create

        if(Checks.isNotEmpty(sendReqs))     sendReqs.forEach(d -> mapper.insertKtGbsMobilePageManage(d.getSrcKey()));
        mapper.updateKtGbs(dto);
    }

    /**
     * <pre>
     * 발송 관련 마스터 테이블 fail 처리
     *
     * @param mstId String
     * @param stsErrMsg String
     * </pre>
     */
    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public void updateSndngMstFailStatus(final String mstId, final SndngSeCode seCode, final String errCode, final String errMsg, final String stsErrMsg) {

        // FIXME :: stsErrMsg 메세지 확인 필
        SndngMssageParam dto = mapper.selectSndProcessStatus(mstId)
            .orElseThrow(() -> BizRuntimeException.create(stsErrMsg));

        String newSndngProcessSttus;
        if (dto.getTrySeq() == 2) {
            newSndngProcessSttus = "send-fail2";
        } else if (dto.getTrySeq() == 3) {
            newSndngProcessSttus = "send-fail3";
        } else {
            newSndngProcessSttus = "send-fail1";
        }

        SndngMssageParam paramDTO = SndngMssageParam.builder()
            .unitySndngMastrId(dto.getUnitySndngMastrId())
            .sndngMastrId(dto.getSndngMastrId())
            .errorCode(errCode)
            .errorMssage(errMsg)
            .newSndngProcessSttus(newSndngProcessSttus)
            .build();

        updateMstTable(paramDTO, seCode);
    }

    //-----------------------------------------------------------------------------------------------------------------
    // REQUIRES_NEW service method
    //-----------------------------------------------------------------------------------------------------------------

    /**
     * <pre>
     * 발송후 연계발송마스터 / 통합발송마스터 / 발송마스터 상태 변경
     * - send-ok|sending1|sending2
     *
     * @param mstId     발송마스터 ID
     * @param unitySndMstId String 통합발송 마스터 ID
     * @param seCode SndngSeCode
     * @param stsErrMsg 발송 연계 마스터의 발송상태 변경값 조회가 불가한 경우 에러 메세지
     * </pre>
     */
    public void updateSendSndngMstStatus(final String mstId, final String unitySndMstId, final SndngSeCode seCode, final String stsErrMsg) {
        SndngMssageParam dto = mapper.selectSndProcessStatus(mstId)
            .orElseThrow(() -> BizRuntimeException.create(stsErrMsg));
        SndngMssageParam paramDTO = SndngMssageParam.builder()
            .sndngMastrId(mstId)
            .unitySndngMastrId(unitySndMstId)
            .newSndngProcessSttus(dto.getNewSndngProcessSttus())
            .build();

        updateMstTable(paramDTO, seCode);
    }

    /**
     * 에러 발생시 마스터 테이블 처리
     * -> 배치 실행 오류 처리
     * @param dto EnsDTO.SndngMssageParam
     * @see TaskCmmUtils#taskEnsBatchServiceUpdateErrorLog
     */
    @Transactional
    public void updateErrorLog(final SndngMssageParam dto) {
        final String status = dto.getNewSndngProcessSttus();

        if ("SndngAcceptJob".equals(dto.getErrorCode())) {
            CmmEnsBizUtils.setMappingProcessStatus(dto, status);
            mapper.updateProcessSttusCntcSndngMst(dto);

        } else if ("SndngMakeJob".equals(dto.getErrorCode())) {
            CmmEnsBizUtils.setMappingProcessStatus(dto, status);
            mapper.updateProcessSttusCntcSndngMst(dto);
            dto.setNewSndngProcessSttus(status);
            mapper.updateProcessSttusUnitySndngMst(dto);

        } else if ("SndngSendBulksJob".equals(dto.getErrorCode())) {
            CmmEnsBizUtils.setMappingProcessStatus(dto, status);
            mapper.updateProcessSttusCntcSndngMst(dto);
            dto.setNewSndngProcessSttus(status);
            mapper.updateProcessSttusUnitySndngMst(dto);
            mapper.updateProcessSttusSndngMst(dto);
        }
    }

    //-----------------------------------------------------------------------------------------------------------------
    // private method
    //-----------------------------------------------------------------------------------------------------------------
    /**
     * <pre>
     * 문서발송요청 연계결과(tb_cntc_sndng_result) 생성
     *
     * @param sndngSeCode        발송구분코드 - KKO-MY-DOC|E-GREEN|SMS|KT-BC|PPLUS
     * @param unitySndngDetailId 통합 발송 상세 ID
     * @param code               에러코드(API호출 결과 코드) : 카카오인 경우 성공시 'SENT'
     * @param errMessage         에러메세지(API호출 결과 에러 메세지)
     * </pre>
     */
    private void insertCntcSndngResult(final String sndngSeCode,
        final String unitySndngDetailId, final String code, final String errMessage) {
        mapper.insertCntcSndngResult(CntcDTO.SndngResult.builder()
            .unitySndngDetailId(unitySndngDetailId)
            .sndngSeCode(sndngSeCode)
            .sndngResultSttus(code)
            .errorCn(errMessage)
            .build());
    }

    private void updateMstTable(SndngMssageParam paramDTO, SndngSeCode seCode) {
        final String status = paramDTO.getNewSndngProcessSttus();

        CmmEnsBizUtils.setMappingProcessStatus(paramDTO, status);
        if(mapper.updateProcessSttusCntcSndngMst(paramDTO) != 1) {
            throw BizRuntimeException.create(String.format("[send-%s]연계 발송 마스터 상태변경 실패", seCode.getCode()));
        }

        paramDTO.setNewSndngProcessSttus(status);
        if(mapper.updateProcessSttusUnitySndngMst(paramDTO) != 1) {
            throw BizRuntimeException.create(String.format("[send-%s]통합 발송 마스터 상태변경 실패", seCode.getCode()));
        }
        if(mapper.updateProcessSttusSndngMst(paramDTO) != 1) {
            throw BizRuntimeException.create(String.format("[send-%s]발송 마스터 상태변경 실패", seCode.getCode()));
        }
    }

    /**
     * E-GREEN 발송 처리 - 파일 생성
     *
     * @param filePath String
     * @param fileName String
     * @param list     List<EnsDTO.PostSndng>
     * @return boolean
     */
    private boolean egreenFileWrite(final String filePath, final String fileName,
        final List<EnsDTO.PostSndng> list) {
        try {
            File folder = new File(filePath);
            if (!folder.exists()) //noinspection ResultOfMethodCallIgnored
            {
                folder.mkdirs();
            }

            File file = new File(filePath + fileName);
            if (!file.exists()) {
                if (!file.createNewFile()) {
                    return false;
                }
            }

            try (BufferedWriter writer = new BufferedWriter(new FileWriter(file, true))) {

                for (EnsDTO.PostSndng postSndng : list) {
                    writer.write(StringUtils.defaultString(postSndng.getConKey()) + "|");
                    writer.write(StringUtils.defaultString(postSndng.getSenderNm()) + "|");
                    writer.write(StringUtils.defaultString(postSndng.getSenderZipNo()) + "|");
                    writer.write(StringUtils.defaultString(postSndng.getSenderAddr()) + "|");
                    writer.write(StringUtils.defaultString(postSndng.getSenderDetailAddr()) + "|");
                    writer.write(StringUtils.defaultString(postSndng.getReceiverSendNo()) + "|");
                    writer.write(StringUtils.defaultString(postSndng.getReceiverNm()) + "|");
                    writer.write(StringUtils.defaultString(postSndng.getReceiverZipNo()) + "|");
                    writer.write(StringUtils.defaultString(postSndng.getReceiverAddr()) + "|");
                    writer.write(
                        StringUtils.defaultString(postSndng.getReceiverDetailAddr()) + "|");
                    writer.write(StringUtils.defaultString(postSndng.getSschnge1()) + "|");
                    writer.write(StringUtils.defaultString(postSndng.getSschnge2()) + "|");
                    writer.write(StringUtils.defaultString(postSndng.getSschnge3()));
                    writer.newLine();
                }
                writer.flush();
            } catch (IOException e) {
                throw BizRuntimeException.create(e.getMessage());
            }
        } catch (IOException ie) {
            throw BizRuntimeException.create(ie.getMessage());
        }
        return true;
    }

}
