package com.izouma.nineth.service;

import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUtil;
import com.huifu.adapay.model.Payment;
import com.izouma.nineth.domain.GiftOrder;
import com.izouma.nineth.domain.Order;
import com.izouma.nineth.domain.PaymentInfo;
import com.izouma.nineth.dto.PageQuery;
import com.izouma.nineth.dto.PageWrapDTO;
import com.izouma.nineth.dto.pay.PayResult;
import com.izouma.nineth.enums.FundChangeType;
import com.izouma.nineth.enums.OrderStatus;
import com.izouma.nineth.enums.PayStatus;
import com.izouma.nineth.exception.BusinessException;
import com.izouma.nineth.repo.GiftOrderRepo;
import com.izouma.nineth.repo.OrderRepo;
import com.izouma.nineth.repo.PaymentInfoRepo;
import com.izouma.nineth.service.pay.UniformPayConstants;
import com.izouma.nineth.utils.JpaUtils;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.hibernate.transform.Transformers;
import org.springframework.data.domain.Page;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.stereotype.Service;

import javax.persistence.EntityManager;
import javax.persistence.Query;
import javax.persistence.TypedQuery;
import javax.persistence.criteria.*;
import javax.transaction.Transactional;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.time.ZoneOffset;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;

@Service
@AllArgsConstructor
@Slf4j
public class PaymentInfoService {
    
    private PaymentInfoRepo paymentInfoRepo;

    private OrderRepo orderRepo;

    private GiftOrderRepo giftOrderRepo;

    private UserFundChangeLogService userFundChangeLogService;

    private EntityManager entityManager;

    private SandpayService sandpayService;

    private LianlianPayService lianlianPayService;

    
    public PageWrapDTO<PaymentInfo> all(PageQuery pageQuery) {
        String orderStatus = Objects.toString(pageQuery.getQuery().remove("orderStatus"),"");
        Specification specification = JpaUtils.toSpecification(pageQuery, PaymentInfo.class);
        if(StringUtils.isNotEmpty(orderStatus)){
            //筛选状态
            specification = specification.and((root, query, criteriaBuilder) -> {
                Subquery subquery = query.subquery(Order.class);
                Root<Order> subroot = subquery.from(Order.class);
                subquery.select(subroot.get("id"));
                Predicate subPredicate = criteriaBuilder.conjunction();
                subPredicate.getExpressions().add(criteriaBuilder.equal(root.get("orderId"), subroot.get("id")));
                subPredicate.getExpressions().add(criteriaBuilder.equal(subroot.get("status"), OrderStatus.valueOf(orderStatus)));
                subquery.where(subPredicate);
                return criteriaBuilder.exists(subquery);
            });
        }

        Page<PaymentInfo> page =  paymentInfoRepo.findAll(specification,
                JpaUtils.toPageRequest(pageQuery));
        //检查订单状态
        page.forEach(item -> {
            if(item.getOrderId() == null){
                return;
            }

            Order order = orderRepo.findById(item.getOrderId()).orElse(null);
            if(order != null){
                item.setOrderStatus(order.getStatus());
            }
        });

        PageWrapDTO pageWrapDTO = new PageWrapDTO(page.toList(), page.getPageable(), page.getTotalElements());
        BigDecimal totalAmount = sumPayAmt(specification);
        pageWrapDTO.setTotalAmount(totalAmount);
        return pageWrapDTO;
    }


    /**
     * 更新支付通知
     * @return
     */
    @Transactional(Transactional.TxType.REQUIRES_NEW)
    public PaymentInfo notifyPaymentInfo(String orderNo, String transactionId, String thirdPayNo,
                                         long eventTime, String payTime){
        if(StringUtils.isEmpty(orderNo)){
            log.warn("订单编号是空的：" + orderNo);
        }

        PaymentInfo paymentInfo = paymentInfoRepo.findByOrderNo(orderNo).orElseThrow(new BusinessException("支付信息不存在"));

        if(paymentInfo != null && paymentInfo.getStatus() != PayStatus.HAVE_PAID){
            //不需要重复更新
            LocalDateTime notifyTime = LocalDateTime.ofEpochSecond(eventTime / 1000,0 , ZoneOffset.ofHours(8));
            paymentInfo.setNotifyTime(notifyTime);
            paymentInfo.setStatus(PayStatus.HAVE_PAID);
            paymentInfo.setTransactionId(transactionId);
            paymentInfo.setThirdPayNo(thirdPayNo);
            if(StringUtils.isNotEmpty(payTime)){
                try {
                    DateTime payTimeObj = DateUtil.parse(payTime);
                    paymentInfo.setPayTime(payTimeObj.toLocalDateTime());
                }catch(Exception e){
                    log.warn("支付时间解析失败：" + payTime);
                }
            }
            paymentInfoRepo.saveAndFlush(paymentInfo);
            //更新购买订单的订单编号
            if(paymentInfo.getOrderId() != null){
                orderRepo.updateOrderNo(paymentInfo.getOrderId(), orderNo, transactionId);
            }

            //更新转赠订单的订单编号
            if(paymentInfo.getGiftOrderId() != null){
                giftOrderRepo.updateOrderNo(paymentInfo.getGiftOrderId(), orderNo, transactionId);
            }


            //更新钱包变动
            if(paymentInfo.getUserFundAccountId() != null){
                userFundChangeLogService.updateSuccess(paymentInfo.getUserId(), FundChangeType.PAYMENT, paymentInfo.getOrderNo());
            }

        }

        return paymentInfo;
    }

    /**
     * 更改支付通知
     */
    @Transactional(Transactional.TxType.REQUIRES_NEW)
    public PaymentInfo notifyPaymentInfo(String orderNo, String transactionId, long eventTime, String payTime){
        return notifyPaymentInfo(orderNo, transactionId, null, eventTime, payTime);
    }

    /**
     * 更改支付通知
     */
    @Transactional(Transactional.TxType.REQUIRES_NEW)
    public PaymentInfo notifyPaymentInfo(String orderNo, String transactionId, long eventTime){
        return notifyPaymentInfo(orderNo, transactionId, null, eventTime, null);
    }

    /**
     * 待退款
     * @param orderNo
     * @return
     */
    @Transactional(Transactional.TxType.REQUIRES_NEW)
    public PaymentInfo setPendingRefund(String orderNo, String refundReason){
        //更改支付信息状态
        if(StringUtils.isEmpty(orderNo)){
            log.warn("订单编号是空的：" + orderNo);
        }

        PaymentInfo paymentInfo = paymentInfoRepo.findByOrderNo(orderNo).orElse(null);
        //已支付的才退款，防止重复操作
        if(paymentInfo != null && paymentInfo.getStatus() == PayStatus.HAVE_PAID){
            paymentInfo.setStatus(PayStatus.PENDING_REFUND);
            paymentInfo.setRefundReason(refundReason);
            paymentInfoRepo.save(paymentInfo);
        }
        return paymentInfo;
    }

    /**
     * 退款修改
     * @param orderNo
     * @return
     */
    @Transactional(Transactional.TxType.REQUIRES_NEW)
    public PaymentInfo completeRefund(String orderNo, String refundNo, String refundTime){
        //更改支付信息状态
        PaymentInfo paymentInfo = paymentInfoRepo.findByOrderNo(orderNo).orElseThrow(new BusinessException("支付信息不存在"));
        paymentInfo.setStatus(PayStatus.REFUNDED);
        if(StringUtils.isNotEmpty(refundNo)){
            paymentInfo.setRefundNo(refundNo);
        }
        if(StringUtils.isNotEmpty(refundTime)){
            try {
                DateTime payTimeObj = DateUtil.parse(refundTime);
                paymentInfo.setRefundTime(payTimeObj.toLocalDateTime());
            }catch(Exception e){
                log.warn("支付时间解析失败：" + refundTime);
                paymentInfo.setRefundTime(LocalDateTime.now());
            }
        }else{
            paymentInfo.setRefundTime(LocalDateTime.now());
        }

        paymentInfoRepo.save(paymentInfo);

        //更新流水记录
        if(paymentInfo.getUserFundAccountId() != null){
            userFundChangeLogService.updateSuccess(paymentInfo.getUserId(), FundChangeType.REFUND, paymentInfo.getOrderNo());
        }

        return paymentInfo;
    }

    /**
     * 退款修改
     * @param orderNo
     * @return
     */
    @Transactional(Transactional.TxType.REQUIRES_NEW)
    public PaymentInfo completeRefund(String orderNo, String refundNo){
        return completeRefund(orderNo, refundNo, null);
    }

    /**
     * 支付失败记录
     * @param orderNo
     * @return
     */
    @Transactional(Transactional.TxType.REQUIRES_NEW)
    public PaymentInfo failedPaymentInfo(String orderNo,String transactionId, String thirdPayNo,
                                         long eventTime, String payTime){
        //更改支付信息状态
        if(StringUtils.isEmpty(orderNo)){
            log.warn("订单编号是空的：" + orderNo);
        }

        PaymentInfo paymentInfo = paymentInfoRepo.findByOrderNo(orderNo).orElse(null);
        if(paymentInfo != null){
            LocalDateTime notifyTime = LocalDateTime.ofEpochSecond(eventTime / 1000,0 , ZoneOffset.ofHours(8));
            paymentInfo.setNotifyTime(notifyTime);
            paymentInfo.setStatus(PayStatus.FAILED_PAID);
            paymentInfo.setTransactionId(transactionId);
            paymentInfo.setThirdPayNo(thirdPayNo);
            if(StringUtils.isNotEmpty(payTime)){
                try {
                    DateTime payTimeObj = DateUtil.parse(payTime);
                    paymentInfo.setPayTime(payTimeObj.toLocalDateTime());
                }catch(Exception e){
                    log.warn("支付时间解析失败：" + payTime);
                }
            }
            paymentInfoRepo.save(paymentInfo);
        }
        return paymentInfo;
    }

    /**
     * 支付失败记录
     */
    @Transactional(Transactional.TxType.REQUIRES_NEW)
    public PaymentInfo failedPaymentInfo(String orderNo,String transactionId, long eventTime,  String payTime){
        return failedPaymentInfo(orderNo, transactionId, null, eventTime, payTime);
    }


    /**
     * 支付失败记录
     */
    @Transactional(Transactional.TxType.REQUIRES_NEW)
    public PaymentInfo failedPaymentInfo(String orderNo,String transactionId, long eventTime){
        return failedPaymentInfo(orderNo, transactionId, null, eventTime, null);
    }


    /**
     * 查询支付结果
     * @param orderNo
     * @return
     */
    @Transactional
    public PayResult checkPayResult(String orderNo){
        PaymentInfo paymentInfo = paymentInfoRepo.findByOrderNo(orderNo).orElseThrow(new BusinessException("支付信息不存在"));

        PayResult payResult = null;
        if(UniformPayConstants.SANDPAY_PLATFORM.equals(paymentInfo.getPayPlatform())){
            payResult = sandpayService.checkPayResult(orderNo);
        }else if(UniformPayConstants.LIANLIANPAY_PLATFORM.equals(paymentInfo.getPayPlatform())){
            payResult = lianlianPayService.checkPayResult(orderNo);
        }else{
            throw new BusinessException("暂不支持该支付平台查询：" + paymentInfo.getPayPlatform());
        }

        payResult.setGoodsTitle(paymentInfo.getGoodsTitle());
        payResult.setPayPlatform(paymentInfo.getPayPlatform());

        //更新支付结果
        if(paymentInfo.getStatus() == PayStatus.UNPAID || paymentInfo.getStatus() == PayStatus.PENDING){
            if(payResult.getStatus() == PayStatus.HAVE_PAID) {
                //修正未支付的状态，检查订单
                OrderStatus orderStatus = null;
                if (paymentInfo.getOrderId() != null) {
                    Order order = orderRepo.findById(paymentInfo.getOrderId()).orElse(null);
                    if (order != null) {
                        orderStatus = order.getStatus();
                    }
                } else if (paymentInfo.getGiftOrderId() != null) {
                    GiftOrder giftOrder = giftOrderRepo.findById(paymentInfo.getGiftOrderId()).orElse(null);
                    if (giftOrder != null) {
                        orderStatus = giftOrder.getStatus();
                    }
                }
                if (orderStatus != null && orderStatus == OrderStatus.CANCELLED) {
                    //标记为待退款
                    paymentInfo.setStatus(PayStatus.PENDING_REFUND);
                    paymentInfo.setRefundReason("订单已被取消");
                } else {
                    //标记为已支付
                    paymentInfo.setStatus(PayStatus.HAVE_PAID);
                    paymentInfo.setRefundReason("同步支付状态");
                }
                paymentInfo.setTransactionId(payResult.getTransactionNo());
                paymentInfo.setPayTime(payResult.getPayTime());
                paymentInfoRepo.save(paymentInfo);

                //更新流水记录
                userFundChangeLogService.updateSuccess(paymentInfo.getUserId(), FundChangeType.PAYMENT, paymentInfo.getOrderNo());
            }else if(payResult.getStatus() == PayStatus.FAILED_PAID){
                //支付失败记录
                paymentInfo.setStatus(PayStatus.FAILED_PAID);
                paymentInfo.setRefundReason("同步支付状态");
                paymentInfo.setTransactionId(payResult.getTransactionNo());
                paymentInfo.setPayTime(payResult.getPayTime());
                paymentInfoRepo.save(paymentInfo);
                //更新流水记录
                userFundChangeLogService.updateFailed(paymentInfo.getUserId(), FundChangeType.PAYMENT, paymentInfo.getOrderNo());
            }

        }


        return payResult;
    }

    /**
     * 统计金额信息
     * @return
     */
    public BigDecimal sumPayAmt(Specification specification){
        CriteriaBuilder criteriaBuilder = entityManager.getCriteriaBuilder();
        CriteriaQuery<BigDecimal> criteriaQuery = criteriaBuilder.createQuery(BigDecimal.class);
        Root<PaymentInfo> root = criteriaQuery.from(PaymentInfo.class);
        criteriaQuery.select(
                criteriaBuilder.sum(root.get("payAmt"))
        );
        Predicate predicate = specification.toPredicate(root, criteriaQuery, criteriaBuilder);
        criteriaQuery.where(predicate);

        TypedQuery<BigDecimal> query = entityManager.createQuery(criteriaQuery);
        return query.getSingleResult();
    }


    public BigDecimal lastDayWalletPayNum() {
        return paymentInfoRepo.lastDayWalletPayNum();
    }

    public BigDecimal lastDayBankPayNum() {
        return paymentInfoRepo.lastDayBankPayNum();
    }
}
