package com.brilliance.isc.common.lock.common;

import com.brilliance.isc.bo.Ord;
import com.brilliance.isc.bo.Spt;
import com.brilliance.isc.bo.Stb;
import com.brilliance.isc.bo.Trn;
import com.brilliance.isc.bo.model.Gcdgrp;
import com.brilliance.isc.bo.model.Gidgrp;
import com.brilliance.isc.bo.model.Ncdgrp;
import com.brilliance.isc.bo.model.Nidgrp;
import com.brilliance.isc.common.codetable.service.CodetableService;
import com.brilliance.isc.common.component.DyncDaoService;
import com.brilliance.isc.common.context.SettleContext;
import com.brilliance.isc.common.lock.BizLocker;
import com.brilliance.isc.common.lock.LockInfo;
import com.brilliance.isc.common.transaction.vo.BaseTransactionVo;
import com.brilliance.isc.common.trnmod.service.PendingCheckService;
import com.brilliance.isc.common.util.Utils;
import com.brilliance.isc.common.vo.BizInfoVo;
import com.brilliance.isc.constant.ErrorCode;
import com.brilliance.isc.exception.RestBizException;
import com.brilliance.isc.mda.dao.OrdMapper;
import com.brilliance.isc.mda.dao.StbMapper;
import com.brilliance.isc.vo.PendingCheckResult;
import com.brilliance.isc.vo.PendingCheckVo;
import com.brilliance.isc.vo.manager.StbVo;
import com.brilliance.mda.runtime.mda.util.MdaUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;

import java.util.List;
import java.util.Map;


@Component
public class BizCommonLocker {

    private final Logger logger = LoggerFactory.getLogger(this.getClass());


    @Autowired
    private BizLocker bizLocker;

    @Autowired
    private PendingCheckService pendingCheckService;

    @Autowired
    private StbMapper stbMapper;

    @Autowired
    private DyncDaoService dyncDaoService;
    @Autowired
    private OrdMapper ordMapper;
    @Autowired
    private CodetableService codetableService;

    private final String GIDGRP = "gidgrp";
    private final String GCDGRP = "gcdgrp";

    private final String NIDGRP = "nidgrp";
    private final String NCDGRP = "ncdgrp";

    public static final int GIT_TYP = 1;


    /**
     * 锁定业务交易的函数，将根据交易码分别锁定模型
     *
     * @param baseTransactionVo
     * @return
     */
    public boolean lockTransBo(BaseTransactionVo baseTransactionVo) {
        String transName = baseTransactionVo.getTransName().toUpperCase();
        int biztyp = 0;

        //保函类交易type为1
        if (transName.startsWith("GI") ||
                transName.startsWith("GC") ||
                transName.startsWith("GE") ||
                transName.startsWith("NI") ||
                transName.startsWith("NE") ||
                transName.startsWith("NC") ||
                transName.startsWith("HI")
        ) {
            biztyp = 1;
        } else {
            //为二期后后续通用锁业务逻辑
            return commonLockTransBo(baseTransactionVo);
        }
        if (biztyp > 0) {
            return lockTransBo(biztyp, baseTransactionVo);
        }
        return true;
    }

    public boolean lockTransBo(int BIZ_TYP, BaseTransactionVo baseTransactionVo) {
        //lockSpt(baseTransactionVo.getSpt(),baseTransactionVo);
        /**
         * 按照交易类型，锁定
         */
        switch (BIZ_TYP) {
            case GIT_TYP:
                return lockGitTransBo(baseTransactionVo);
        }
        return true;
    }

    /**
     * 锁定保函交易
     *
     * @param baseTransactionVo 交易模型
     * @return
     */
    public boolean lockGitTransBo(BaseTransactionVo baseTransactionVo) {
        String transName = baseTransactionVo.getTransName().toUpperCase();
        int cnt = 0;
        Gidgrp gidgrp = Utils.getFieldValue(baseTransactionVo, GIDGRP);
        if (gidgrp != null) {
            cnt += lockGidgrp(baseTransactionVo, gidgrp, transName);
        }
        Gcdgrp gcdgrp = Utils.getFieldValue(baseTransactionVo, GCDGRP);
        if (gcdgrp != null) {
            cnt += lockGcdgrp(baseTransactionVo, gcdgrp, transName);
        }
        Nidgrp nidgrp = Utils.getFieldValue(baseTransactionVo, NIDGRP);
        if (nidgrp != null) {
            cnt += lockNidgrp(baseTransactionVo, nidgrp, transName);
        }
        Ncdgrp ncdgrp = Utils.getFieldValue(baseTransactionVo, NCDGRP);
        if (ncdgrp != null) {
            cnt += lockNcdgrp(baseTransactionVo, ncdgrp, transName);
        }
        return cnt == 0;
    }

    public void lockTrn(Trn trn, BaseTransactionVo baseTransactionVo) {
        if (trn == null || MdaUtils.isEmpty(trn.getInr())) {
            return;
        }
        LockInfo lockInfoTrn = LockInfo.builder().objInr(trn.getInr()).objType("TRN")
                .trnName("TRNREL")
                .usrId(SettleContext.getSessionUserVo().getLogName()).reentrant(true).build();
        if (!bizLocker.lock(lockInfoTrn)) {
            //锁定SPT失败
            logger.error("锁定复核任务失败[TRN:{}]", trn.getInr());
            LockInfo lockInfo = bizLocker.getLock(lockInfoTrn);
            if (lockInfo != null) {
                throw new RestBizException(ErrorCode.LOCK_BIZ_FAIL, "该笔复核数据已被" + lockInfo.getUsrId() + "使用" + lockInfo.getTrnName() + "锁定");
            }
            throw new RestBizException(ErrorCode.LOCK_BIZ_FAIL, "锁定复核任务失败");
        }
    }


    public void lockSpt(Spt spt, BaseTransactionVo baseTransactionVo) {
        if (spt == null || MdaUtils.isEmpty(spt.getInr())) {
            return;
        }
        String objtyp = spt.getObjtyp();
        String objinr = spt.getObjinr();
        String ordinr = spt.getOrdinr();
        if (!MdaUtils.isEmpty(objtyp) && !MdaUtils.isEmpty(objinr) && !MdaUtils.isEmpty(ordinr)) {
            if (!isAccess(objtyp, objinr, ordinr)) {
                throw new RestBizException(ErrorCode.LOCK_BIZ_FAIL, "当前交易不准进入：请先完成起始交易");
            }
        }
        //锁定SPT
        LockInfo lockInfoSpt = LockInfo.builder().objInr(spt.getInr()).objType("SPT")
                .trnName(baseTransactionVo.getTransName().toUpperCase())
                .usrId(SettleContext.getSessionUserVo().getLogName()).reentrant(true).build();
        if (!bizLocker.lock(lockInfoSpt)) {
            //锁定SPT失败
            logger.error("锁定待办任务失败[SPT:{}]", spt.getInr());
            LockInfo lockInfo = bizLocker.getLock(lockInfoSpt);
            if (lockInfo != null) {
                throw new RestBizException(ErrorCode.LOCK_BIZ_FAIL, "该笔业务数据已被" + lockInfo.getUsrId() + "使用" + lockInfo.getTrnName() + "锁定");
            }
            throw new RestBizException(ErrorCode.LOCK_BIZ_FAIL, "锁定待办任务失败");
        }

        //如果该笔业务有objtyp和objinr，则去锁定数据
        if (!MdaUtils.isEmpty(spt.getObjinr()) && !MdaUtils.isEmpty(spt.getObjtyp())) {
            //锁定业务数据
            LockInfo lockInfoObj = LockInfo.builder().objInr(spt.getObjinr()).objType(spt.getObjtyp())
                    .trnName(baseTransactionVo.getTransName().toUpperCase())
                    .usrId(SettleContext.getSessionUserVo().getLogName()).reentrant(true).build();
            if (!bizLocker.lock(lockInfoObj)) {
                //锁定业务交易以失败
                logger.error("锁定业务交易{}数据[{}:{}]失败", baseTransactionVo.getTransName(), spt.getObjtyp(), spt.getInr());
                LockInfo lockInfo = bizLocker.getLock(lockInfoObj);
                if (lockInfo != null) {
                    throw new RestBizException(ErrorCode.LOCK_BIZ_FAIL, "该笔业务数据已被" + lockInfo.getUsrId() + "使用" + lockInfo.getTrnName() + "锁定");
                }
                throw new RestBizException(ErrorCode.LOCK_BIZ_FAIL, "锁定业务交易失败");
            }
            logger.info("锁定业务交易{}数据[{}:{}]成功", baseTransactionVo.getTransName(), spt.getObjtyp(), spt.getInr());
        }
    }

    /**
     * 通用交易锁定
     */
    public boolean commonLockTransBo(BaseTransactionVo baseTransactionVo) {
        if (!getSwitchFlag()) {
            logger.info("锁定函数暂时关闭");
            return true;
        }
        String transName = baseTransactionVo.getTransName().toUpperCase();
        BizInfoVo bizInfoVo = SettleContext.invokeBizInfo(baseTransactionVo);
        String objtyp = bizInfoVo.getObjtyp();
        String objinr = bizInfoVo.getObjinr();
        int cnt = 0;
        //锁当前交易
        if (!MdaUtils.isEmpty(objtyp) && !MdaUtils.isEmpty(objinr)) {
            cnt += doCommonLockTransBo(baseTransactionVo, objtyp, objinr, transName);
        }
        //锁父交易
        String pnttyp = bizInfoVo.getPnttyp();
        String pntinr = bizInfoVo.getPntinr();
        if (!MdaUtils.isEmpty(pnttyp) && !MdaUtils.isEmpty(pntinr)) {
            cnt += doCommonLockTransBo(baseTransactionVo, pnttyp, pntinr, transName);
        }
        return cnt == 0;
    }

    /**
     * 判断当前待办交易是否准许进入
     *
     * @param objtyp 业务主表
     * @param objinr 业务主表主键
     * @param ordinr 当前待办任务关联的ord主键
     * @return 准许进入返回true
     */
    public boolean isAccess(String objtyp, String objinr, String ordinr) {
        try {
            objtyp = objtyp.length() > 3 ? objtyp.substring(0, 3) : objtyp;
            Map<String, Object> map = dyncDaoService.dyncReadByInr(objtyp, objinr);
            if (!CollectionUtils.isEmpty(map)) {
                return true;
            }
            //主表数据不存在，继续判断是否为起头交易
            Ord ord = ordMapper.selectFirstByObjtypAndObjinr(objtyp, objinr);
            if (ord != null && ordinr.equals(ord.getInr())) {
                return true;
            }
            return false;
        } catch (Exception e) {
            logger.warn("准入判断失败直接忽略：{}", e.getLocalizedMessage());
        }
        return true;
    }

    private boolean getSwitchFlag() {
        StbVo stbVo = new StbVo();
        stbVo.setTbl("DBLOCK");
        List<Stb> stbs = stbMapper.queryByPage(stbVo);
        if (stbs == null || stbs.size() == 0) {
            return false;
        }
        return "Y".equals(stbs.get(0).getCod()) ? true : false;
    }

    /**
     * 通用交易锁定具体处理
     */
    private int doCommonLockTransBo(BaseTransactionVo baseTransactionVo, String objTyp, String objInr, String trnName) {
        LockInfo lockInfo = LockInfo.builder().objInr(objInr)
                .objType(objTyp)
                .usrId(SettleContext.getSessionUserVo().getLogName())
                .trnName(trnName)
                .reentrant(true)
                .build();
        int cnt = bizLocker.lock(lockInfo) ? 0 : 1;
        if (cnt > 0) {
            checkLockInfo(lockInfo);
        } else {
            //检查Pending
            checkPending(baseTransactionVo, lockInfo);
        }
        return cnt;
    }

    public int lockGidgrp(BaseTransactionVo baseTransactionVo, Gidgrp gidgrp, String trnName) {
        if (gidgrp.getRec() != null && !MdaUtils.isEmpty(gidgrp.getRec().getInr())) {
            LockInfo lockInfo = LockInfo.builder().objInr(gidgrp.getRec().getInr())
                    .objType("GID")
                    .usrId(SettleContext.getSessionUserVo().getLogName())
                    .trnName(trnName)
                    .reentrant(true)
                    .build();
            int cnt = bizLocker.lock(lockInfo) ? 0 : 1;
            if (cnt > 0) {
                checkLockInfo(lockInfo);
            } else {
                //检查Pending
                checkPending(baseTransactionVo, lockInfo);
            }
            return cnt;
        }
        return 0;
    }

    public int lockGcdgrp(BaseTransactionVo baseTransactionVo, Gcdgrp gcdgrp, String trnName) {
        if (gcdgrp.getRec() != null && !MdaUtils.isEmpty(gcdgrp.getRec().getInr())) {
            LockInfo lockInfo = LockInfo.builder().objInr(gcdgrp.getRec().getInr())
                    .objType("GCD")
                    .usrId(SettleContext.getSessionUserVo().getLogName())
                    .trnName(trnName)
                    .reentrant(true)
                    .build();
            int cnt = bizLocker.lock(lockInfo) ? 0 : 1;
            if (cnt > 0) {
                checkLockInfo(lockInfo);
            } else {
                //检查Pending
                checkPending(baseTransactionVo, lockInfo);
            }
            return cnt;
        }
        return 0;
    }

    public int lockNidgrp(BaseTransactionVo baseTransactionVo, Nidgrp nidgrp, String trnName) {
        if (nidgrp.getRec() != null && !MdaUtils.isEmpty(nidgrp.getRec().getInr())) {
            LockInfo lockInfo = LockInfo.builder().objInr(nidgrp.getRec().getInr())
                    .objType("NID")
                    .usrId(SettleContext.getSessionUserVo().getLogName())
                    .trnName(trnName)
                    .reentrant(true)
                    .build();
            int cnt = bizLocker.lock(lockInfo) ? 0 : 1;
            if (cnt > 0) {
                checkLockInfo(lockInfo);
            } else {
                //检查Pending
                checkPending(baseTransactionVo, lockInfo);
            }
            return cnt;
        }
        return 0;
    }

    public int lockNcdgrp(BaseTransactionVo baseTransactionVo, Ncdgrp ncdgrp, String trnName) {
        if (ncdgrp.getRec() != null && !MdaUtils.isEmpty(ncdgrp.getRec().getInr())) {
            LockInfo lockInfo = LockInfo.builder().objInr(ncdgrp.getRec().getInr())
                    .objType("NCD")
                    .usrId(SettleContext.getSessionUserVo().getLogName())
                    .trnName(trnName)
                    .reentrant(true)
                    .build();
            int cnt = bizLocker.lock(lockInfo) ? 0 : 1;
            if (cnt > 0) {
                checkLockInfo(lockInfo);
            } else {
                //检查Pending
                checkPending(baseTransactionVo, lockInfo);
            }
            return cnt;
        }
        return 0;
    }

    private void checkLockInfo(LockInfo info) {
        LockInfo lockInfo = bizLocker.getLock(info);
        //锁定业务交易以失败
        logger.error("锁定业务交易{}数据[{}:{}]失败", info.getTrnName(), info.getObjType(), info.getObjInr());
        if (lockInfo != null) {
            logger.error("该笔业务交易数据[{}:{}]，已被{}使用{}锁定", info.getObjType(), info.getObjInr(), lockInfo.getUsrId(), lockInfo.getTrnName());
            throw new RestBizException(ErrorCode.LOCK_BIZ_FAIL, "该笔业务数据已被" + lockInfo.getUsrId() + "使用" + lockInfo.getTrnName() + "锁定");
        }
        logger.error("无法读取锁定信息数据[{}:{}]", info.getObjType(), info.getObjInr());
        throw new RestBizException(ErrorCode.LOCK_BIZ_FAIL, "锁定业务交易失败");
    }

    public void unlockAllOfTrans(String transName, String userId) {
        bizLocker.unlockAllOfTrans(transName, userId);
    }

    public PendingCheckResult checkPending(BaseTransactionVo baseTransactionVo, LockInfo lockInfo) {
        //在初始化时做CheckPending判断
        if (!SettleContext.isInitTransSta()) {
            return null;
        }
        //take进来的，不做警告，老国结如此
        if (baseTransactionVo.getSpt() != null && !MdaUtils.isEmpty(baseTransactionVo.getSpt().getInr())) {
            return null;
        }
        //检查Pneding
        PendingCheckVo pendingCheckVo = new PendingCheckVo();
        pendingCheckVo.setSetWarning(true);
        pendingCheckVo.setObjinr(lockInfo.getObjInr());
        pendingCheckVo.setObjtyp(lockInfo.getObjType());
        pendingCheckVo.setTransName(lockInfo.getTrnName());
        return pendingCheckService.checkPendingSPTs(pendingCheckVo);
    }

    public String convertTransName(String trnName) {
        //ATPTXT
        String trnChsName = codetableService.getTblLabel(trnName, "ATPTXT", "CN");
        if (MdaUtils.isEmpty(trnChsName)) {
            return trnName;
        }
        return trnChsName;
    }
}
