package cokr.xit.ens.modules.common.ctgy.intgrnnoti.domain.repository;

import cokr.xit.ens.core.utils.CmmnUtil;
import cokr.xit.ens.modules.common.code.IntgrnDtlStatCd;
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.domain.IntgrnSendDetail;
import cokr.xit.ens.modules.common.ctgy.intgrnnoti.domain.IntgrnSendMast;
import cokr.xit.ens.modules.common.ctgy.intgrnnoti.domain.QIntgrnSendMast;
import cokr.xit.ens.modules.common.ctgy.intgrnnoti.model.IntgrnDetailStatVO;
import cokr.xit.ens.modules.common.domain.SendMast;
import com.querydsl.core.BooleanBuilder;
import com.querydsl.core.types.Projections;
import com.querydsl.jpa.impl.JPAQueryFactory;
import lombok.RequiredArgsConstructor;

import java.time.LocalDateTime;
import java.util.*;

import static cokr.xit.ens.modules.common.ctgy.intgrnnoti.domain.QIntgrnSendDetail.intgrnSendDetail;
import static cokr.xit.ens.modules.common.ctgy.intgrnnoti.domain.QIntgrnSendMast.intgrnSendMast;
import static cokr.xit.ens.modules.common.domain.QSendMast.sendMast;
import static cokr.xit.ens.modules.kkoalimtalk.domain.QSendDetailKkoAlimtalk.sendDetailKkoAlimtalk;
import static cokr.xit.ens.modules.kkomydoc.domain.QSendDetailKkoMydoc.sendDetailKkoMydoc;
import static cokr.xit.ens.modules.ktsigntalk.direct.domain.QSendDetailKtSigntalk.sendDetailKtSigntalk;
import static cokr.xit.ens.modules.ktsigntalk.gibis.domain.QSendDetailKtGibis.sendDetailKtGibis;
import static cokr.xit.ens.modules.nvsigntalk.domain.QSendDetailNvSigntalk.sendDetailNvSigntalk;

@RequiredArgsConstructor
public class IntgrnSendDetailRepositoryImpl implements IntgrnSendDetailRepositoryCustom {

    private final JPAQueryFactory query;

    @Override
    public List<IntgrnSendDetail> findAllBySendTargetAndSendMast(IntgrnSendMast intgrnSendMast) {
        return findAllBySendTargetAndSendMastAndTryStatCdIn(intgrnSendMast, null);
    }

    @Override
    public List<IntgrnSendDetail> findAllBySendTargetAndSendMastAndTryStatCdIn(IntgrnSendMast intgrnSendMast, List<TryStatCd> tryStatCds) {
        return query.selectFrom(intgrnSendDetail)
                .innerJoin(intgrnSendDetail.intgrnSendMast, QIntgrnSendMast.intgrnSendMast)
                .where(isSendTarget(tryStatCds)
                        .and(intgrnSendDetail.intgrnSendMast.eq(intgrnSendMast))
                )
                .fetch();
    }

    @Override
    public void modifyDetailStatCdByIntgrnDetailStatVO(List<IntgrnDetailStatVO> list) {
        list.forEach(data ->

                query.update(intgrnSendDetail)
                        .set(intgrnSendDetail.curStatCd, IntgrnDtlStatCd.ACPT_CMPLT.equals(data.getCurStatCd().getIntgrnDtlStatCd()) ? (CmmnUtil.isEmpty(data.getMastErrorCode()) ? data.getCurStatCd().getIntgrnDtlStatCd() : IntgrnDtlStatCd.FAIL) : data.getCurStatCd().getIntgrnDtlStatCd())
                        .set(intgrnSendDetail.docSentDt, data.getDocSentDt())
                        .set(intgrnSendDetail.docReceivedDt, data.getDocReceivedDt())
                        .set(intgrnSendDetail.docAuthFrstDt, data.getDocAuthFrstDt())
                        .set(intgrnSendDetail.docTokenVrfyFrstDt, data.getDocTokenVrfyFrstDt())
                        .set(intgrnSendDetail.docReadFrstDt, data.getDocReadFrstDt())
                        .set(intgrnSendDetail.docUserNotiedDt, data.getDocUserNotiedDt())
                        .set(intgrnSendDetail.error.errorCode, data.getMastErrorCode() == null && data.getErrorCode() == null ? null : String.format("[M]%s. [D]%s", data.getMastErrorCode(), data.getErrorCode()))
                        .set(intgrnSendDetail.error.errorMessage, data.getMastErrorCode() == null && data.getErrorCode() == null ? null : String.format("[M]%s. [D]%s", data.getMastErrorMessage(), data.getErrorMessage()))
                        .where(intgrnSendDetail.intSendDetailId.eq(data.getIntSendDetailId()))
                        .execute()
        );
    }

    @Override
    public List<IntgrnDetailStatVO> findIntgrnDetailStatVOsByIntSendMastIdAndCurPostSe(Long intSendMastId) {
        List<IntgrnDetailStatVO> list = new ArrayList<>();


        list.addAll(query.select(Projections.fields(IntgrnDetailStatVO.class
                        , intgrnSendDetail.intSendDetailId
                        , intgrnSendDetail.linkedUuid
                        , intgrnSendDetail.curPostSe
                        , sendDetailKkoAlimtalk.kkoResultCd.as("curStatCd")
                        , sendDetailKkoAlimtalk.kkoRequestDt.as("docSentDt")
                        , sendDetailKkoAlimtalk.kkoReceivedDt.as("docReceivedDt")
//                        , isNull().as("docAuthFrstDt")
//                        , isNull().as("docTokenVrfyFrstDt")
//                        , isNull().as("docReadFrstDt")
//                        , isNull().as("docUserNotiedDt")
                        , sendDetailKkoAlimtalk.error.errorCode
                        , sendDetailKkoAlimtalk.error.errorMessage
                        , sendMast.error.errorCode.as("mastErrorCode")
                        , sendMast.error.errorMessage.as("mastErrorMessage")
                ))
                .from(intgrnSendDetail)
                .innerJoin(sendDetailKkoAlimtalk).on(sendDetailKkoAlimtalk.sendDetailId.eq(intgrnSendDetail.sendDetailIdKkoAt))
                .innerJoin(sendDetailKkoAlimtalk.sendMast, sendMast)
                .where(
                        intgrnSendDetail.curPostSe.eq(PostSeCd.kkoAlimtalk)
                                .and(intgrnSendDetail.intgrnSendMast.intSendMastId.eq(intSendMastId))
                )
                .fetch());

        list.addAll(query.select(Projections.fields(IntgrnDetailStatVO.class
                        , intgrnSendDetail.intSendDetailId
                        , intgrnSendDetail.linkedUuid
                        , intgrnSendDetail.curPostSe
                        , sendDetailKkoMydoc.kkoDocStat.as("curStatCd")
                        , sendDetailKkoMydoc.kkoDocSentDt.as("docSentDt")
                        , sendDetailKkoMydoc.kkoDocReceivedDt.as("docReceivedDt")
                        , sendDetailKkoMydoc.kkoDocAuthFrstDt.as("docAuthFrstDt")
                        , sendDetailKkoMydoc.kkoDocTokenVrfyFrstDt.as("docTokenVrfyFrstDt")
                        , sendDetailKkoMydoc.kkoDocReadFrstDt.as("docReadFrstDt")
                        , sendDetailKkoMydoc.kkoDocUserNotiedDt.as("docUserNotiedDt")
                        , sendDetailKkoMydoc.error.errorCode
                        , sendDetailKkoMydoc.error.errorMessage
                        , sendMast.error.errorCode.as("mastErrorCode")
                        , sendMast.error.errorMessage.as("mastErrorMessage")
                ))
                .from(intgrnSendDetail)
                .innerJoin(sendDetailKkoMydoc).on(sendDetailKkoMydoc.sendDetailId.eq(intgrnSendDetail.sendDetailIdKkoMd))
                .innerJoin(sendDetailKkoMydoc.sendMast, sendMast)
                .where(
                        intgrnSendDetail.curPostSe.eq(PostSeCd.kkoMydoc)
                                .and(intgrnSendDetail.intgrnSendMast.intSendMastId.eq(intSendMastId))
                )
                .fetch());

        list.addAll(query.select(Projections.fields(IntgrnDetailStatVO.class
                        , intgrnSendDetail.intSendDetailId
                        , intgrnSendDetail.linkedUuid
                        , intgrnSendDetail.curPostSe
                        , sendDetailNvSigntalk.nvDocStat.as("curStatCd")
                        , sendDetailNvSigntalk.nvDocSentDt.as("docSentDt")
                        , sendDetailNvSigntalk.nvDocReceivedDt.as("docReceivedDt")
                        , sendDetailNvSigntalk.nvDocAuthFrstDt.as("docAuthFrstDt")
                        , sendDetailNvSigntalk.nvDocTokenVrfyFrstDt.as("docTokenVrfyFrstDt")
                        , sendDetailNvSigntalk.nvDocReadFrstDt.as("docReadFrstDt")
//                        , sendDetailNvSigntalk.nvdocnoti.as("docUserNotiedDt")
                        , sendDetailNvSigntalk.error.errorCode
                        , sendDetailNvSigntalk.error.errorMessage
                        , sendMast.error.errorCode.as("mastErrorCode")
                        , sendMast.error.errorMessage.as("mastErrorMessage")
                ))
                .from(intgrnSendDetail)
                .innerJoin(sendDetailNvSigntalk).on(sendDetailNvSigntalk.sendDetailId.eq(intgrnSendDetail.sendDetailIdNvSt))
                .innerJoin(sendDetailNvSigntalk.sendMast, sendMast)
                .where(
                        intgrnSendDetail.curPostSe.eq(PostSeCd.nvSigntalk)
                                .and(intgrnSendDetail.intgrnSendMast.intSendMastId.eq(intSendMastId))
                )
                .fetch());

        list.addAll(query.select(Projections.fields(IntgrnDetailStatVO.class
                        , intgrnSendDetail.intSendDetailId
                        , intgrnSendDetail.linkedUuid
                        , intgrnSendDetail.curPostSe
                        , sendDetailKtSigntalk.docStat.as("curStatCd")
                        , sendDetailKtSigntalk.docSentDt.as("docSentDt")
                        , sendDetailKtSigntalk.docReceivedDt.as("docReceivedDt")
                        , sendDetailKtSigntalk.docAuthFrstDt.as("docAuthFrstDt")
                        , sendDetailKtSigntalk.docTokenVrfyFrstDt.as("docTokenVrfyFrstDt")
                        , sendDetailKtSigntalk.docReadFrstDt.as("docReadFrstDt")
//                        , sendDetailKtSigntalk.nvdocnoti.as("docUserNotiedDt")
                        , sendDetailKtSigntalk.error.errorCode
                        , sendDetailKtSigntalk.error.errorMessage
                        , sendMast.error.errorCode.as("mastErrorCode")
                        , sendMast.error.errorMessage.as("mastErrorMessage")
                ))
                .from(intgrnSendDetail)
                .innerJoin(sendDetailKtSigntalk).on(sendDetailKtSigntalk.sendDetailId.eq(intgrnSendDetail.sendDetailIdKtSt))
                .innerJoin(sendDetailKtSigntalk.sendMast, sendMast)
                .where(
                        intgrnSendDetail.curPostSe.eq(PostSeCd.ktSigntalk)
                                .and(intgrnSendDetail.intgrnSendMast.intSendMastId.eq(intSendMastId))
                )
                .fetch());

        list.addAll(query.select(Projections.fields(IntgrnDetailStatVO.class
                        , intgrnSendDetail.intSendDetailId
                        , intgrnSendDetail.linkedUuid
                        , intgrnSendDetail.curPostSe
                        , sendDetailKtGibis.docStat.as("curStatCd")
                        , sendDetailKtGibis.docSentDt.as("docSentDt")
                        , sendDetailKtGibis.docReceivedDt.as("docReceivedDt")
                        , sendDetailKtGibis.docAuthFrstDt.as("docAuthFrstDt")
                        , sendDetailKtGibis.docTokenVrfyFrstDt.as("docTokenVrfyFrstDt")
                        , sendDetailKtGibis.docReadFrstDt.as("docReadFrstDt")
//                        , sendDetailKtGibis.nvdocnoti.as("docUserNotiedDt")
                        , sendDetailKtGibis.error.errorCode
                        , sendDetailKtGibis.error.errorMessage
                        , sendMast.error.errorCode.as("mastErrorCode")
                        , sendMast.error.errorMessage.as("mastErrorMessage")
                ))
                .from(intgrnSendDetail)
                .innerJoin(sendDetailKtGibis).on(sendDetailKtGibis.sendDetailId.eq(intgrnSendDetail.sendDetailIdKtGbs))
                .innerJoin(sendDetailKtGibis.sendMast, sendMast)
                .where(
                        intgrnSendDetail.curPostSe.eq(PostSeCd.ktGibis)
                                .and(intgrnSendDetail.intgrnSendMast.intSendMastId.eq(intSendMastId))
                )
                .fetch());

        return list;
    }

    @Override
    public List<IntgrnDetailStatVO> findIntgrnDetailStatVOsByIntSendMastId(Long intSendMastId) {
        return query.select(Projections.fields(IntgrnDetailStatVO.class
                        , intgrnSendDetail.intSendDetailId
                        , intgrnSendDetail.linkedUuid
                        , intgrnSendDetail.curPostSe
                        , intgrnSendDetail.curStatCd
                        , intgrnSendDetail.docSentDt
                        , intgrnSendDetail.docReceivedDt
                        , intgrnSendDetail.docAuthFrstDt
                        , intgrnSendDetail.docTokenVrfyFrstDt
                        , intgrnSendDetail.docReadFrstDt
                        , intgrnSendDetail.docUserNotiedDt
                        , intgrnSendDetail.error.errorCode
                        , intgrnSendDetail.error.errorMessage
                ))
                .from(intgrnSendDetail)
                .where(intgrnSendDetail.intgrnSendMast.intSendMastId.eq(intSendMastId))
                .fetch();
    }


    @Override
    public List<Long> findIntSendMastIdsBySendTarget() {

        return query.select(intgrnSendMast.intSendMastId)
                .from(intgrnSendDetail)
                .innerJoin(intgrnSendDetail.intgrnSendMast, intgrnSendMast)
                .where(isSendTarget(null))
                .groupBy(intgrnSendMast.intSendMastId)
                .fetch();
    }

    @Override
    public Optional<IntgrnSendDetail> findByDocUuidAndLinkedUuid(String docUuid, String linkedUuid) {
        List<Long> intSendDetailIds = new ArrayList<>();
        intSendDetailIds.addAll(findIntSendDetailIdsByKkoMydoc(docUuid, linkedUuid));
        intSendDetailIds.addAll(findIntSendDetailIdsByNvSigntalk(docUuid, linkedUuid));
        intSendDetailIds.addAll(findIntSendDetailIdsByKtSigntalk(docUuid, linkedUuid));
        intSendDetailIds.addAll(findIntSendDetailIdsByKtGibis(docUuid, linkedUuid));
        return Optional.ofNullable(query.selectFrom(intgrnSendDetail)
                .where(intgrnSendDetail.intSendDetailId.in(intSendDetailIds))
                .fetchOne());
    }

    @Override
    public PostSeCd findPostSeByDocUuidAndLinkedUuid(String docUuid, String linkedUuid) {
        if (findIntSendDetailIdsByKkoMydoc(docUuid, linkedUuid).size() > 0) return PostSeCd.kkoMydoc;
        if (findIntSendDetailIdsByNvSigntalk(docUuid, linkedUuid).size() > 0) return PostSeCd.nvSigntalk;
        if (findIntSendDetailIdsByKtSigntalk(docUuid, linkedUuid).size() > 0) return PostSeCd.ktSigntalk;
        if (findIntSendDetailIdsByKtGibis(docUuid, linkedUuid).size() > 0) return PostSeCd.ktGibis;

        return null;
    }

    @Override
    public Optional<Long> findIntSendMastIdBySendMastId(Long sendMastId) {
        SendMast sendMastObj = query.selectFrom(sendMast).where(sendMast.sendMastId.eq(sendMastId)).fetchOne();


        if (PostSeCd.kkoMydoc.equals(sendMastObj.getPostSe()))
            return Optional.ofNullable(query.select(intgrnSendDetail.intgrnSendMast.intSendMastId)
                    .from(intgrnSendDetail)
                    .innerJoin(sendDetailKkoMydoc).on(intgrnSendDetail.sendDetailIdKkoMd.eq(sendDetailKkoMydoc.sendDetailId))
                    .where(sendDetailKkoMydoc.sendMast.sendMastId.eq(sendMastId))
                    .groupBy(intgrnSendDetail.intgrnSendMast.intSendMastId)
                    .fetchOne());

        if (PostSeCd.kkoAlimtalk.equals(sendMastObj.getPostSe()))
            return Optional.ofNullable(query.select(intgrnSendDetail.intgrnSendMast.intSendMastId)
                    .from(intgrnSendDetail)
                    .innerJoin(sendDetailKkoAlimtalk).on(intgrnSendDetail.sendDetailIdKkoAt.eq(sendDetailKkoAlimtalk.sendDetailId))
                    .where(sendDetailKkoAlimtalk.sendMast.sendMastId.eq(sendMastId))
                    .groupBy(intgrnSendDetail.intgrnSendMast.intSendMastId)
                    .fetchOne());

        if (PostSeCd.nvSigntalk.equals(sendMastObj.getPostSe()))
            return Optional.ofNullable(query.select(intgrnSendDetail.intgrnSendMast.intSendMastId)
                    .from(intgrnSendDetail)
                    .innerJoin(sendDetailNvSigntalk).on(intgrnSendDetail.sendDetailIdNvSt.eq(sendDetailNvSigntalk.sendDetailId))
                    .where(sendDetailNvSigntalk.sendMast.sendMastId.eq(sendMastId))
                    .groupBy(intgrnSendDetail.intgrnSendMast.intSendMastId)
                    .fetchOne());

        if (PostSeCd.ktSigntalk.equals(sendMastObj.getPostSe()))
            return Optional.ofNullable(query.select(intgrnSendDetail.intgrnSendMast.intSendMastId)
                    .from(intgrnSendDetail)
                    .innerJoin(sendDetailKtSigntalk).on(intgrnSendDetail.sendDetailIdKtSt.eq(sendDetailKtSigntalk.sendDetailId))
                    .where(sendDetailKtSigntalk.sendMast.sendMastId.eq(sendMastId))
                    .groupBy(intgrnSendDetail.intgrnSendMast.intSendMastId)
                    .fetchOne());

        if (PostSeCd.ktGibis.equals(sendMastObj.getPostSe()))
            return Optional.ofNullable(query.select(intgrnSendDetail.intgrnSendMast.intSendMastId)
                    .from(intgrnSendDetail)
                    .innerJoin(sendDetailKtGibis).on(intgrnSendDetail.sendDetailIdKtGbs.eq(sendDetailKtGibis.sendDetailId))
                    .where(sendDetailKtGibis.sendMast.sendMastId.eq(sendMastId))
                    .groupBy(intgrnSendDetail.intgrnSendMast.intSendMastId)
                    .fetchOne());

        return Optional.ofNullable(null);
    }

    private List<Long> findIntSendDetailIdsByKkoMydoc(String docUuid, String linkedUuid) {
        BooleanBuilder isMatched = new BooleanBuilder();
        isMatched.and(sendDetailKkoMydoc.documentBinderUuid.eq(docUuid));
        if (linkedUuid != null)
            isMatched.and(intgrnSendDetail.linkedUuid.eq(linkedUuid));

        return query.select(intgrnSendDetail.intSendDetailId)
                .from(intgrnSendDetail)
                .innerJoin(sendDetailKkoMydoc).on(intgrnSendDetail.sendDetailIdKkoMd.eq(sendDetailKkoMydoc.sendDetailId))
                .where(isMatched)
                .fetch();
    }

    private List<Long> findIntSendDetailIdsByNvSigntalk(String docUuid, String linkedUuid) {
        BooleanBuilder isMatched = new BooleanBuilder();
        isMatched.and(sendDetailNvSigntalk.docId.eq(docUuid));
        if (linkedUuid != null)
            isMatched.and(intgrnSendDetail.linkedUuid.eq(linkedUuid));

        return query.select(intgrnSendDetail.intSendDetailId)
                .from(intgrnSendDetail)
                .innerJoin(sendDetailNvSigntalk).on(intgrnSendDetail.sendDetailIdNvSt.eq(sendDetailNvSigntalk.sendDetailId))
                .where(isMatched)
                .fetch();
    }

    private List<Long> findIntSendDetailIdsByKtSigntalk(String docUuid, String linkedUuid) {
        BooleanBuilder isMatched = new BooleanBuilder();
        isMatched.and(sendDetailKtSigntalk.srcKey.eq(docUuid));
        if (linkedUuid != null)
            isMatched.and(intgrnSendDetail.linkedUuid.eq(linkedUuid));

        return query.select(intgrnSendDetail.intSendDetailId)
                .from(intgrnSendDetail)
                .innerJoin(sendDetailKtSigntalk).on(intgrnSendDetail.sendDetailIdKtSt.eq(sendDetailKtSigntalk.sendDetailId))
                .where(isMatched)
                .fetch();
    }

    private List<Long> findIntSendDetailIdsByKtGibis(String docUuid, String linkedUuid) {
        BooleanBuilder isMatched = new BooleanBuilder();
        isMatched.and(sendDetailKtGibis.srcKey.eq(docUuid));
        if (linkedUuid != null)
            isMatched.and(intgrnSendDetail.linkedUuid.eq(linkedUuid));

        return query.select(intgrnSendDetail.intSendDetailId)
                .from(intgrnSendDetail)
                .innerJoin(sendDetailKtGibis).on(intgrnSendDetail.sendDetailIdKtGbs.eq(sendDetailKtGibis.sendDetailId))
                .where(isMatched)
                .fetch();
    }

    /**
     * 전송 타깃 데이터
     * -.조건: statCd!='close'
     * and (
     * (statCd='makeok' and tryStatCd='stby' and curStatCd='MAKING')
     * or (tryStatCd in ('ok','fail) and trySeq<tryCnt and sendDt<now() and curStatCd in ('ACPT_FAIL', 'FAIL'))
     * )
     *
     * @param tryStatCds
     * @return
     */
    private BooleanBuilder isSendTarget(List<TryStatCd> tryStatCds) {
        List<TryStatCd> try1StatCds = new ArrayList<>(Arrays.asList(TryStatCd.stby));
        List<TryStatCd> try2StatCds = new ArrayList<>(Arrays.asList(TryStatCd.ok, TryStatCd.fail));
        if (!CmmnUtil.isEmpty(tryStatCds)) {
            tryStatCds.forEach(tryStatCd -> {
                try1StatCds.add(tryStatCd);
                try2StatCds.add(tryStatCd);
            });
        }


        BooleanBuilder builder = new BooleanBuilder();
        return builder.and(intgrnSendMast.statCd.ne(StatCd.close))
                .and(intgrnSendMast.trySeq.lt(intgrnSendMast.tryCnt))
                .and(

                        (intgrnSendMast.statCd.eq(StatCd.makeok)
                                .and(intgrnSendMast.tryStatCd.in(try1StatCds))
                                .and(intgrnSendDetail.curStatCd.eq(IntgrnDtlStatCd.MAKING))
                        )

                                .or(
                                        (intgrnSendMast.tryStatCd.in(try2StatCds)
                                                .and(intgrnSendMast.sendDt.before(LocalDateTime.now()))
                                                .and(intgrnSendDetail.curStatCd.in(IntgrnDtlStatCd.getFails()))
                                        )
                                )
                );


    }

//
//    @Override
//    public List<IntgrnSendDetail> findAllFetchBySendMastIdAndMkBillUseYAndUrlIsNull(Long intSendMastId, boolean urlIsNull) {
//        return query.selectFrom(intgrnSendDetail)
//                .innerJoin(intgrnSendDetail.intgrnSendMast, intgrnSendMast)
//                .fetchJoin()
//                .leftJoin(intgrnSendDetail.kkoBillMast, kkoBillMast)
//                .fetchJoin()
//                .where(intgrnSendDetail.intgrnSendMast.intSendMastId.eq(intSendMastId)
//                        .and(intgrnSendDetail.mkBillUseYn.eq("Y"))
//                        .and(urlIsNull ? intgrnSendDetail.kkoBillMast.url.isNull() : intgrnSendDetail.kkoBillMast.url.isNotNull())
//                )
//                .fetch();
//    }
}
