package com.iformall.service.impl;

import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.iformall.common.ErrorCode;
import com.iformall.common.Result;
import com.iformall.common.ResultData;
import com.iformall.domain.dto.WxSharingOrderDto;
import com.iformall.domain.po.*;
import com.iformall.domain.vo.WxCardSpendVo;
import com.iformall.enums.*;
import com.iformall.exception.MallinkException;
import com.iformall.mapper.*;
import com.iformall.service.ExcelService;
import com.iformall.service.WxCardSpendService;
import com.iformall.service.WxProfitSharingOrderService;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.iformall.common.IdWorker;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.*;

import java.util.*;

@Service
public class WxCardSpendServiceImpl implements WxCardSpendService {

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

	@Autowired
    WxCardSpendMapper wxCardSpendMapper;

	@Autowired
    WxCardInfoMapper wxCardInfoMapper;

	@Autowired
    WxCouponMapper wxCouponMapper;

	@Autowired
    WxMerchantMapper wxMerchantMapper;

	@Autowired
    WxMerchantSubsidyMapper wxMerchantSubsidyMapper;

	@Autowired
    WxCouponMerchantMapper wxCouponMerchantMapper;

	@Autowired
    WxPayAccountMapper payAccountMapper;

	@Autowired
    WxOrderMapper wxOrderMapper;

	@Autowired
    WxCouponOrderMapper wxCouponOrderMapper;

	@Autowired
    WxProfitSharingOrderService profitSharingOrderService;

    @Autowired
    ExcelService excelService;

	@Override
    public WxCouponMerchant checkMerchantInCoupon(WxCardInfo cardInfo, Long merchantId) {
        WxCouponMerchant couponMerchant = null;
        WxCouponMerchant couponMerchantQ = new WxCouponMerchant();
        couponMerchantQ.setTenantId(cardInfo.getTenantId());
        couponMerchantQ.setProductId(cardInfo.getCouponId());
        couponMerchantQ.setMerchantId(merchantId);
        couponMerchantQ.setStatus(EnumCouponMerchantStatus.COUPON_MERCHANT_STATUS_VALID.getCode());
        try {
            couponMerchant = wxCouponMerchantMapper.selectOne(couponMerchantQ);
        } catch (Exception e) {
            logger.error("couponmerchant not found: " + cardInfo.getCouponId());
        }
        return couponMerchant;
    }

	@Override
    @Transactional(propagation = Propagation.REQUIRED, readOnly = false, rollbackFor = {Exception.class})
    public ResultData createCardSpend(WxCardSpend record, WxOrder order, WxCouponMerchant couponMerchant) {
	    Date curDate = new Date();
	    final IdWorker idWorker = IdWorker.get();
	    // 1. get card info
        WxCardInfo cardInfo = wxCardInfoMapper.selectByPrimaryKey(record.getCardId());
        if (cardInfo == null) {
            logger.error("card not found: " + record.getCardId());
            return new ResultData(ErrorCode.CARD_IS_NOT_FOUND);
        }
        // 2. coupon
        Integer subsidyRate = 0;
        WxCoupon coupon = wxCouponMapper.selectByPrimaryKey(cardInfo.getCouponId());
        if (coupon == null) {
            logger.error("card's coupon not found: " + cardInfo.getCouponId());
            return new ResultData(ErrorCode.CARD_IS_NOT_FOUND);
        }
        if (coupon.getSubsidyNum() == null) {
            logger.error("card's coupon not found: " + cardInfo.getCouponId());
            return new ResultData(ErrorCode.SYS_PARAMETER_ERROR.getCode(), "券补贴额未填写");
        }

        // 3. get pay account
        WxPayAccount payAccount = null;
        WxPayAccount payAccountQ = new WxPayAccount();
        payAccountQ.setTenantId(record.getTenantId());
        try {
            payAccount = payAccountMapper.selectOne(payAccountQ);
        } catch (Exception e) {
            logger.error("获取payAccount error: " + record.getTenantId());
            return new ResultData(ErrorCode.DB_FAIL.getCode(), "获取payAccount失败");
        }

        // 4.  扣减计算
        Double paymentD = 1.0 * cardInfo.getSaleAmount() * record.getDeductionAmount() / cardInfo.getAmount();
        Integer payment = paymentD.intValue();
        Double dChargeFee = Math.ceil(payment * 1.0D * payAccount.getRate() / 10000);
        Integer real_payment = payment - dChargeFee.intValue();
        Integer remain_real_pament = cardInfo.getRemainingShareFeeAmount() - real_payment;

        Integer remaingAmount = cardInfo.getRemainingAmount()-record.getDeductionAmount();
        if(remaingAmount.equals(0)) {
            // 最后一次支付时
            real_payment = Math.min(real_payment, cardInfo.getRemainingShareFeeAmount());
            remain_real_pament = cardInfo.getRemainingShareFeeAmount() - real_payment;
        }

        // 5 insert card_spend
        record.setId(idWorker.nextId());
        record.setPayment(payment);
        record.setRealPayment(real_payment);
        record.setCardBeforeAmount(cardInfo.getRemainingAmount());
        record.setCardRemainAmount(remaingAmount);
        record.setCardBeforeRealAmount(cardInfo.getRemainingShareFeeAmount());
        record.setCardRemainRealAmount(remain_real_pament);
        record.setPayStatus(EnumCardSpendStatus.NOT_PAY.getCode());
        record.setCreateDate(curDate);
        record.setUpdateDate(curDate);
        try {
            wxCardSpendMapper.insertSelective(record);
        } catch (Exception e) {
            logger.error("card spend insert error");
            return new ResultData(ErrorCode.DB_FAIL.getCode(), "card spend 插入出错！");
        }

        // 6. update order status
        WxOrder orderUpdate = new WxOrder();
        orderUpdate.setId(order.getId());
        orderUpdate.setPaymentTime(curDate);
        orderUpdate.setOrderStatus(EnumOrderStatus.ORDER_STATUS_PAYMENT_SUCCESS.getCode());
        orderUpdate.setUpdateDate(curDate);
        try {
            wxOrderMapper.updateByPrimaryKeySelective(orderUpdate);
        } catch (Exception e) {
            logger.error("order update error");
            return new ResultData(ErrorCode.DB_FAIL.getCode(), "order update 出错！");
        }

        // 7. update card info
        cardInfo.setRemainingAmount(remaingAmount);
        cardInfo.setRemainingShareFeeAmount(remain_real_pament);
        cardInfo.setUpdateDate(curDate);
        try {
            wxCardInfoMapper.updateByPrimaryKeySelective(cardInfo);
        } catch (Exception e) {
            logger.error("card info update error");
            return new ResultData(ErrorCode.DB_FAIL.getCode(), "card info 更新出错！");
        }
        // 7.1 update coupon card status
        if(cardInfo.getRemainingAmount().equals(0)) {
            if (cardFinished(order, curDate, cardInfo)) {
                return new ResultData(ErrorCode.DB_FAIL.getCode(), "couponOrder status 更新出错！");
            }
        }

        // 8. 补贴
        // 补贴额 = 商城补贴总额/卡券面额 * 抵扣额(本次支付额)
        Double subsidyFeeD = 1.0 * record.getDeductionAmount() * coupon.getSubsidyNum() / cardInfo.getAmount();
        Integer subsidyFee =  subsidyFeeD.intValue();
        logger.info("补贴额: " + subsidyFee);
        if(subsidyFee > 0) {
            // 有补贴时入库
            Double dSubChargeFee = Math.ceil(subsidyFee * 1.0D * payAccount.getRate() / 10000);
            Integer realSubsidyFee = subsidyFee - dSubChargeFee.intValue();
            WxMerchantSubsidy merchantSubsidy = new WxMerchantSubsidy();
            merchantSubsidy.setId(idWorker.nextId());
            merchantSubsidy.setTenantId(record.getTenantId());
            merchantSubsidy.setOrderId(order.getId());
            merchantSubsidy.setOrderType(EnumOrderType.PREPAIDCARD.getCode());
            merchantSubsidy.setMerchantId(record.getMerchantId());
            merchantSubsidy.setCouponOrderId(cardInfo.getId());
            merchantSubsidy.setCouponType(EnumCouponType.CARD_MULTIMCH.getCode());
            merchantSubsidy.setOrderPayment(record.getDeductionAmount());
            merchantSubsidy.setReceiverPayment(payment);
            merchantSubsidy.setRealPayment(real_payment);
            merchantSubsidy.setSubsidy(subsidyFee);
            merchantSubsidy.setRealSubsidy(realSubsidyFee);
            if (coupon.getSubsidyType().equals(EnumCouponSubsidyType.WECHAT_COUPON.getCode())) {
                // 立减领券
                merchantSubsidy.setStatus(EnumMerchantSubsidyStatus.AUTO_SUBSIDIED.getCode());
            } else if (coupon.getSubsidyType().equals(EnumCouponSubsidyType.WECHAT_MCHPAY.getCode()))  {
                // 转账倒现金
            } else {
                // 未补贴
                merchantSubsidy.setStatus(EnumMerchantSubsidyStatus.NOT_SUBSIDY.getCode());
            }

            merchantSubsidy.setCreateDate(curDate);
            merchantSubsidy.setUpdateDate(curDate);
            try {
                wxMerchantSubsidyMapper.insertSelective(merchantSubsidy);
            } catch (Exception e) {
                logger.error("merchantSubsidy info insert error");
                return new ResultData(ErrorCode.DB_FAIL.getCode(), "merchantSubsidy info 插入出错！");
            }
        }

        // 9. 分账
        try {
            shareForCardPay(record.getTenantId(), record.getCardId(), record.getOrderId(), record.getId());
        } catch (MallinkException e) {
            logger.error(e.getMessage());
            if(ErrorCode.PROFIT_SHARING_NUM_UP_LIMIT.getCode() == e.getErrorCode()) {
                // 分账已到20次限制，后面只记录，无法分账
                logger.error("分账已到20次限制，后面只记录，无法分账");
            } else {
                throw new MallinkException(e.getErrorCode(), e.getMessage());
            }
        }

        //10、消费后修改卡转赠状态
        WxCardInfo wxCardInfo = new WxCardInfo();
        wxCardInfo.setId(record.getCardId());
        wxCardInfo.setSupportTransfer(EnumCouponTransfer.NO.getCode());
        if (cardInfo.getSupportTransfer().equals(EnumCouponTransfer.YES.getCode())) {
            wxCardInfoMapper.updateByPrimaryKeySelective(wxCardInfo);
        }
        return new ResultData(record);
    }

    private boolean cardFinished(WxOrder order, Date curDate, WxCardInfo cardInfo) {
        WxCouponOrder couponOrder = new WxCouponOrder();
        couponOrder.setId(cardInfo.getId());
        couponOrder.setCouponId(cardInfo.getCouponId());
        couponOrder.setOrderId(order.getId());
        couponOrder.setCouponOrderStatus(EnumCouponOrderStatus.CARD_COMPLETE.getCode());
        couponOrder.setUpdateDate(curDate);
        try {
            wxCouponOrderMapper.updateByPrimaryKeySelective(couponOrder);
        } catch (Exception e) {
            logger.error("couponOrder status update error");
            return true;
        }
        return false;
    }


    @Override
    public PageInfo<WxCardSpendVo> listAsPage(WxCardSpendVo record, Integer pageIndex, Integer pageSize) {
        return PageHelper.startPage(pageIndex, pageSize).doSelectPageInfo(() -> wxCardSpendMapper.findCardSpendVoList(record));
    }

    @Override
    public List<Map<String, Object>> sumCardSpendForMerchant(WxCardSpendVo record) {
	    return wxCardSpendMapper.sumCardSpendVoForMerchant(record);
    }

    @Override
    public List<Map<String, Object>> sumCardSpendForOwner(WxCardSpendVo record) {
        return wxCardSpendMapper.sumCardSpendVoForOwner(record);
    }

    @Override
    public WxCardSpend getById(Long id) {
        return wxCardSpendMapper.selectByPrimaryKey(id);
    }

    @Override
    public void saveOrUpdate(WxCardSpend record) {
        if (record.getId() == null) {
            //record.setId(UUID.randomUUID().toString().replaceAll("-", ""));
            final IdWorker idWorker = IdWorker.get();
        	record.setId(idWorker.nextId());
            wxCardSpendMapper.insertSelective(record);
        } else {
            wxCardSpendMapper.updateByPrimaryKeySelective(record);
        }
    }

    @Override
    public void update(WxCardSpend record) {
        // 1. 检查符合条件的卡消费记录是否已支付
        List<WxCardSpend> cardSpends = wxCardSpendMapper.findList(record);
        if (cardSpends.stream().anyMatch((cs->(!cs.getPayStatus().equals(EnumCardSpendStatus.NOT_PAY.getCode()))))){
            throw new MallinkException(ErrorCode.CARD_SPEND_IS_PAID);
        }
        // 2. 更新支付状态
        record.setPayStatus(EnumCardSpendStatus.MANUAL_PAY.getCode());
        record.setUpdateDate(new Date());
        wxCardSpendMapper.updateByCond(record);
    }

    @Override
    public int finishCard(String tenantId, Long cardId) {
        // admin用户手动帮已用卡消费用户退卡, 此卡需要分账完结, 买卡的钱进商场账户
        WxCardInfo cardInfo = null;
        try {
            cardInfo = wxCardInfoMapper.selectByPrimaryKey(cardId);
        } catch (Exception e) {
            logger.error("微信分账: " + e.getMessage());
            throw new MallinkException(ErrorCode.DB_FAIL.getCode(), e.getMessage());
        }
        if(cardInfo != null) {
            if (cardInfo.getRemainingAmount().equals(cardInfo.getAmount())){
                logger.error("请用订单退款接口: ");
                throw new MallinkException(ErrorCode.CARD_IS_NOT_COST);
            } else if(cardInfo.getRemainingAmount() > 0){
                // 卡已消费， 余额不为0
                int remainShareFeeAmount = cardInfo.getRemainingShareFeeAmount();
                try {
                    WxCouponOrder couponOrder = wxCouponOrderMapper.selectByPrimaryKey(cardInfo.getId());
                    // 更新 coupon order状态
                    couponOrder.setCouponOrderStatus(EnumCouponOrderStatus.CARD_RETURNED.getCode());
                    couponOrder.setUpdateDate(new Date());
                    wxCouponOrderMapper.updateByPrimaryKeySelective(couponOrder);
                    // 卡余额
                    WxCardInfo cardInfo1 = new WxCardInfo();
                    cardInfo1.setId(cardInfo.getId());
                    cardInfo1.setRemainingAmount(0);
                    cardInfo1.setUpdateDate(new Date());
                    wxCardInfoMapper.updateByPrimaryKeySelective(cardInfo1);

                    // 分账完结
                    WxSharingOrderDto shareOrder = new WxSharingOrderDto();
                    shareOrder.setcUserId(couponOrder.getOwnerId());
                    shareOrder.setTenantId(tenantId);
                    shareOrder.setMerchantId(0L); // 卡完结，默认给0
                    shareOrder.setTransactionId(cardInfo.getTransactionId());
                    cardShareFinished(cardInfo, shareOrder);
                } catch (MallinkException e) {
                    logger.error("微信分账: " + e.getMessage());
                    throw new MallinkException(e.getErrorCode(), e.getMessage());
                }
                return remainShareFeeAmount;
            }
        }
        return 0;
    }

    @Override
    public void deleteById(Long id) {
        wxCardSpendMapper.deleteByPrimaryKey(id);
    }


    @Override
    @Transactional(propagation = Propagation.REQUIRED, readOnly = false, rollbackFor = {Exception.class})
    public void shareForCardPay(String tenantId, Long cardId, Long orderId, Long cardSpendId) {
	    // 微信已分账检查次数
        int psNum = 0;
        try {
            WxCardSpend cardSpendQ = new WxCardSpend();
            cardSpendQ.setTenantId(tenantId);
            cardSpendQ.setCardId(cardId);
            psNum = wxCardSpendMapper.selectCount(cardSpendQ);
            if (psNum > 20) {
                logger.error("微信分账已超过限制次数-20次");
                throw new MallinkException(ErrorCode.PROFIT_SHARING_NUM_UP_LIMIT);
            }
        } catch (MallinkException e) {
            logger.error(e.getMessage());
            throw new MallinkException(e.getErrorCode(), e.getMessage());
        } catch (Exception e) {
            logger.error(e.getMessage());
            throw new MallinkException(ErrorCode.DB_FAIL);
        }
        // 微信分账
        try {
            WxCardSpend cardSpend = new WxCardSpend();
            cardSpend.setTenantId(tenantId);
            cardSpend.setId(cardSpendId);
            cardSpend.setCardId(cardId);
            cardSpend.setOrderId(orderId);
            cardSpend = wxCardSpendMapper.selectOne(cardSpend);
            if (cardSpend != null && cardSpend.getRealPayment() <= 0) {
                logger.error("微信分账金额为0, 无法再分");
                return;
            }
            WxCardInfo cardInfo = wxCardInfoMapper.selectByPrimaryKey(cardSpend.getCardId());
            WxOrder wxOrder = wxOrderMapper.selectByPrimaryKey(orderId);
            WxMerchant merchant = wxMerchantMapper.selectByPrimaryKey(cardSpend.getMerchantId());

            if (cardSpend != null && cardInfo != null && merchant != null) {
                // 分账
                WxSharingOrderDto shareOrder = new WxSharingOrderDto();
                shareOrder.setcUserId(cardSpend.getOwnerId());
                shareOrder.setMerchantId(cardSpend.getMerchantId());
                shareOrder.setType(EnumProfitSharingOrderType.PROFIT_SHARING_MULTI.getCode());
                shareOrder.setPayAmount(cardSpend.getPayment());
                shareOrder.setOrderId(cardSpendId);
                shareOrder.setPayTimeStart(cardSpend.getCreateDate());
                shareOrder.setPayTimeEnd(cardSpend.getUpdateDate());
                shareOrder.setTenantId(tenantId);
                shareOrder.setTransactionId(cardInfo.getTransactionId());
                shareOrder.setShareAmount(cardSpend.getRealPayment());
                ResultData resultData = profitSharingOrderService.createSharingOrder(shareOrder);
                if(resultData.code != Result.SUCCESS) {
                    // 分账异常
                    logger.error(resultData.message);
                    throw new MallinkException(resultData.code, resultData.message);
                }

                // 卡消费记录状态更新
                {
                    WxCardSpend updateRecord = new WxCardSpend();
                    updateRecord.setId(cardSpend.getId());
                    updateRecord.setPayStatus(EnumCardSpendStatus.PS_SHARED.getCode());
                    updateRecord.setUpdateDate(new Date());
                    wxCardSpendMapper.updateByPrimaryKeySelective(updateRecord);
                }


                if(cardSpend.getCardRemainAmount().equals(0)
                        || psNum == 20) {
                    // 卡余额为0，或者卡已分账20次，发起分账完结
                    if (cardSpend.getCardRemainRealAmount() > 0) {
                        // 卡已被抵扣，但是分账还有余额
                        shareOrder.setMerchantId(0L); // 卡完结，填0
                        cardShareFinished(cardInfo, shareOrder);
                    }
                }
            } else {
                logger.error("微信分账: 未找到payorder or merchant at orderid-" + orderId);
            }
        } catch (MallinkException e) {
            logger.error("微信分账: " + e.getMessage());
            throw new MallinkException(e.getErrorCode(), e.getMessage());
        }
    }



    /**
     * 卡分账完结
     * @param cardInfo
     * @param shareOrder
     */
    private void cardShareFinished(WxCardInfo cardInfo, WxSharingOrderDto shareOrder) {
        Date curDate = new Date();
        // card spend 添加一条 记录]
        final IdWorker idWorker = IdWorker.get();
        WxCardSpend cardSpend = new WxCardSpend();
        cardSpend.setTenantId(cardInfo.getTenantId());
        cardSpend.setId(idWorker.nextId());
        cardSpend.setCardId(cardInfo.getId());
        cardSpend.setOrderId(null);
        cardSpend.setDeductionAmount(0);
        cardSpend.setPayment(cardInfo.getRemainingShareFeeAmount());
        cardSpend.setRealPayment(cardInfo.getRemainingShareFeeAmount());
        cardSpend.setCardBeforeAmount(cardInfo.getRemainingAmount());
        cardSpend.setCardRemainAmount(0);
        cardSpend.setCardBeforeRealAmount(cardInfo.getRemainingShareFeeAmount());
        cardSpend.setCardRemainRealAmount(cardInfo.getRemainingShareFeeAmount());
        cardSpend.setCreateDate(curDate);
        cardSpend.setUpdateDate(curDate);
        try {
            wxCardSpendMapper.insertSelective(cardSpend);
        } catch (Exception e) {
            logger.error("card spend insert error");
            return;
        }
        // card info 更新 分账 余额
        WxCardInfo cardInfo1 = new WxCardInfo();
        cardInfo1.setId(cardInfo.getId());
        cardInfo1.setRemainingShareFeeAmount(0);
        cardInfo1.setUpdateDate(curDate);
        try {
            wxCardInfoMapper.updateByPrimaryKeySelective(cardInfo1);
        } catch (Exception e) {
            logger.error("card info remaining error");
            return;
        }

        // 分账完结
        shareOrder.setOrderId(cardSpend.getId());
        shareOrder.setPayAmount(0);
        shareOrder.setOrderId(cardSpend.getId());
        shareOrder.setShareAmount(cardInfo.getRemainingShareFeeAmount());
        shareOrder.setType(EnumProfitSharingOrderType.PROFIT_SHARING_MULTI_FINISH.getCode());
        profitSharingOrderService.finishSharingOrder(shareOrder);
    }

    @Override
    public void exportData(WxCardSpendVo wxCardSpendVo, HttpServletRequest request, HttpServletResponse response) {
        List<WxCardSpendVo> list = wxCardSpendMapper.findCardSpendVoList(wxCardSpendVo);
        excelService.exportExcel(list,null,"消费记录",WxCardSpendVo.class,"消费记录.xls",response);
    }

}
