package com.jeequan.jeepay.pay.service.openapi.impl;

import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.ObjUtil;
import com.alibaba.fastjson.JSON;
import com.alipay.api.domain.BankcardExtInfo;
import com.jeequan.jeepay.converter.TransferConverter;
import com.jeequan.jeepay.core.constants.CS;
import com.jeequan.jeepay.core.entity.TransferOrder;
import com.jeequan.jeepay.core.entity.TransferSubject;
import com.jeequan.jeepay.core.exception.BizException;
import com.jeequan.jeepay.core.interfaces.paychannel.ITransferService;
import com.jeequan.jeepay.core.model.openapi.AccessReq;
import com.jeequan.jeepay.core.model.openapi.biz.transfer.TransferOrderParam;
import com.jeequan.jeepay.core.model.openapi.biz.transfer.TransferResp;
import com.jeequan.jeepay.core.model.openapi.biz.transfer.TransferWalletResp;
import com.jeequan.jeepay.core.model.rqrs.msg.ChannelRetMsg;
import com.jeequan.jeepay.core.model.tranfer.TransferBasicInfo;
import com.jeequan.jeepay.core.service.ValidatorService;
import com.jeequan.jeepay.core.validate.Add;
import com.jeequan.jeepay.db.entity.*;
import com.jeequan.jeepay.pay.service.TransferOrderReissueService;
import com.jeequan.jeepay.service.impl.*;
import com.jeequan.jeepay.thirdparty.channel.IsvFactory;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.tuple.MutablePair;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.support.TransactionCallback;
import org.springframework.transaction.support.TransactionTemplate;

import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;

/**
 * TODO
 *
 * @author deng
 * @since 2024/5/14
 */
@RequiredArgsConstructor
@Service
@Slf4j
public class TransferApiService {

    private final ValidatorService validatorService;
    private final TransferSubjectService transferSubjectService;
    private final TransferWalletService transferWalletService;
    private final TransferConverter transferConverter;
    private final TransferOrderService transferOrderService;
    private final TransactionTemplate transactionTemplate;

    private final TransferOrderReissueService transferOrderReissueService;
    private final MchAppService mchAppService;
    private final PackageOrderService packageOrderService;
    private final ProductInfoService productInfoService;

    private final MchInfoService mchInfoService;

    public void preCheck(AccessReq accessReq) {
        String appId = accessReq.getAppId();


        TransferOrderParam transferOrderParam = JSON.parseObject(accessReq.getBizData(), TransferOrderParam.class);
        Assert.notNull(transferOrderParam.getTransferFqId(), "缺少发起方编号参数");

        // 校验代付账号与应用的关联关系
        MchAppEntity mchApp = mchAppService.getById(appId);

        String apiCode = CS.PRODUCT_API_CODE.CONN_MAP.get(transferOrderParam.getIfCode());
        List<ProductInfo> list = productInfoService.list(apiCode);
        List<String> productIdList = list.stream().map(ProductInfo::getProductId).collect(Collectors.toList());
        Long openCount = packageOrderService.lambdaQuery().in(PackageOrder::getProductId, productIdList)
                .eq(PackageOrder::getAppId, mchApp.getAppId())
                .eq(PackageOrder::getState, PackageOrder.STATE_SUCCESS)
                .count();

        if (openCount == 0) {
            throw new BizException("当前应用未开通相关功能");
        }

        TransferSubjectEntity transferSubject = transferSubjectService.getById(transferOrderParam.getTransferFqId());

        if (!Objects.equals(transferSubject.getAppId(), appId)) {
            throw new BizException("当前转账发起方与应用不匹配");
        }

        if (!Objects.equals(transferSubject.getTransIfCode(), transferOrderParam.getIfCode())) {
            throw new BizException("转账方与通道编码不匹配");
        }

        Assert.notNull(transferSubject, "未获取到发起方数据");

        if (!transferSubject.getState().equals(TransferSubject.STATE_ENABLE)) {
            throw new BizException("当前转账发起方未启用");
        }

        if (!Objects.equals(transferSubject.getSignState(), (int) TransferSubject.SIGN_STATE_SUCCESS)) {
            throw new BizException("当前转账发起方未完成签约");
        }
    }

    /**
     * 发起代付/转账
     *
     * @param accessReq
     */
    public Object transfer(AccessReq accessReq) {
        String bizData = accessReq.getBizData();
        TransferOrderParam transferOrderParam = JSON.parseObject(bizData, TransferOrderParam.class);
        validatorService.validate(transferOrderParam, Add.class);

        if (transferOrderParam.getAccountType().equals(TransferBasicInfo.ACCOUNT_TYPE_COR)
                && transferOrderParam.getEntryType().equals(TransferBasicInfo.ENTRY_TYPE_BANK)) {
            Assert.notNull(transferOrderParam.getBankName(), "开户银行名称不能为空");
            Assert.notNull(transferOrderParam.getBankProvinceName(), "开户支行所在省不能为空");
            Assert.notNull(transferOrderParam.getBankCityName(), "开户支行所在市不能为空");
            Assert.notNull(transferOrderParam.getBankBranchName(), "开户支行名称不能为空");
        }

        Long count = transferOrderService.lambdaQuery()
                .eq(TransferOrder::getMchOrderNo, transferOrderParam.getMchOrderNo())
                .count();

        if (count > 0) {
            throw new BizException("订单号重复");
        }

        String sipw = transferOrderParam.getSipw();


        TransferSubjectEntity sponsor = transferSubjectService.getById(transferOrderParam.getTransferFqId());

        MchInfo mchInfo = mchInfoService.getById(sponsor.getMchNo());
        if (!new BCryptPasswordEncoder().matches(sipw, mchInfo.getSipw())) {
            throw new BizException("支付密码验证不通过！");
        }

        TransferWalletEntity transferWallet = transferWalletService.getByApplyId(sponsor.getTransApplyId(), null);

        ITransferService transferService = IsvFactory.getTransferService(transferWallet.getTransIfCode());
        // 生成订单信息
        TransferOrder transferOrder = transferService.genTransferOrder(transferOrderParam, sponsor, transferConverter.toModel(transferWallet), sponsor.getTransIfCode());

        BankcardExtInfo bankcardExtInfo = new BankcardExtInfo();
        bankcardExtInfo.setInstCity(transferOrderParam.getBankCityName());
        bankcardExtInfo.setInstProvince(transferOrderParam.getBankProvinceName());
        bankcardExtInfo.setInstName(transferOrderParam.getBankName());
        bankcardExtInfo.setInstBranchName(transferOrderParam.getBankBranchName());

        TransferSubjectEntity receiver = transferSubjectService.getReceiverSaveIfNotExist(
                transferOrderParam.getAccountNo(), transferOrderParam.getAccountName()
                , transferOrderParam.getEntryType(), transferOrderParam.getAccountType()
                , sponsor, bankcardExtInfo);

        transferOrder.setTransferSubjectIdJs(receiver.getId());

        TransferResp transferResp = new TransferResp();

        transactionTemplate.execute((TransactionCallback<Void>) transactionStatus -> {
            try {
                transferService.commonCheck(transferOrderParam, transferOrder);

                TransferOrderEntity dbEntity = transferConverter.toDbEntity(transferOrder);
                transferOrderService.save(dbEntity);

                ChannelRetMsg channelRetMsg = transferService.transfer(transferOrderParam, transferOrder);
                transferOrderReissueService.processChannelMsg(channelRetMsg, dbEntity);

                transferConverter.cp(dbEntity, transferResp);
            } catch (Exception e) {
                transactionStatus.setRollbackOnly();
                log.info("代付发起失败", e);

                throw new BizException(e.getMessage());
            }

            return null;
        });

        transferConverter.desensitized(transferResp);
        return transferResp;
    }

    /**
     * 代付 / 转账结果查询
     *
     * @param accessReq
     */
    public Object transferResult(AccessReq accessReq) {
        String bizData = accessReq.getBizData();
        String appId = accessReq.getAppId();
        MchAppEntity mchApp = mchAppService.getById(appId);

        TransferOrderParam orderParam = JSON.parseObject(bizData, TransferOrderParam.class);

        if (ObjUtil.isEmpty(orderParam.getMchOrderNo()) && ObjUtil.isEmpty(orderParam.getTransferId())) {
            throw new BizException("商户订单号和转账订单号不能同时为空");
        }

        TransferOrderEntity transferOrder = transferOrderService.queryMchOrder(mchApp.getMchNo(), orderParam.getMchOrderNo(), orderParam.getTransferId());
        Assert.notNull(transferOrder, "未获取到转账订单信息");

        transferOrderReissueService.processOrder(transferOrder);

        TransferOrderEntity refreshedOrder = transferOrderService.getById(transferOrder.getTransferId());
        TransferResp transferResp = new TransferResp();
        transferConverter.cp(refreshedOrder, transferResp);
        transferConverter.desensitized(transferResp);

        return transferResp;
    }

    public Object transferWalletInfo(AccessReq accessReq) {
        String appId = accessReq.getAppId();
        TransferOrderParam transferOrderParam = JSON.parseObject(accessReq.getBizData(), TransferOrderParam.class);
        Assert.notNull(transferOrderParam.getTransferFqId(), "缺少发起方编号参数");

        TransferSubjectEntity transferSubject = transferSubjectService.getById(transferOrderParam.getTransferFqId());

        TransferWalletEntity transferWallet = transferWalletService.getByApplyId(transferSubject.getTransApplyId(), null);
        Assert.notNull(transferWallet, "未获取到账户信息");

        if (!Objects.equals(appId, transferWallet.getAppId())) {
            // 非法的操作，这里以为获取到数据返回提示
            Assert.notNull(transferWallet, "未获取到账户信息");
        }

        ITransferService transferService = IsvFactory.getTransferService(transferWallet.getTransIfCode());
        MutablePair<String, Long> walletBalance = transferService.queryBalanceAmount(transferConverter.toModel(transferWallet));
        transferWallet.setAccountBookBalance(walletBalance.right);
        transferWalletService.updateById(transferWallet);

        TransferWalletResp transferWalletResp = new TransferWalletResp();
        transferWalletResp.setWalletId(transferWallet.getTransApplyId());
        transferWalletResp.setAccountBalance(walletBalance.right);

        return transferWalletResp;
    }
}
