package com.ruoyi.system.task;

import cn.hutool.core.util.StrUtil;
import com.ruoyi.common.constant.ServiceConstants;
import com.ruoyi.common.utils.DateUtils;
import com.ruoyi.common.utils.ServiceUtil;
import com.ruoyi.common.utils.YPMsgUtils;
import com.ruoyi.common.utils.uuid.IdUtils;
import com.ruoyi.ss.clean.domain.Clean;
import com.ruoyi.ss.clean.domain.CleanQuery;
import com.ruoyi.ss.clean.domain.CleanVO;
import com.ruoyi.ss.clean.service.ICleanService;
import com.ruoyi.ss.device.service.DeviceService;
import com.ruoyi.ss.order.domain.Order;
import com.ruoyi.ss.order.domain.OrderVO;
import com.ruoyi.ss.order.service.IOrderService;
import com.ruoyi.ss.orderOper.service.IOrderOperService;
import com.ruoyi.ss.pay.service.IWxPayService;
import com.ruoyi.ss.payBill.domain.PayBill;
import com.ruoyi.ss.payBill.domain.PayBillQuery;
import com.ruoyi.ss.payBill.domain.enums.PayBillStatus;
import com.ruoyi.ss.payBill.service.PayBillService;
import com.ruoyi.ss.refund.domain.RefundDTO;
import com.ruoyi.ss.refund.service.IRefundService;
import com.ruoyi.ss.room.domain.Room;
import com.ruoyi.ss.room.service.IRoomService;
import com.ruoyi.ss.user.domain.UserVO;
import com.ruoyi.ss.user.service.IUserService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.support.TransactionTemplate;

import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import java.math.BigDecimal;
import java.util.List;
import java.util.Objects;

import static com.ruoyi.common.constant.ServiceConstants.IS_REFUND_STATUS_PENDING;
import static com.ruoyi.common.constant.ServiceConstants.IS_REFUND_STATUS_REFUNDED;
import static com.ruoyi.common.utils.SecurityUtils.getUserId;

/**
 * 定时任务调度测试
 *
 * @author ruoyi
 */
@Slf4j
@Component("ssTask")
public class SsTask {


    @Resource
    private IOrderService orderService;

    @Autowired
    private ICleanService cleanService;

    @Autowired
    private IUserService userService;

    @Autowired
    private IRoomService roomService;

    @Resource
    private IWxPayService wxPayService;

    @Autowired
    private TransactionTemplate transactionTemplate;

    @Autowired
    private DeviceService smDeviceService;

    @Autowired
    private PayBillService paymentService;

    @Autowired
    private IOrderOperService orderOperService;

    @Autowired
    private IRefundService refundService;



    /**
     * 1.启动时判断是否有未取消预约的订单
     * 2.判断已完成的订单未退还押金的
     * 3.启动时判断是否分账
     */
    @Transactional
    @PostConstruct
    public void init() {
    }

    /**
     *  结束订单处理
     *  查询未结束已到期的订单，通知保洁，修改订单状态
     */
    public void orderEndHandler(){
        log.info("-------------------【定时任务】结束订单处理---开始----------------");
        List<OrderVO> orderVOS = orderService.selectEndOrderList();
        for (OrderVO orderVO: orderVOS) {
            if(!orderVO.getIsSendMsg()){//没有通知过去通知保洁。修改订单状态：已结束、通知状态：已通知
                orderVO.setIsSendMsg(true);
                // todo 通知保洁, 发送短信  店铺id,保洁
                String merchantPhone = orderVO.getMerchantPhone();
                YPMsgUtils.sendClearMsg(orderVO.getRoomName(),merchantPhone);

                Clean clean = cleanService.buildClean(orderVO);
                int i = cleanService.insertEClean(clean);
                ServiceUtil.assertion(i==0,"生成保洁订单失败");
            }
            updateOrderStatus(orderVO,ServiceConstants.ORDER_STATUS_COMPLETED);
            // todo 改变房间状态，改为未打扫
            updateRoomStatus(orderVO,ServiceConstants.ROOM_STATUS_UNCLEANED);
        }
        log.info("-------------------【定时任务】结束订单处理---结束----------------");
    }

    /**
     *  开始使用处理
     *  查询待使用的订单，判断是否超过当前时间，
     *      如果超过当前时间修改订单状态：使用中
     *          房间也更新状态为使用中
     *      如果未超过当前时间则不修改  分成  user_divide  提现
     */
    public void orderStartHandler(){
        log.info("-------------------【定时任务】开始使用处理---开始----------------");
        List<OrderVO> orderVOS = orderService.selectToeUsedOrderList();
        for (OrderVO orderVO: orderVOS) {
            if(orderVO.getReserveStartTime().before(DateUtils.getNowDate()) && orderVO.getReserveEndTime().after(DateUtils.getNowDate())){
                Boolean execute = transactionTemplate.execute(e -> {
                    // 订单中的预定开始时间 是否已超过当前时间
                    updateOrderStatus(orderVO,ServiceConstants.ORDER_STATUS_IN_USE);
                    // 改变房间状态，改为使用中
                    updateRoomStatus(orderVO,ServiceConstants.ROOM_STATUS_INUSE);

                    // 发送一次房间的全开  用time  门打开 其他的用settime
                    smDeviceService.roomAllDeviceSetTime(orderVO, "下单成功后房间全开");
                    return Boolean.TRUE;
                });
            }
        }
        log.info("-------------------【定时任务】开始使用处理---结束----------------");
    }

    /**
     *  自动完成打扫处理
     */
    public void autoCompleteCleaning(){
        log.info("-------------------【定时任务】自动完成打扫处理---开始----------------");
        CleanQuery cleanQuery = new CleanQuery();
        cleanQuery.setStatus(ServiceConstants.STATUS_PENDING_CLEANING);
        List<CleanVO> cleanVOS = cleanService.selectECleanList(cleanQuery);
        if(cleanVOS.isEmpty()){
            return;
        }
        for (CleanVO clean: cleanVOS) {
            Boolean execute = transactionTemplate.execute(e -> {
                Long merchantId = clean.getMerchantId();
                UserVO userVO = userService.selectUserById(merchantId);
                if(!Objects.isNull(userVO)){
                    Integer cleanDuration = userVO.getCleanDuration();
                    int i = DateUtils.timeDifferenceInMinutes(DateUtils.getNowDate(), clean.getStartTime());
                    if(i>=cleanDuration){
                        boolean i1 = cleanService.completeCleaning(clean.getCleanId());
                        log.info("【自动完成打扫】{}", i1? "失败" : "成功");
                    }
                }
                return Boolean.TRUE;
            });
        }
        log.info("-------------------【定时任务】自动完成打扫处理---结束----------------");
    }

    /**
     *  押金自动退还处理 30秒执行一次
     *  查询所有智能押金模式下，还未退款的订单，根据实际退款时间退款
     */
    public void depositAutoRefund(){
        log.info("-------------------【定时任务】押金自动退还处理---开始----------------");
        List<OrderVO> orderVOS = orderService.selectNotRefundedOrderList();
        for (OrderVO orderVO: orderVOS) {
            if(orderVO.getIsRefund().equals(IS_REFUND_STATUS_PENDING)){
                // todo 是否加锁？判空
                // todo 更新订单是否退款状态
                Boolean execute = transactionTemplate.execute(e -> {
                    Order order = new Order();
                    order.setIsRefund(IS_REFUND_STATUS_REFUNDED);
                    order.setOrderNo(orderVO.getOrderNo());
                    int i = orderService.updateRlOrderByOrderNo(order);
                    ServiceUtil.assertion(i==0,"更新订单是否退款状态失败");

                    // 更新支付单状态为退款中
                    PayBill updatedPayBill =  new PayBill();
                    updatedPayBill.setStatus(PayBillStatus.REFUNDING.getStatus());
                    PayBillQuery billQuery = new PayBillQuery();
                    billQuery.setPayId(orderVO.getPayId());
                    billQuery.setStatus(PayBillStatus.PAY_SUCCESS.getStatus());
                    int i2 = paymentService.updateByQuery(updatedPayBill,billQuery);
                    ServiceUtil.assertion(i2 != 1, "修改支付订单状态失败");

                    // 增加退款中金额
                    int addRefunding = paymentService.addRefundingAmount(orderVO.getPayId(), orderVO.getDepositRefund());
                    ServiceUtil.assertion(addRefunding != 1, "记录退款中的金额失败");

                    // 记录订单履历
                    String desc = "押金模式自动退款金额:"+orderVO.getDepositRefund()+"元";
                    boolean b = orderOperService.recordOrderHistory(orderVO.getOrderNo(), ServiceConstants.ORDER_OPERATION_REFUND,
                            orderVO.getStatus(), orderVO.getStatus(), orderVO.getPayFee(), orderVO.getPayFee(),null , null, desc);
                    ServiceUtil.assertion(!b, "记录订单履历失败");

                    BigDecimal depositRefund = orderVO.getDepositRefund();
                    BigDecimal usedFee = orderVO.getPayFee();//已使用金额
                    orderVO.setPayFee(orderVO.getDeposit());//退款金额

                    // 保存退款记录
                    RefundDTO refundDTO = new RefundDTO();
                    refundDTO.setPayId(orderVO.getPayId());
                    refundDTO.setAmount(depositRefund);
                    refundDTO.setReason(StrUtil.format("押金模式自动退款:押金=【{}】,已使用金额=【{}】,退还金额=【{}】", orderVO.getDeposit(),usedFee,depositRefund));
                    String refNo = refundService.saveRefund(refundDTO, null,orderVO);

                    wxPayService.refund(orderVO,"订单结束，已使用金额："+usedFee+",退还剩余金额："+depositRefund, depositRefund,refNo,orderVO.getPayNo());
                    return Boolean.TRUE;
                });

            }
        }
        log.info("-------------------【定时任务】押金自动退还处理---结束----------------");
    }



    private void updateRoomStatus(OrderVO orderVO,String status) {
        Room room = new Room();
        room.setRoomId(orderVO.getRoomId());
        room.setStatus(status);
        int i = roomService.updateERoom(room);
        ServiceUtil.assertion(i==0,"更新房间状态失败");
    }

    private void updateOrderStatus(OrderVO orderVO,String status) {
        orderVO.setStatus(status);
        int i = orderService.updateRlOrderByOrderNo(orderVO);
        ServiceUtil.assertion(i==0,"更新订单状态失败");
    }

}
