package com.ruoyi.bst.order.service.impl;

import java.math.BigDecimal;
import java.time.Duration;
import java.time.LocalDateTime;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

import com.github.pagehelper.PageHelper;
import com.ruoyi.bst.changeRecord.domain.ChangeRecord;
import com.ruoyi.bst.changeRecord.domain.enums.ChangeRecordBstType;
import com.ruoyi.bst.changeRecord.service.ChangeRecordService;
import com.ruoyi.bst.lightingNum.domain.LightingNum;
import com.ruoyi.bst.lightingNum.domain.LightingNumQuery;
import com.ruoyi.bst.lightingNum.domain.LightingNumVO;
import com.ruoyi.bst.lightingNum.service.LightingNumService;
import com.ruoyi.bst.order.domain.bo.OrderCreateBO;
import com.ruoyi.bst.order.domain.dto.OrderCreateDTO;
import com.ruoyi.bst.order.domain.dto.OrderRefundDTO;
import com.ruoyi.bst.order.domain.enums.OrderStatus;
import com.ruoyi.bst.order.service.OrderConverter;
import com.ruoyi.bst.order.service.OrderValidator;
import com.ruoyi.bst.order.utils.OrderUtil;
import com.ruoyi.bst.pay.domain.Pay;
import com.ruoyi.bst.pay.domain.PayQuery;
import com.ruoyi.bst.pay.domain.dto.PayRefundDTO;
import com.ruoyi.bst.pay.domain.enums.PayBstType;
import com.ruoyi.bst.pay.domain.vo.DoPayVO;
import com.ruoyi.bst.pay.service.PayConverter;
import com.ruoyi.bst.pay.service.PayService;
import com.ruoyi.common.domain.vo.LocalDateTimeIntegerVO;
import com.ruoyi.common.domain.vo.LongDecimalVO;
import com.ruoyi.common.domain.vo.LongIntegerVO;
import com.ruoyi.common.domain.vo.LongLocalDateTimeVO;
import com.ruoyi.common.utils.DateUtils;
import com.ruoyi.common.utils.ServiceUtil;
import com.ruoyi.common.utils.SnowFlakeUtil;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.common.utils.collection.CollectionUtils;

import com.ruoyi.system.user.domain.vo.UserConsumeVO;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.ruoyi.bst.order.mapper.OrderMapper;
import com.ruoyi.bst.order.domain.Order;
import com.ruoyi.bst.order.domain.OrderVO;
import com.ruoyi.bst.order.domain.OrderQuery;
import com.ruoyi.bst.order.service.OrderService;
import org.springframework.transaction.support.TransactionTemplate;

/**
 * 订单列表Service业务层处理
 *
 * @author ruoyi
 * @date 2025-04-26
 */
@Service
public class OrderServiceImpl implements OrderService
{
    @Autowired
    private OrderMapper orderMapper;
    @Autowired
    private TransactionTemplate transactionTemplate;
    @Autowired
    private OrderConverter orderConverter;
    @Autowired
    private OrderValidator orderValidator;
    @Autowired
    private PayConverter payConverter;
    @Autowired
    private PayService payService;
    @Autowired
    private ScheduledExecutorService scheduledExecutorService;
    @Autowired
    private LightingNumService lightingNumService;
    @Autowired
    private ChangeRecordService changeRecordService;
    @Autowired
    private OrderService orderService;

    /**
     * 查询订单列表
     *
     * @param id 订单列表主键
     * @return 订单列表
     */
    @Override
    public OrderVO selectOrderById(Long id)
    {
        return orderMapper.selectOrderById(id);
    }

    /**
     * 查询订单列表列表
     *
     * @param order 订单列表
     * @return 订单列表
     */
    @Override
    public List<OrderVO> selectOrderList(OrderQuery order)
    {
        return orderMapper.selectOrderList(order);
    }

    /**
     * 新增订单列表
     *
     * @param order 订单列表
     * @return 结果
     */
    @Override
    public int insertOrder(Order order)
    {
        order.setCreateTime(DateUtils.getNowDate());
        order.setOrderNo(String.valueOf(SnowFlakeUtil.newId()));
        return orderMapper.insertOrder(order);
    }

    /**
     * 修改订单列表
     *
     * @param order 订单列表
     * @return 结果
     */
    @Override
    public int updateOrder(Order order)
    {
        return orderMapper.updateOrder(order);
    }

    /**
     * 批量删除订单列表
     *
     * @param ids 需要删除的订单列表主键
     * @return 结果
     */
    @Override
    public int deleteOrderByIds(Long[] ids)
    {
        return orderMapper.deleteOrderByIds(ids);
    }

    /**
     * 删除订单列表信息
     *
     * @param id 订单列表主键
     * @return 结果
     */
    @Override
    public int deleteOrderById(Long id)
    {
        return orderMapper.deleteOrderById(id);
    }

    @Override
    public DoPayVO createOrder(OrderCreateDTO dto) {
        // 转为BO对象
        OrderCreateBO bo = orderConverter.toCreateBO(dto);
        // 校验参数
        orderValidator.validate(bo);

        return transactionTemplate.execute(status -> {

            // 创建订单
            Order order = bo.getOrder();
            int rows = this.insertOrder(order);
            ServiceUtil.assertion(rows != 1, "创建订单失败");

            // 查询订单
            OrderVO vo = this.selectOrderByNo(order.getOrderNo());
            ServiceUtil.assertion(vo == null, "查询创建的订单失败");

            // 支付
            DoPayVO doPayVO = this.pay(bo);
            ServiceUtil.assertion(doPayVO == null, "支付失败");
            bo.setDoPayVO(doPayVO);

            // 加入延时队列，超时取消
            this.cancelWhenExpired(order);
            return doPayVO;
        });
    }

    private DoPayVO pay(OrderCreateBO bo) {

        // 转为支付PO
        Pay pay = payConverter.toPayPO(bo);
        ServiceUtil.assertion(pay == null, "转为支付单失败");

        return transactionTemplate.execute(status -> {
            // 创建并调起支付
            return payService.createPayBill(pay);
        });
    }

    // 加入延时队列，超时取消
    @Override
    public void cancelWhenExpired(Order order) {
        if (order == null || order.getPayExpireTime() == null) {
            return;
        }

        Duration duration = Duration.between(LocalDateTime.now(), order.getPayExpireTime());
        long delay = duration.getSeconds();
        if (delay <= 0) {
            delay = 1;
        }
        scheduledExecutorService.schedule(() -> {
            // 尝试取消订单
            this.cancelOrder(order.getId(), "订单超时取消");
        }, delay, TimeUnit.SECONDS);
    }

    // 取消订单
    @Override
    public int cancelOrder(Long id, String remark) {
        if (id == null) {
            return 0;
        }
        // 查询订单
        OrderVO order = this.selectOrderById(id);
        ServiceUtil.assertion(order == null, "ID为%s的订单不存在", id);
        ServiceUtil.assertion(!OrderStatus.canCancelPay().contains(order.getStatus()), "ID为%s的订单当前状态不允许取消", id);

        // 刷新支付结果
        if (order.getPayNo() != null) {
            payService.refreshPayResult(order.getPayNo());
        }

        // 查询最新订单状态
        order = this.selectOrderById(id);
        ServiceUtil.assertion(order == null, "ID为%s的订单不存在", id);
        ServiceUtil.assertion(!OrderStatus.canCancelPay().contains(order.getStatus()), "ID为%s的订单当前状态不允许取消", id);


        // 取消订单
        Integer result = transactionTemplate.execute(status -> {
            // 更新订单状态
            Order data = new Order();
            data.setStatus(OrderStatus.CANCELED.getCode());
            data.setCancelRemark(remark);
            OrderQuery query = new OrderQuery();
            query.setId(id);
            query.setStatusList(OrderStatus.canCancelPay());
            int rows = orderMapper.updateByQuery(data, query);
            ServiceUtil.assertion(rows != 1, "ID为%s的订单取消失败，状态已发生变化", id);

            // 取消支付单
            boolean cancelPay = payService.closeByBstId(PayBstType.ORDER.getType(), id);
            ServiceUtil.assertion(!cancelPay, "ID为%s的订单取消支付单失败", id);

            return rows;
        });

        return result == null ? 0 : result;
    }

    @Override
    public OrderVO selectOrderByBstId(Long bstId) {
        return orderMapper.selectOrderByBstId(bstId);
    }



    @Override
    public OrderVO selectOrderByNo(String no, boolean scope) {
        if (StringUtils.isBlank(no)) {
            return null;
        }
        OrderQuery query = new OrderQuery();
        query.setEqNo(no);
        query.setScope(scope);
        return this.selectOne(query);
    }

    @Override
    public OrderVO selectOne(OrderQuery query) {
        PageHelper.startPage(1, 1);
        List<OrderVO> list = this.selectOrderList(query);
        return CollectionUtils.firstElement(list);
    }

    @Override
    public int refund(OrderRefundDTO dto) {
        OrderVO order = this.selectOrderById(dto.getOrderId());

        return this.refund(order, dto.getAmount(), dto.getNumber(),dto.getReason(), dto.getUserId(), dto.getUserName());
    }

    @Override
    public int addRefundNum(Long id, Integer number) {
        return orderMapper.addRefundNum(id,number);
    }

    @Override
    public List<LocalDateTimeIntegerVO> selectOrderCount(OrderQuery orderQuery) {
        return orderMapper.selectOrderCount(orderQuery);
    }

    @Override
    public List<LongIntegerVO> selectCountGroupByStoreId(OrderQuery query) {
        return orderMapper.selectCountGroupByStoreId(query);
    }

    @Override
    public List<LongDecimalVO> selectSumOfAmountGroupByUserId(OrderQuery query) {
        return orderMapper.selectSumOfAmountGroupByUserId(query);
    }

    @Override
    public List<LongLocalDateTimeVO> assembleLastConsumeTime(OrderQuery query) {
        return orderMapper.assembleLastConsumeTime(query);
    }

    @Override
    public Integer selectUserCount(OrderQuery query) {
        return orderMapper.selectUserCount(query);

    }


    /**
     * 退款
     * @param order 订单
     * @param amount 退款金额
     * @param number 退款次数
     * @param reason 退款原因
     * @param userId 操作人ID
     * @param userName 操作人名称
     * @return 结果
     */
    private int refund(OrderVO order, BigDecimal amount,Integer number, String reason, Long userId, String userName) {
        ServiceUtil.assertion(order == null, "参数错误，order不允许为空");
        ServiceUtil.assertion(!OrderStatus.canRefund().contains(order.getStatus()), "ID为%s的订单当前状态不允许退款", order.getId());
        ServiceUtil.assertion(amount == null || amount.compareTo(BigDecimal.ZERO) < 0, "参数错误，退款金额不允许为空且");
        ServiceUtil.assertion(number == null || number < 0, "参数错误，退款次数不允许为空且不允许小于0");

        BigDecimal canRefundAmount = OrderUtil.calcCanRefundAmount(order);
        ServiceUtil.assertion(canRefundAmount.compareTo(amount) < 0, "ID为%s的订单可退款金额不足，当前可退款金额为%s元", order.getId(), canRefundAmount);

        // 退款原因
        String refundReason = null;
        refundReason = String.format("%s:订单%s退款%s元%s次", userName, order.getOrderNo(),amount,number);
        if (StringUtils.isNotBlank(reason)) {
            refundReason += "：" + reason;
        }
        String finalRefundReason = refundReason;
        Integer result = transactionTemplate.execute(status -> {
            // 判断用户当前次数是否满足退款条件
            LightingNumQuery lightingNumQuery = new LightingNumQuery();
            lightingNumQuery.setUserId(order.getUserId());
            lightingNumQuery.setStoreId(order.getStoreId());
            LightingNumVO vo = lightingNumService.selectOne(lightingNumQuery);
            ServiceUtil.assertion(vo.getNumber()<number,"当前用户爆灯次数小于退款爆灯次数，无法退款");
            // 根据条件更新用户爆灯次数
            LightingNum data = new LightingNum();
            data.setNumber(number);
            OrderQuery query = new OrderQuery();
            query.setUserId(order.getUserId());
            query.setStoreId(order.getStoreId());
            lightingNumService.reduceLightingNumByQuery(data,query);
            // 生成次数变化记录表
            ChangeRecord changeRecord = new ChangeRecord();
            changeRecord.setBstType(ChangeRecordBstType.REFUND.getCode());
            changeRecord.setBstId(order.getId());
            changeRecord.setUserId(order.getUserId());
            changeRecord.setStoreId(order.getStoreId());
            changeRecord.setNumber(number);
            changeRecord.setReason(finalRefundReason);
            changeRecord.setBeforeNum(vo.getNumber());
            changeRecord.setAfterNum(vo.getNumber()-number);
            changeRecord.setCreateTime(DateUtils.getNowDate());
            changeRecordService.insertChangeRecord(changeRecord);
            // 更新订单状态
            order.setStatus(OrderStatus.REFUNDED.getCode());
            orderService.updateOrder(order);
            // 支付退款
            PayRefundDTO dto = new PayRefundDTO();
            dto.setId(order.getPayId());
            dto.setRefundAmount(amount);
            dto.setRefundReason(finalRefundReason);
            dto.setNumber(number);
            dto.setUserId(userId);
            dto.setUserName(userName);
            return payService.refund(dto);
        });
        return result == null ? 0 : 1;

    }
}
