package com.slodon.b2b2c.model.system;


import cn.hutool.core.date.DateUtil;
import com.slodon.b2b2c.business.example.OrderExample;
import com.slodon.b2b2c.business.pojo.Order;
import com.slodon.b2b2c.business.pojo.OrderPay;
import com.slodon.b2b2c.core.constant.BillConst;
import com.slodon.b2b2c.core.constant.OrderConst;
import com.slodon.b2b2c.core.constant.PayConst;
import com.slodon.b2b2c.core.constant.StoreConst;
import com.slodon.b2b2c.core.exception.MallException;
import com.slodon.b2b2c.core.response.PagerInfo;
import com.slodon.b2b2c.dao.read.business.OrderReadMapper;
import com.slodon.b2b2c.dao.read.seller.StoreReadMapper;
import com.slodon.b2b2c.dao.read.system.BillOrderSplitReadMapper;
import com.slodon.b2b2c.dao.write.system.BillOrderBindWriteMapper;
import com.slodon.b2b2c.dao.write.system.BillOrderSplitWriteMapper;
import com.slodon.b2b2c.dao.write.system.BillWriteMapper;
import com.slodon.b2b2c.seller.pojo.Store;
import com.slodon.b2b2c.starter.entity.cpcn.TxSplitAccountRequest;
import com.slodon.b2b2c.starter.util.ZjPayUtil;
import com.slodon.b2b2c.system.example.BillExample;
import com.slodon.b2b2c.system.example.BillOrderBindExample;
import com.slodon.b2b2c.system.example.BillOrderSplitExample;
import com.slodon.b2b2c.system.pojo.Bill;
import com.slodon.b2b2c.system.pojo.BillOrderBind;
import com.slodon.b2b2c.system.pojo.BillOrderSplit;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
import payment.api.tx.aggregate.Tx5016Response;
import payment.api.tx.aggregate.Tx5031Response;
import payment.api.util.GUIDGenerator;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

/**
 * 结算-中金商户订单分账表model
 */
@Component
@Slf4j
public class BillOrderSplitModel {
    @Resource
    private BillOrderSplitReadMapper billOrderSplitReadMapper;

    @Resource
    private BillOrderSplitWriteMapper billOrderSplitWriteMapper;
    @Resource
    private StringRedisTemplate stringRedisTemplate;
    @Resource
    private OrderReadMapper orderReadMapper;
    @Resource
    private StoreReadMapper storeReadMapper;
    @Resource
    private BillWriteMapper billWriteMapper;
    @Resource
    private BillOrderBindWriteMapper billOrderBindWriteMapper;

    /**
     * 新增结算-中金商户订单分账表
     *
     * @param billOrderSplit 结算-中金商户订单分账信息
     * @return
     */
    public Integer saveBillOrderSplit(BillOrderSplit billOrderSplit) {
        int count = billOrderSplitWriteMapper.insert(billOrderSplit);
        if (count == 0) {
            throw new MallException("添加结算-中金商户订单分账表失败，请重试");
        }
        return count;
    }

    /**
     * 根据splitId删除结算-中金商户订单分账表
     *
     * @param splitId splitId
     * @return
     */
    public Integer deleteBillOrderSplit(Integer splitId) {
        if (StringUtils.isEmpty(splitId)) {
            throw new MallException("请选择要删除的数据");
        }
        int count = billOrderSplitWriteMapper.deleteByPrimaryKey(splitId);
        if (count == 0) {
            log.error("根据splitId：" + splitId + "删除结算-中金商户订单分账表失败");
            throw new MallException("删除结算-中金商户订单分账表失败,请重试");
        }
        return count;
    }

    /**
     * 根据splitId更新结算-中金商户订单分账表
     *
     * @param billOrderSplit 结算-中金商户订单分账信息
     * @return
     */
    public Integer updateBillOrderSplit(BillOrderSplit billOrderSplit) {
        if (StringUtils.isEmpty(billOrderSplit.getSplitId())) {
            throw new MallException("请选择要修改的数据");
        }
        int count = billOrderSplitWriteMapper.updateByPrimaryKeySelective(billOrderSplit);
        if (count == 0) {
            log.error("根据splitId：" + billOrderSplit.getSplitId() + "更新结算-中金商户订单分账表失败");
            throw new MallException("更新结算-中金商户订单分账表失败,请重试");
        }
        return count;
    }

    /**
     * 根据splitId获取结算-中金商户订单分账表详情
     *
     * @param splitId splitId
     * @return
     */
    public BillOrderSplit getBillOrderSplitBySplitId(Integer splitId) {
        return billOrderSplitReadMapper.getByPrimaryKey(splitId);
    }

    /**
     * 根据条件获取结算-中金商户订单分账表列表
     *
     * @param example 查询条件信息
     * @param pager   分页信息
     * @return
     */
    public List<BillOrderSplit> getBillOrderSplitList(BillOrderSplitExample example, PagerInfo pager) {
        List<BillOrderSplit> billOrderSplitList;
        if (pager != null) {
            pager.setRowsCount(billOrderSplitReadMapper.countByExample(example));
            billOrderSplitList = billOrderSplitReadMapper.listPageByExample(example, pager.getStart(), pager.getPageSize());
        } else {
            billOrderSplitList = billOrderSplitReadMapper.listByExample(example);
        }
        return billOrderSplitList;
    }

    /**
     * 执行分账
     * 此处加事务处理，防止重复打款或未打款情况
     *
     * @param orderPay 支付信息
     * @param order 订单
     * @param splitAmount 分账金额
     * @param billSn 计算单号
     */
    @Transactional
    public void splitAccount(OrderPay orderPay, Order order, BigDecimal splitAmount, String billSn) throws Exception {
        if (splitAmount.compareTo(BigDecimal.ZERO) > 0){
            //分账金额大于0，给商户分账
            //查询是否已有分账记录
            BillOrderSplitExample billOrderSplitExample = new BillOrderSplitExample();
            billOrderSplitExample.setOrderSn(order.getOrderSn());
            List<BillOrderSplit> billOrderSplits = billOrderSplitWriteMapper.listByExample(billOrderSplitExample);
            if (!CollectionUtils.isEmpty(billOrderSplits)){
                //该订单已经分账成功，无需分账
                return;
            }

            TxSplitAccountRequest txSplitAccountRequest = new TxSplitAccountRequest();
            txSplitAccountRequest.setInstitutionId(stringRedisTemplate.opsForValue().get("zj_pay_institution_id"));
            txSplitAccountRequest.setTxSn(GUIDGenerator.genGUID());
            txSplitAccountRequest.setPaymentTxSN(orderPay.getTradeSn());
            txSplitAccountRequest.setSourceTxTime(DateUtil.format(orderPay.getCallbackTime(),"yyyyMMdd"));
            txSplitAccountRequest.setRemainFundsProcess("2");

            TxSplitAccountRequest.SplitSellerInfo splitSellerInfo = new TxSplitAccountRequest.SplitSellerInfo();
            splitSellerInfo.setSplitTxSN(GUIDGenerator.genGUID());
            splitSellerInfo.setSplitUserID(order.getStoreId().toString());
            splitSellerInfo.setSplitAmount(String.valueOf(splitAmount.multiply(new BigDecimal(100)).intValue()));
            splitSellerInfo.setNote("订单号：" + order.getOrderSn() + "的分账");
            txSplitAccountRequest.getSplitSellerInfoList().add(splitSellerInfo);

            //保存分账记录
            BillOrderSplit billOrderSplit = new BillOrderSplit();
            billOrderSplit.setOrderSn(order.getOrderSn());
            billOrderSplit.setBillSn(billSn);
            billOrderSplit.setStoreId(order.getStoreId());
            billOrderSplit.setTxSn(txSplitAccountRequest.getTxSn());
            billOrderSplit.setSplitAmount(splitAmount);
            billOrderSplit.setCreateTime(new Date());
            billOrderSplitWriteMapper.insert(billOrderSplit);

            //分账
            Tx5031Response tx5031Response = ZjPayUtil.splitAccount(txSplitAccountRequest);
            if (!"2000".equals(tx5031Response.getCode())){
                //请求失败
                log.error("订单{}分账异常：{}，分账请求流水号{}",order.getOrderSn() ,tx5031Response.getMessage() ,txSplitAccountRequest.getTxSn());
                throw new MallException("分账异常：" + tx5031Response.getMessage());
            }
            String status = tx5031Response.getStatus();
            if ("30".equals(status)){
                log.error("订单{}分账失败：{}，分账请求流水号{}",order.getOrderSn() ,tx5031Response.getResponseMessage() ,txSplitAccountRequest.getTxSn());
                throw new MallException("分账失败：" + tx5031Response.getResponseMessage());
            }
        }

    }

    /**
     * 查看是否已全部分账，全部分账则把剩余可分账金额分给平台
     *
     * @param orderPay 订单支付信息
     * @param order 订单信息
     */
    @Transactional
    public void splitToPlatform(OrderPay orderPay, Order order) throws Exception {
        //查询此支付单号是否已经结算给平台
        BillOrderSplitExample billOrderSplitExample = new BillOrderSplitExample();
        billOrderSplitExample.setOrderSn("admin");
        billOrderSplitExample.setBillSn(orderPay.getPaySn());
        List<BillOrderSplit> billOrderSplits = billOrderSplitWriteMapper.listByExample(billOrderSplitExample);
        if (!CollectionUtils.isEmpty(billOrderSplits)){
            //已结算给平台
            return;
        }

        //查询支付单号下所有订单
        OrderExample orderExample = new OrderExample();
        orderExample.setPaySn(orderPay.getPaySn());
        orderExample.setOrderState(OrderConst.ORDER_STATE_40);
        List<Order> orderList = orderReadMapper.listByExample(orderExample);

        boolean splitToPlatform = false;//支付单号下所有订单是否已全部分账，可以结算给平台
        if (orderList.size() == 1){
            //此支付单号下只有当前分账的订单，可以结算给平台
            splitToPlatform = true;
        }else {
            List<Order> splitOrderList = new ArrayList<>(orderList.size());//除了当前订单外，可分账的订单列表，同时满足 1-支付方式为中金支付吗；2-入驻店铺
            for (Order o : orderList) {
                if (o.getOrderSn().equals(order.getOrderSn())) {
                    //过滤当前订单
                    continue;
                }
                if (!o.getPaymentCode().toLowerCase().contains(PayConst.METHOD_ZJ.toLowerCase())) {
                    //过滤非中金支付的订单
                    continue;
                }
                Store store = storeReadMapper.getByPrimaryKey(o.getStoreId());
                if (store.getIsOwnStore().equals(StoreConst.IS_OWN_STORE)){
                    //过滤自营店铺
                    continue;
                }
                splitOrderList.add(o);
            }
            if (CollectionUtils.isEmpty(splitOrderList)){
                //其他订单不需要分账，可以结算给平台
                splitToPlatform = true;
            }else {
                //遍历其他需要分账的订单，查询是否已结算完成,只要有一个订单未结算完成，则不可以结算给平台，直接跳出循环
                for (Order o : splitOrderList) {
                    BillOrderBindExample billOrderBindExample = new BillOrderBindExample();
                    billOrderBindExample.setOrderSn(o.getOrderSn());
                    List<BillOrderBind> billOrderBinds = billOrderBindWriteMapper.listByExample(billOrderBindExample);
                    if (CollectionUtils.isEmpty(billOrderBinds)) {
                        //还未生成结算单,不可结算给平台
                        splitToPlatform = false;
                        break;
                    }
                    //查询结算信息
                    BillExample billExample = new BillExample();
                    billExample.setBillSn(billOrderBinds.get(0).getBillSn());
                    List<Bill> billList = billWriteMapper.listByExample(billExample);
                    if (CollectionUtils.isEmpty(billList)){
                        //未生成结算单
                        splitToPlatform = false;
                        break;
                    }
                    if (!billList.get(0).getState().equals(BillConst.STATE_4)){
                        //结算单未完成
                        splitToPlatform = false;
                        break;
                    }
                    splitToPlatform = true;
                }
            }
        }

        if (splitToPlatform){//结算给平台
            //查询支付信息
            Tx5016Response tx5016Response = ZjPayUtil.queryOrder(orderPay.getTradeSn(), stringRedisTemplate.opsForValue().get("zj_pay_institution_id"));
            String availableSplitAmount = tx5016Response.getAvailableSplitAmount();//剩余可分账金额
            if (!"0".equals(availableSplitAmount)){
                //剩余金额不为0才结算
                TxSplitAccountRequest txSplitAccountRequest = new TxSplitAccountRequest();
                txSplitAccountRequest.setInstitutionId(stringRedisTemplate.opsForValue().get("zj_pay_institution_id"));
                txSplitAccountRequest.setTxSn(GUIDGenerator.genGUID());
                txSplitAccountRequest.setPaymentTxSN(orderPay.getTradeSn());
                txSplitAccountRequest.setSourceTxTime(DateUtil.format(orderPay.getCallbackTime(),"yyyyMMdd"));
                txSplitAccountRequest.setRemainFundsProcess("1");//无后续分账

                TxSplitAccountRequest.SplitSellerInfo splitSellerInfo = new TxSplitAccountRequest.SplitSellerInfo();
                splitSellerInfo.setSplitTxSN(GUIDGenerator.genGUID());
                splitSellerInfo.setSplitUserID(stringRedisTemplate.opsForValue().get("zj_pay_institution_id"));//分账用户是机构号
                splitSellerInfo.setSplitAccountNumber("0001");
                splitSellerInfo.setSplitAmount(availableSplitAmount);
                splitSellerInfo.setNote("支付单号：" + orderPay.getPaySn() + "结算给平台");
                txSplitAccountRequest.getSplitSellerInfoList().add(splitSellerInfo);

                //保存分账记录
                BillOrderSplit billOrderSplit = new BillOrderSplit();
                billOrderSplit.setOrderSn("admin");
                billOrderSplit.setBillSn(orderPay.getPaySn());
                billOrderSplit.setStoreId(0L);
                billOrderSplit.setTxSn(txSplitAccountRequest.getTxSn());
                billOrderSplit.setSplitAmount(new BigDecimal(availableSplitAmount).divide(new BigDecimal(100),2, RoundingMode.HALF_UP));
                billOrderSplit.setCreateTime(new Date());
                billOrderSplitWriteMapper.insert(billOrderSplit);

                //分账
                Tx5031Response tx5031Response = ZjPayUtil.splitAccount(txSplitAccountRequest);
                if (!"2000".equals(tx5031Response.getCode())){
                    //请求失败
                    log.error("支付单号{}结算给平台出现异常：{}，分账请求流水号{}",order.getPaySn() ,tx5031Response.getMessage() ,txSplitAccountRequest.getTxSn());
                    throw new MallException("资金结算到平台账户异常：" + tx5031Response.getMessage());
                }
                String status = tx5031Response.getStatus();
                if ("30".equals(status)){
                    log.error("支付单号{}结算给平台出现异常：{}，分账请求流水号{}",order.getPaySn() ,tx5031Response.getResponseMessage() ,txSplitAccountRequest.getTxSn());
                    throw new MallException("资金结算到平台账户失败：" + tx5031Response.getResponseMessage());
                }
            }
        }
    }
}