package com.yonge.cooleshow.biz.dal.service.impl;

import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.yonge.cooleshow.auth.api.entity.SysUser;
import com.yonge.cooleshow.biz.dal.dto.req.OrderPayReq;
import com.yonge.cooleshow.biz.dal.enums.ClientEnum;
import com.yonge.cooleshow.biz.dal.enums.OrderStatusEnum;
import com.yonge.cooleshow.biz.dal.service.*;
import com.yonge.cooleshow.biz.dal.vo.UserOrderDetailVo;
import com.yonge.cooleshow.biz.dal.vo.UserOrderVo;
import com.yonge.cooleshow.biz.dal.vo.res.OrderPayRes;
import com.yonge.cooleshow.common.entity.HttpResponseResult;
import com.yonge.cooleshow.common.enums.ContractTemplateTypeEnum;
import com.yonge.cooleshow.common.enums.SysUserType;
import com.yonge.cooleshow.common.service.IdGeneratorService;
import com.yonge.toolset.base.exception.BizException;
import com.yonge.toolset.base.result.BaseResult;
import com.yonge.toolset.base.util.StringUtil;
import com.yonge.toolset.payment.base.enums.OpenEnum;
import com.yonge.toolset.payment.base.enums.PayChannelEnum;
import com.yonge.toolset.payment.base.enums.PaymentClientEnum;
import com.yonge.toolset.payment.base.enums.TradeStatusEnum;
import com.yonge.toolset.payment.base.model.*;
import com.yonge.toolset.payment.base.model.callback.PaymentCallBack;
import com.yonge.toolset.payment.core.props.PaymentProperties;
import com.yonge.toolset.payment.core.service.PaymentClient;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.yonge.cooleshow.biz.dal.entity.UserOrderPayment;
import com.yonge.cooleshow.biz.dal.dao.UserOrderPaymentDao;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.*;
import java.util.concurrent.CompletableFuture;


@Service
public class UserOrderPaymentServiceImpl extends ServiceImpl<UserOrderPaymentDao, UserOrderPayment> implements UserOrderPaymentService {
    private final static Logger log = LoggerFactory.getLogger(UserOrderPaymentServiceImpl.class);

    @Autowired
    private UserOrderService userOrderService;
    @Autowired
    private IdGeneratorService idGeneratorService;
    @Autowired
    private PaymentProperties paymentProperties;
    @Autowired
    private PaymentClient paymentClient;
    /**
     * 服务对象
     */
    @Resource
    private SysUserContractRecordService sysUserContractRecordService;
    @Autowired
    private SysUserService userService;
    @Autowired
    private UserOrderRefundService orderRefundService;

    @Override
    public UserOrderPayment detailByOrderNo(String orderNo, PayChannelEnum payChannel, TradeStatusEnum status) {
        return baseMapper.selectOne(Wrappers.<UserOrderPayment>lambdaQuery().eq(UserOrderPayment::getOrderNo, orderNo).eq(UserOrderPayment::getPayChannel, payChannel).eq(UserOrderPayment::getStatus, status));
    }

    @Override
    public UserOrderPayment detailByTransNoOrPaymentNo(String transNo, String paymentNo) {
        return baseMapper.selectOne(Wrappers.<UserOrderPayment>lambdaQuery().and(wrapper -> wrapper.eq(UserOrderPayment::getTransNo, transNo).or().eq(UserOrderPayment::getPaymentNo, paymentNo)));
    }

    @Override
    public Boolean onlyClosePayment(String orderNo, String reason) {
        UserOrderPayment orderPayment = baseMapper.selectOne(Wrappers.<UserOrderPayment>lambdaQuery().eq(UserOrderPayment::getOrderNo, orderNo).eq(UserOrderPayment::getStatus, TradeStatusEnum.pending));
        if (null == orderPayment) {
            return true;
        }

        //更新付款单
        orderPayment.setStatus(TradeStatusEnum.close);
        orderPayment.setPayFailMsg(StringUtil.isEmpty(reason) ? "交易取消" : reason);
        orderPayment.setUpdateTime(new Date());

        updateById(orderPayment);
        return true;
    }

    @Override
    public Boolean closePaymentAndReqOpen(String orderNo, String reason) {
        UserOrderPayment orderPayment = baseMapper.selectOne(Wrappers.<UserOrderPayment>lambdaQuery().eq(UserOrderPayment::getOrderNo, orderNo).eq(UserOrderPayment::getStatus, TradeStatusEnum.pending));
        if (null == orderPayment) {
            return true;
        }
        //更新付款单
        orderPayment.setStatus(TradeStatusEnum.close);
        orderPayment.setPayFailMsg(StringUtil.isEmpty(reason) ? "交易取消" : reason);
        orderPayment.setUpdateTime(new Date());

        //发送支付关单请求
        if (!StringUtil.isEmpty(orderPayment.getTransNo()) || !StringUtil.isEmpty(orderPayment.getPaymentNo())) {
            ClosePayment param = new ClosePayment(orderPayment.getOpenType(), PaymentClientEnum.valueOf(orderPayment.getPaymentClient()), orderPayment.getPayChannel());
            param.setId(orderPayment.getTransNo());
            param.setPaymentNo(orderPayment.getPaymentNo());
            param.setReason(reason);

            BaseResult<ClosePayment> responseResult = paymentClient.closePayment(param);
            log.info("关单返回: {}", JSONObject.toJSONString(responseResult));
            if (!responseResult.getStatus()) {
                orderPayment.setCloseStatus(TradeStatusEnum.failed);
                orderPayment.setCloseFailMsg(responseResult.getMsg());
            } else {
                if (responseResult.getData().getHasNotify()) {
                    orderPayment.setCloseStatus(TradeStatusEnum.pending);
                } else {
                    orderPayment.setCloseStatus(TradeStatusEnum.succeeded);
                }
            }
        }
        updateById(orderPayment);
        return true;
    }

    @Override
    public HttpResponseResult<OrderPayRes> createOrderPayment(OrderPayReq payReq, UserOrderVo detail) {
        OrderPayRes orderPayRes = new OrderPayRes();
        //支付订单号生成
        Long paymentNo = idGeneratorService.generatorId("paymentNo");

        //构建统一下单请求
        OpenEnum openType = OpenEnum.valueOf(paymentProperties.getOpenType());
        Payment payment = new Payment(openType, payReq.getPaymentClient(), payReq.getPayChannel());
        payment.setPaymentNo(paymentNo.toString());
        payment.setOrderNo(payReq.getOrderNo());
        payment.setOrderTitle(detail.getOrderName());
        payment.setOrderDesc(detail.getOrderDesc());

        //封装订单详情
        List<OrderDetil> orderDetils = new ArrayList<>();
        for (UserOrderDetailVo orderDetailVo : detail.getOrderDetailList()) {
            orderDetils.add(new OrderDetil(orderDetailVo.getId().toString(), orderDetailVo.getGoodName(), 1, orderDetailVo.getActualPrice().toString()));
        }
        payment.setOrderDetils(orderDetils);
        payment.setPayAmt(detail.getActualPrice().setScale(2, RoundingMode.HALF_UP).toString());
        payment.setDescription(payReq.getReason());

        if (OpenEnum.ADAPAY.equals(openType)) {
            //创建分账对象
            DivMember div_member = new DivMember();
            div_member.setMember_id("0");
            div_member.setAmount(detail.getActualPrice().setScale(2, RoundingMode.HALF_UP).toString());
            div_member.setFee_flag("Y");
            List<DivMember> div_members = Arrays.asList(div_member);
            payment.setDivMembers(div_members);

            //前端设备信息
            DeviceInfo deviceInfo = new DeviceInfo();
            deviceInfo.setDevice_ip(payReq.getIpAddress());
            payment.setDeviceInfo(deviceInfo);

            if (PayChannelEnum.wx_lite.equals(payReq.getPayChannel())) {
                Map<String, Object> expend = new HashMap<>();
                expend.put("open_id", payReq.getOpenId());
                payment.setExpend(expend);
            }
        }
        closePaymentAndReqOpen(payment.getOrderNo(), "用户发起了新的付款请求");

        //付款请求
        BaseResult<Payment> paymentResBaseResult = paymentClient.executePayment(payment);
        log.info("付款返回: {}", JSONObject.toJSONString(paymentResBaseResult));
        if (paymentResBaseResult.getStatus()) {
            orderPayRes.setPay_amt(detail.getActualPrice().setScale(2, RoundingMode.HALF_UP).toString());
            String pay_info = paymentResBaseResult.getData().getPayInfo();
            orderPayRes.setPay_info(pay_info);
            orderPayRes.setPayChannel(payReq.getPayChannel());
            orderPayRes.setPaymentNo(payment.getPaymentNo());

            //入订单付款表
            insertOrderPayment(paymentResBaseResult.getData());
            //修改订单状态
            userOrderService.updateStatusByOrderNo(payReq.getOrderNo(), OrderStatusEnum.PAYING.getCode());
            return HttpResponseResult.succeed(orderPayRes);
        } else {
            //付款失败
            throw new BizException(paymentResBaseResult.getMsg());
        }
    }


    @Override
    @Transactional(rollbackFor = Exception.class)
    public void executePaymentCallBack(PaymentCallBack data) {
        UserOrderVo userOrderVo = userOrderService.getUserOrderByPaymentNoOrTransNo(data.getPaymentNo(), data.getId());
        if (OrderStatusEnum.WAIT_PAY.equals(userOrderVo.getStatus()) || OrderStatusEnum.PAYING.equals(userOrderVo.getStatus())) {
            if (TradeStatusEnum.succeeded.equals(data.getStatus())) {//支付成功
                paymentSucceededHandle(data);
            } else if (TradeStatusEnum.failed.equals(data.getStatus())) {//支付失败
                paymentFailedHandle(data, data.getErrMsg());
            }
        } else {
            //原路退还
            orderRefundService.orderFailRefund(data, userOrderVo.getOrderNo(), "订单已超时，金额原路退回");
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void closePaymentCallBack(PaymentCallBack data) {
        UserOrderVo userOrderVo = userOrderService.getUserOrderByPaymentNoOrTransNo(data.getPaymentNo(), data.getId());
        if (OrderStatusEnum.WAIT_PAY.equals(userOrderVo.getStatus()) || OrderStatusEnum.PAYING.equals(userOrderVo.getStatus())) {
            if (TradeStatusEnum.succeeded.equals(data.getStatus())) {//关单成功
                paymentCloseSucceededHandle(data);
            } else if (TradeStatusEnum.failed.equals(data.getStatus())) {//关单失败
                paymentCloseFailedHandle(data);
            }
        }
    }


    /***
     * 入订单付款表
     * @author liweifan
     * @param: paymentRes
     * @param: payReq
     * @updateTime 2022/5/11 18:47
     * @return: com.yonge.cooleshow.biz.dal.entity.UserOrderPayment
     */
    private UserOrderPayment insertOrderPayment(Payment payment) {
        UserOrderPayment orderPayment = new UserOrderPayment();
        orderPayment.setOpenType(payment.getOpenType());
        orderPayment.setPaymentClient(payment.getPaymentClient().getCode());
        orderPayment.setPayChannel(payment.getPayChannel());
        orderPayment.setOrderNo(payment.getOrderNo());
        orderPayment.setPaymentNo(payment.getPaymentNo());
        orderPayment.setTransNo(payment.getId());
        orderPayment.setPayAmt(new BigDecimal(payment.getPayAmt()).setScale(2, RoundingMode.HALF_UP));
        orderPayment.setPayInfo(payment.getPayInfo());
        orderPayment.setStatus(TradeStatusEnum.pending);

        //查询订单下支付中的付款单，取消支付
        closePaymentAndReqOpen(payment.getOrderNo(), "用户发起了新的付款请求");
        //保存新的付款单
        save(orderPayment);
        return orderPayment;
    }

    /***
     * 处理回调-支付成功
     * @author liweifan
     * @param: data
     * @updateTime 2022/4/27 15:09
     */
    private void paymentSucceededHandle(PaymentCallBack data) {
        UserOrderPayment orderPayment = detailByTransNoOrPaymentNo(data.getId(), data.getPaymentNo());
        if (null == orderPayment) {
            log.error("支付回调，付款单未找到。 req is {}", JSONObject.toJSONString(data));
            return;
        }
        //订单完成
        UserOrderVo detail = userOrderService.detail(orderPayment.getOrderNo(), null);
        if (null == detail) {
            log.error("支付回调，订单未找到。 req is {}", JSONObject.toJSONString(data));
            return;
        }
        if (detail.getStatus().equals(OrderStatusEnum.WAIT_PAY) || detail.getStatus().equals(OrderStatusEnum.PAYING)) {
            userOrderService.orderSuccess(detail, data);
        }

        setContractRecord(orderPayment.getPaymentClient(), detail.getUserId());
    }

    @Override
    public void setContractRecord(String type, Long userId) {
        //处理支付完成签署协议
        SysUserType userType = SysUserType.STUDENT;
        if (ClientEnum.TEACHER.getCode().equals(type)) {
            userType = SysUserType.TEACHER;
        }
        Boolean aBoolean = sysUserContractRecordService.checkContractSign(userId, userType, ContractTemplateTypeEnum.BUY_ORDER);
        if (!aBoolean) {
            SysUser userById = userService.findUserById(userId);
            SysUserType finalUserType = userType;
            CompletableFuture.runAsync(() -> sysUserContractRecordService.sign(ContractTemplateTypeEnum.BUY_ORDER, finalUserType, userById));
        }
    }


    /***
     * 处理回调-支付失败
     * @author liweifan
     * @param: data
     * @updateTime 2022/4/27 15:09
     */
    private void paymentFailedHandle(PaymentCallBack data, String errMsg) {
        UserOrderPayment userOrderPayment = detailByTransNoOrPaymentNo(data.getId(), data.getPaymentNo());
        if (null == userOrderPayment) {
            log.error("支付回调，付款单未找到。 req is {}", JSONObject.toJSONString(data));
            return;
        }
        UserOrderVo detail = userOrderService.detail(userOrderPayment.getOrderNo(), null);
        if (null == detail) {
            log.error("支付回调，订单未找到。 req is {}", JSONObject.toJSONString(data));
            return;
        }
        if (detail.getStatus().equals(OrderStatusEnum.WAIT_PAY) || detail.getStatus().equals(OrderStatusEnum.PAYING)) {
            userOrderService.doOrderCancel(detail, OrderStatusEnum.FAIL, "支付回调失败:" + errMsg);
            //关闭付款单
            closePaymentAndReqOpen(detail.getOrderNo(), "支付回调失败:" + errMsg);
        }
    }

    /***
     * 处理回调-关单成功
     * @author liweifan
     * @param: data
     * @updateTime 2022/4/27 15:09
     */
    private void paymentCloseSucceededHandle(PaymentCallBack data) {
        UserOrderPayment orderPayment = detailByTransNoOrPaymentNo(data.getId(), data.getPaymentNo());
        if (null == orderPayment) {
            return;
        }
        orderPayment.setCloseStatus(TradeStatusEnum.succeeded);
        orderPayment.setUpdateTime(new Date());
        updateById(orderPayment);

        //若当前关单回调是当前订单付款单，则可以关单
        UserOrderVo detail = userOrderService.getUserOrderByPaymentNoOrTransNo(data.getPaymentNo(), data.getId());
        if (null != detail && OrderStatusEnum.PAYING.equals(detail.getStatus())
                && orderPayment.getPaymentNo().equals(detail.getPaymentNo())) {
            //关闭订单
            userOrderService.doOrderCancel(detail, OrderStatusEnum.CLOSE, "订单取消");
        }
    }

    /***
     * 处理回调-关单失败
     * @author liweifan
     * @param: data
     * @updateTime 2022/4/27 15:09
     */
    private void paymentCloseFailedHandle(PaymentCallBack data) {
        UserOrderPayment orderPayment = detailByTransNoOrPaymentNo(data.getId(), data.getPaymentNo());
        if (null == orderPayment) {
            return;
        }
        orderPayment.setCloseStatus(TradeStatusEnum.failed);
        orderPayment.setCloseFailMsg("支付关单回调失败-" + data.getErrMsg());
        orderPayment.setUpdateTime(new Date());
        updateById(orderPayment);
    }


}