package com.jeequan.jeepay.service.impl;

import cn.hutool.core.text.CharSequenceUtil;
import cn.hutool.extra.spring.SpringUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.jeequan.jeepay.converter.MchInfoConverter;
import com.jeequan.jeepay.core.constants.CS;
import com.jeequan.jeepay.core.entity.MchApplyment;
import com.jeequan.jeepay.core.entity.MchModifyApplyment;
import com.jeequan.jeepay.core.exception.BizException;
import com.jeequan.jeepay.core.interfaces.paychannel.IIsvmchModifyApplymentService;
import com.jeequan.jeepay.core.model.ApiRes;
import com.jeequan.jeepay.core.model.applyment.MchModifyApplymentModel;
import com.jeequan.jeepay.core.utils.SeqKit;
import com.jeequan.jeepay.core.utils.SpringBeansUtil;
import com.jeequan.jeepay.db.entity.MchModifyApplymentEntity;
import com.jeequan.jeepay.service.mapper.MchModifyApplymentMapper;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.tuple.MutablePair;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert;

import java.util.Arrays;
import java.util.Date;
import java.util.List;

/**
 * <p>
 * 商户进件信息变更服务类
 * </p>
 *
 * @author [mybatis plus generator]
 * @since 2023-12-05
 */
@Service
public class MchModifyApplymentService extends ServiceImpl<MchModifyApplymentMapper, MchModifyApplymentEntity> {

    @Autowired
    private MchApplymentService mchApplymentService;

    @Autowired
    private MchInfoConverter mchInfoConverter;

    @Autowired
    private MchModifyApplymentMapper mchModifyApplymentMapper;

    @Autowired
    private MchInfoService mchInfoService;

    @Autowired
    private AgentInfoService agentInfoService;


    public MchModifyApplymentEntity getByOrderId(String applyNo, String ifCode) {
        return lambdaQuery()
                .eq(MchModifyApplymentEntity::getChannelApplyNo, applyNo)
                .eq(MchModifyApplymentEntity::getIfCode, ifCode).one();
    }

    public void save(MchModifyApplyment mchModifyApplyment) {
        MchModifyApplymentEntity dbEntity = mchInfoConverter.toDbEntity(mchModifyApplyment);
        saveOrUpdate(dbEntity);
    }

    /**
     * 本地数据变更, 仅本地数据保存
     *
     * @return
     */
    public void localModify(MchModifyApplyment modifyApplyment) {
        MchApplyment tbMchApplyment = fetchMchApplyment(modifyApplyment);

        modifyApplyment.setState(MchApplyment.STATE_SUCCESS);
        modifyApplyment.setChannelMchNo(tbMchApplyment.getChannelMchNo());
        modifyApplyment.setMchNo(tbMchApplyment.getMchNo());
        modifyApplyment.setRemark("本地变更");
        updateMchInfo(modifyApplyment);

        IIsvmchModifyApplymentService modifyApplymentService = getModifyApplymentService(tbMchApplyment);
        saveModifiedEntity(modifyApplyment);

        MutablePair<MchModifyApplyment, MchApplyment> modifyData = performModification(modifyApplyment, tbMchApplyment, modifyApplymentService);
        handleModificationResult(modifyData);
    }

    /**
     * 变更通道数据,
     * 一般需要处理回调, 相当于发起商户信息变更, 获取到通道变更结果之后同步更新本地数据
     */
    public MchModifyApplymentEntity syncChannelModify(MchModifyApplyment modifyApplyment) {
        MchApplyment tbMchApplyment = fetchMchApplyment(modifyApplyment);
        preCheck(modifyApplyment);

        modifyApplyment.setState(MchApplyment.STATE_AUDITING);
        modifyApplyment.setChannelMchNo(tbMchApplyment.getChannelMchNo());
        modifyApplyment.setMchNo(tbMchApplyment.getMchNo());
        updateMchInfo(modifyApplyment);

        IIsvmchModifyApplymentService modifyApplymentService = getModifyApplymentService(tbMchApplyment);
        saveModifiedEntity(modifyApplyment);

        MutablePair<MchModifyApplyment, MchApplyment> modifyData = performChannelModification(modifyApplyment, tbMchApplyment, modifyApplymentService);
        handleModificationResult(modifyData);

        return mchInfoConverter.toDbEntity(modifyData.getKey());
    }

    public void preCheck(MchModifyApplyment modifyApplyment) {
        List<Byte> stateList = Arrays.asList(MchApplyment.STATE_AUDITING, MchApplyment.STATE_WAIT_SIGN, MchApplyment.STATE_SUCCESS_INEFFECTIVE);
        MchModifyApplymentEntity existData = lambdaQuery().eq(MchModifyApplymentEntity::getApplyId, modifyApplyment.getApplyId())
                .in(MchModifyApplymentEntity::getState, stateList)
                .eq(MchModifyApplymentEntity::getModifyApplyType, modifyApplyment.getModifyApplyType())
                .orderByDesc(MchModifyApplymentEntity::getCreatedAt)
                .last("limit 1")
                .one();
        if (existData != null) {
            throw new BizException("已存在同类变更数据，暂不允许该操作");
        }
    }

    public MchApplyment fetchMchApplyment(MchModifyApplyment modifyApplyment) {
        return mchInfoConverter.toModel(mchApplymentService.getById(modifyApplyment.getApplyId()));
    }

    public void updateMchInfo(MchModifyApplyment modifyApplyment) {
        MchApplyment tbMchApplyment = mchInfoConverter.toModel(mchApplymentService.getById(modifyApplyment.getApplyId()));
        modifyApplyment.setOriginDetailInfo(tbMchApplyment.getApplyDetailInfo());
        modifyApplyment.setIfCode(tbMchApplyment.getIfCode());
        modifyApplyment.setIfName(tbMchApplyment.getIfName());
    }

    private IIsvmchModifyApplymentService getModifyApplymentService(MchApplyment tbMchApplyment) {
        return ((IIsvmchModifyApplymentService) SpringBeansUtil.getBean(tbMchApplyment.getIfCode() + "IsvmchModifyApplymentService"));
    }

    public void saveModifiedEntity(MchModifyApplyment modifyApplyment) {
        saveOrUpdate(mchInfoConverter.toDbEntity(modifyApplyment));
    }

    private MutablePair<MchModifyApplyment, MchApplyment> performModification(MchModifyApplyment modifyApplyment, MchApplyment tbMchApplyment, IIsvmchModifyApplymentService modifyApplymentService) {
        MutablePair<MchModifyApplyment, MchApplyment> modifyData;
        try {
            switch (modifyApplyment.getModifyApplyType()) {
                case MchModifyApplyment.MODIFY_TYPE_BASE:
                    modifyData = modifyApplymentService.localModifyBase(new MutablePair<>(modifyApplyment, tbMchApplyment));
                    break;
                case MchModifyApplyment.MODIFY_TYPE_SETTLEMENT:
                    modifyData = modifyApplymentService.localModifySettlement(new MutablePair<>(modifyApplyment, tbMchApplyment));
                    break;
                case MchModifyApplyment.MODIFY_TYPE_RATE:
                    modifyData = modifyApplymentService.localModifyRate(new MutablePair<>(modifyApplyment, tbMchApplyment));
                    break;
                case MchModifyApplyment.MODIFY_TYPE_SETTLEMENT_TYPE:
                    modifyData = modifyApplymentService.localModifySettlementType(new MutablePair<>(modifyApplyment, tbMchApplyment));
                    break;
                default:
                    throw new BizException("未知的变更类型");
            }
        } catch (Exception e) {
            return new MutablePair<>(handleModificationException(modifyApplyment, e), null);
        }

        return modifyData;
    }

    private MutablePair<MchModifyApplyment, MchApplyment> performChannelModification(MchModifyApplyment modifyApplyment, MchApplyment tbMchApplyment, IIsvmchModifyApplymentService modifyApplymentService) {
        MutablePair<MchModifyApplyment, MchApplyment> modifyData;
        try {
            switch (modifyApplyment.getModifyApplyType()) {
                case MchModifyApplyment.MODIFY_TYPE_BASE:
                    modifyData = modifyApplymentService.syncChannelModifyBase(new MutablePair<>(modifyApplyment, tbMchApplyment));
                    break;
                case MchModifyApplyment.MODIFY_TYPE_SETTLEMENT:
                    modifyData = modifyApplymentService.syncChannelModifySettlement(new MutablePair<>(modifyApplyment, tbMchApplyment));
                    break;
                case MchModifyApplyment.MODIFY_TYPE_RATE:
                    modifyData = modifyApplymentService.syncChannelModifyRate(new MutablePair<>(modifyApplyment, tbMchApplyment));
                    break;
                case MchModifyApplyment.MODIFY_TYPE_SETTLEMENT_TYPE:
                    modifyData = modifyApplymentService.syncChannelModifySettlementType(new MutablePair<>(modifyApplyment, tbMchApplyment));
                    break;
                default:
                    throw new BizException("未知的变更类型");
            }
        } catch (Exception e) {
            return new MutablePair<>(handleModificationException(modifyApplyment, e), null);
        }
        return modifyData;
    }

    private MchModifyApplyment handleModificationException(MchModifyApplyment modifyApplyment, Exception e) {
        MchModifyApplyment result = new MchModifyApplyment();
        result.setRemark(e.getMessage());
        result.setApplyId(modifyApplyment.getApplyId());
        result.setState(MchApplyment.STATE_REJECT_WAIT_MODIFY);
        return result;
    }

    private void handleModificationResult(MutablePair<MchModifyApplyment, MchApplyment> modifyData) {
        MchModifyApplyment result = modifyData.getKey();
        if (result.getState() == com.jeequan.jeepay.core.entity.MchApplyment.STATE_SUCCESS) {
            updateById(mchInfoConverter.toDbEntity(modifyData.getKey()));
            if (modifyData.getValue() != null) {
                mchApplymentService.updateById(mchInfoConverter.toDbEntity(modifyData.getValue()));
            }
        } else {
            updateById(mchInfoConverter.toDbEntity(modifyData.getKey()));
        }
    }

    /**
     * 分页查询
     *
     * @param iPage
     * @param modifyApplyment
     * @param paramJSON
     * @return
     */
    public Page<MchModifyApplymentEntity> pageList(Page<MchModifyApplymentEntity> iPage, MchModifyApplymentEntity modifyApplyment, JSONObject paramJSON) {
        // 时间范围条件
        Date[] dateRange = modifyApplyment.buildQueryDateRange();
        if (dateRange[0] != null) {
            modifyApplyment.setFirstDate(dateRange[0]);
        }
        if (dateRange[1] != null) {
            modifyApplyment.setLastDate(dateRange[1]);
        }
        Page<MchModifyApplymentEntity> resultPage = mchModifyApplymentMapper.selectListPage(iPage, modifyApplyment);

        resultPage.getRecords().forEach(r -> mchInfoConverter.desensitized(r));

        return resultPage;
    }

    /**
     * 申请单审核
     *
     * @param modifyApplymentId
     * @param state
     * @param remark
     */
    @Transactional(rollbackFor = Exception.class)
    public void audit(String modifyApplymentId, Boolean state, String remark, boolean syncChannel) {

        Assert.notNull(modifyApplymentId, "缺失申请单ID参数");
        Assert.notNull(state, "缺失审核状态参数");

        if (!state && StringUtils.isEmpty(remark)) {
            throw new BizException("审核驳回时需要填写驳回原因");
        }
        MchModifyApplymentEntity mchModifyApplyment = getById(modifyApplymentId);
        Assert.notNull(mchModifyApplyment, "未获取到申请单数据");

        if (MchApplyment.STATE_AUDITING != mchModifyApplyment.getState() && MchApplyment.STATE_AFFIRM != mchModifyApplyment.getState()) {
            throw new BizException("只能处理审核中的申请单数据");
        }

        MchApplyment mchApplyment = mchInfoConverter.toModel(mchApplymentService.getById(mchModifyApplyment.getApplyId()));
        if (mchApplyment == null) {
            throw new BizException("变更的商户信息异常");
        }

        mchModifyApplyment.setRemark(remark);
        // 审核驳回
        if (!state) {
            mchModifyApplyment.setState(MchApplyment.STATE_REJECT_WAIT_MODIFY);
            updateById(mchModifyApplyment);
            return;
        }

        // 审核通过，同步商户变更逻辑
        updateById(mchModifyApplyment);
        MchModifyApplyment model = mchInfoConverter.toModel(mchModifyApplyment);
        // 申请单类型是同步到上游并且制定当前操作类型为同步上游通道时，才做同步上游的操作
        if (syncChannel && model.getOtherOperationType().equals(MchModifyApplyment.OPERATION_TYPE_SYNC)) {
            syncChannelModify(model);
        } else {
            localModify(model);
        }
    }

    @Transactional(rollbackFor = Exception.class)
    public ApiRes mchAudit(Boolean state, MchModifyApplymentEntity applymentEntity) {
        if (StringUtils.isEmpty(applymentEntity.getModifyApplyId())) {
            throw new BizException("缺失申请单ID参数");
        }
        if (state == null) {
            throw new BizException("缺失审核状态参数");
        }
        if (!state && StringUtils.isEmpty(applymentEntity.getRemark())) {
            throw new BizException("审核驳回时需要填写驳回原因");
        }
        MchModifyApplymentEntity mchModifyApplyment = getById(applymentEntity.getModifyApplyId());
        if (mchModifyApplyment == null) {
            throw new BizException("申请单数据异常");
        }
        if (MchApplyment.STATE_AFFIRM != mchModifyApplyment.getState()) {
            throw new BizException("只能处理运营或者服务商代变更的申请单数据");
        }
        MchApplyment mchApplyment = mchInfoConverter.toModel(mchApplymentService.getById(mchModifyApplyment.getApplyId()));
        if (mchApplyment == null) {
            throw new BizException("变更的商户信息异常");
        }
        //审核驳回
        if (!state) {
            mchModifyApplyment.setState(MchApplyment.STATE_REJECT_WAIT_MODIFY);
            mchModifyApplyment.setRemark(applymentEntity.getRemark());
            updateById(mchModifyApplyment);
            return ApiRes.ok();
        }
        //审核通过
        // 变更类型，1: 本地变更； 2: 变更并同步到上游通道
        String operationType = mchModifyApplyment.getOtherOperationType();
        MchModifyApplymentModel reqModel = JSON.parseObject(mchModifyApplyment.getApplyDetailInfo(), MchModifyApplymentModel.class);
        reqModel.setMchApplyId(applymentEntity.getApplyId());
        Assert.notNull(operationType, "缺少操作类型[otherOperationType]");
        Assert.notNull(reqModel.getMchApplyId(), "缺少商户编号[mchApplyId]");
        Assert.notNull(reqModel.getModifyApplyType(), "缺少变更类型[modifyApplyType]");
        switch (reqModel.getModifyApplyType()) {
            case MchModifyApplyment.MODIFY_TYPE_BASE:
                Assert.isTrue(!CharSequenceUtil.isEmpty(reqModel.getMchShortName()), "商户简称[mchShortName]不能为空");
                break;
            case MchModifyApplyment.MODIFY_TYPE_SETTLEMENT:
                Assert.notNull(reqModel.getSettAccountType(), "结算账户类型[settleAccountType]不能为空");
                Assert.notNull(reqModel.getIllegal(), "结算类型[illegal]不能为空");
                Assert.notNull(reqModel.getSettAccountNo(), "结算账户号[settAccountNo]不能为空");

                if ("Y".equals(reqModel.getIllegal())) {
                    Assert.notNull(reqModel.getSettAccountName(), "结算账户名称[settAccountName]不能为空");
                    Assert.notNull(reqModel.getSettAccountIdcardNo(), "结算人身份证号[settAccountIdcardNo]不能为空");
                    Assert.notNull(reqModel.getSettAccountIdcard1Img(), "结算人身份证卡号面图片[settAccountIdcard1Img]不能为空");
                    Assert.notNull(reqModel.getSettAccountIdcard2Img(), "结算人身份证国徽面图片[settAccountIdcard2Img]不能为空");
                    Assert.notNull(reqModel.getSettAccountIdcardEffectBegin(), "结算人身份证有效期开始时间[settAccountIdcardEffectBegin]不能为空");
                    Assert.notNull(reqModel.getSettAccountIdcardEffectEnd(), "结算人身份证有效期截止时间[settAccountIdcardEffectEnd]不能为空");
                }
                // 其他的校验放到业务代码中处理
                break;
            case MchModifyApplyment.MODIFY_TYPE_SETTLEMENT_TYPE:
                // 变更结算类型
                Assert.notNull(reqModel.getSettlementType(), "结算方式[settlementType]不能为空");
                if (!reqModel.getSettlementType().equals(CS.SETTLEMENT_TYPE.D1)
                        && !reqModel.getSettlementType().equals(CS.SETTLEMENT_TYPE.D0)
                        && !reqModel.getSettlementType().equals(CS.SETTLEMENT_TYPE.T1)) {
                    throw new BizException("结算方式[settlementType]必须为【D0】、【D1】、【T1】");
                }
                break;
            default:
                throw new BizException("未知的变更类型");
        }

        // check
        MchModifyApplyment modifyApplyment = new MchModifyApplyment();

        modifyApplyment.setModifyApplyType(reqModel.getModifyApplyType());
        modifyApplyment.setApplyDetailInfo(JSON.toJSONString(reqModel));
        modifyApplyment.setApplyId(reqModel.getMchApplyId());
        modifyApplyment.setApplyPageType(applymentEntity.getApplyPageType());
        modifyApplyment.setUpdatedAt(new Date());
        modifyApplyment.setCreatedUid(applymentEntity.getCreatedUid());
        modifyApplyment.setCreatedBy(applymentEntity.getCreatedBy());
        modifyApplyment.setModifyApplyId(applymentEntity.getModifyApplyId());
        if ("1".equals(operationType)) {
            localModify(modifyApplyment);
        } else if ("2".equals(operationType)) {
            syncChannelModify(modifyApplyment);
        } else {
            throw new BizException("非法的操作类型[operationType]");
        }

        return ApiRes.ok();

    }

    /**
     * 通用申请变更
     *
     * @param currentMchNo
     * @param applyId
     * @param type
     */
    public void commonApply(String currentMchNo, String applyId, byte type) {
        MchModifyApplyment mchModifyApplyment = new MchModifyApplyment();
        mchModifyApplyment.setApplyId(applyId);
        mchModifyApplyment.setModifyApplyType(type);
        mchModifyApplyment.setModifyApplyId(SeqKit.genMchNotifyApplyNo());
        preCheck(mchModifyApplyment);
        MchApplyment mchApplyment = mchInfoConverter.toModel(mchApplymentService.getById(applyId));
        if (mchApplyment == null) {
            throw new BizException("商户信息异常");
        }
        if (!mchApplyment.getMchNo().equals(currentMchNo)) {
            throw new BizException("当前数据暂无操作权限");
        }
        JSONObject applyDetailInfo = new JSONObject();
        applyDetailInfo.put("mchShortName", mchApplyment.getMchShortName());
        switch (type) {
            case MchModifyApplyment.MODIFY_TYPE_REFUND_ACCOUNT_TYPE:
                JSONObject originDetailInfo = new JSONObject();
                originDetailInfo.put("mchShortName", mchApplyment.getMchShortName());
                originDetailInfo.put("refundWay", mchApplyment.getRefundWay());
                applyDetailInfo.put("refundWay", MchApplyment.REFUND_WAY_PAYMENT.equals(mchApplyment.getRefundWay()) ? MchApplyment.REFUND_WAY_PLATFORM : MchApplyment.REFUND_WAY_PAYMENT);
                if (CS.IF_CODE.LKLSPAY.equals(mchApplyment.getIfCode())) {
                    JSONObject params = JSON.parseObject(mchApplyment.getSuccResParameter());
                    applyDetailInfo.put("termNo", params.getString("termNo"));
                    applyDetailInfo.put("channelMchNo", mchApplyment.getChannelMchNo());
                }
                mchModifyApplyment.setOriginDetailInfo(originDetailInfo.toString());
                mchModifyApplyment.setApplyDetailInfo(applyDetailInfo.toString());
                mchApplyment.setRemark("商户退货类型变更");
                break;
            default:
                throw new BizException("未知的变更类型");
        }
        mchModifyApplyment.setIfName(mchApplyment.getIfName());
        mchModifyApplyment.setIfCode(mchApplyment.getIfCode());
        mchModifyApplyment.setChannelMchNo(mchApplyment.getChannelMchNo());
        mchModifyApplyment.setMchNo(mchApplyment.getMchNo());
//        mchModifyApplyment.setApplyPageType(mchApplyment.getApplyPageType()); 由前端传
        mchModifyApplyment.setState(MchApplyment.STATE_AUDITING);
        saveModifiedEntity(mchModifyApplyment);
    }

    public int getModifyApplyMentChecknNumber(String mchNo) {
        return mchModifyApplymentMapper.getModifyApplyMentChecknNumber(mchNo);
    }

    public void getResult(String modifyApplyId) {
        MchModifyApplymentEntity modifyApplyment = getById(modifyApplyId);
        Assert.notNull(modifyApplyment, "找不到变更记录");

        com.jeequan.jeepay.db.entity.MchApplyment mchApplyment = mchApplymentService.getById(modifyApplyment.getApplyId());
        Assert.notNull(mchApplyment, "找不到进件信息");
        if (modifyApplyment.getState() == MchApplyment.STATE_SUCCESS || modifyApplyment.getState() == MchApplyment.STATE_REJECT_WAIT_MODIFY) {
            return;
        }

        IIsvmchModifyApplymentService isvmchModifyApplymentService = SpringUtil.getBean(modifyApplyment.getIfCode() + "IsvmchModifyApplymentService", IIsvmchModifyApplymentService.class);
        // 调起变更结果查询接口
        MutablePair<MchModifyApplyment, com.jeequan.jeepay.core.entity.MchApplyment> queryModifyResult = isvmchModifyApplymentService.queryModifyResult(new MutablePair<>(mchInfoConverter.toModel(modifyApplyment), mchInfoConverter.toModel(mchApplyment)));
        if (queryModifyResult == null) {
            return;
        }

        if (queryModifyResult.left != null) {
            updateById(mchInfoConverter.toDbEntity(queryModifyResult.left));
        }

        if (queryModifyResult.right != null) {
            mchApplymentService.updateById(mchInfoConverter.toDbEntity(queryModifyResult.right));
        }
    }

    public MchModifyApplymentEntity getInfoById(String recordId) {
        MchModifyApplymentEntity applyment = getById(recordId);

        if (applyment == null) {
            return null;
        }

        mchInfoConverter.desensitized(applyment);

        return applyment;
    }
}
