package com.cyl.manager.staff.service.impl;

import cn.hutool.core.util.IdUtil;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.cyl.h5.pojo.vo.H5OrderVO;
import com.cyl.h5.service.H5OrderService;
import com.cyl.manager.staff.domain.DispatchOrder;
import com.cyl.manager.staff.domain.Staff;
import com.cyl.manager.staff.mapper.DispatchOrderMapper;
import com.cyl.manager.staff.service.IDispatchOrderService;
import com.cyl.manager.staff.service.IStaffService;
import com.ruoyi.common.constant.Constants;
import com.ruoyi.common.constant.DispatchOrderConstants;
import com.ruoyi.common.core.redis.RedisCache;
import com.ruoyi.common.core.redis.RedisService;
import com.ruoyi.common.core.sms.AliyunSmsUtils;
import com.ruoyi.common.exception.ServiceException;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.common.utils.spring.SpringUtils;
import com.ruoyi.framework.config.LocalDataUtil;
import lombok.extern.slf4j.Slf4j;
import net.bytebuddy.implementation.bytecode.Throw;
import org.redisson.Redisson;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.Date;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

/**
 * <p>
 * 派单信息 服务实现类
 * </p>
 *
 * @author 谷瑶
 * @since 2025-01-04
 */
@Service
@Slf4j
public class DispatchOrderServiceImpl extends ServiceImpl<DispatchOrderMapper, DispatchOrder> implements IDispatchOrderService {

    @Autowired
    private H5OrderService h5OrderService;

    @Autowired
    private IStaffService staffService;

    @Autowired
    private ScheduledExecutorService executor;

    @Autowired
    private RedisService redisService;

    @Autowired
    private RedisCache redisCache;


    @Autowired
    private RedissonClient redisson;


    @Override
    @Transactional(rollbackFor = Throwable.class)
    public void dispatch(Long id) {
        //获取订单全量信息
        H5OrderVO h5OrderVO = h5OrderService.orderDetail(id);
        //获取伴宠师id
        Long staffId = h5OrderVO.getStaffId();

        try{
            //订单库存信息存入redis，供抢单使用
            redisCache.setCacheMapValue(DispatchOrderConstants.DISPATCH_ORDER_COUNT,
                    h5OrderVO.getOrderId().toString(),
                    1);

            if (staffId != null) {
                //指定派单
                dispatchWithSpecifiedStaff(staffId, h5OrderVO);
            }else {
                //系统派单
                dispatchWithoutSpecifiedStaff(h5OrderVO, null);
            }
        }catch (Exception e) {
            log.error("派单失败!订单编号:"+id,e);
            throw e;
        }finally {
            //派单失败，清空库存
            redisCache.setCacheMapValue(DispatchOrderConstants.DISPATCH_ORDER_COUNT,
                    h5OrderVO.getOrderId().toString(),
                    0);
        }

    }

    /**
     * @param dispatchOrderId
     * @return
     */
    @Override
    @Transactional(rollbackFor = Throwable.class)
    public synchronized boolean acceptDispatch(String dispatchOrderId) {
        //校验操作人信息
        Staff staff = (Staff)LocalDataUtil.getVar(Constants.STAFF_INFO);
        Long staffId = staff.getId();
        DispatchOrder dispatchOrder = this.getById(dispatchOrderId);
        if (dispatchOrder.getDispatchedStatus() == 3) {
            //订单被抢或者订单超时自动加入了已流失
            throw new ServiceException("抱歉, 订单已流失");
        }
        if (dispatchOrder == null || dispatchOrder.getOrderId() == null) {
            throw new ServiceException("原始订单获取失败");
        }
        if (staffId != dispatchOrder.getStaffId()) {
            //操作人无权限
            throw new ServiceException("操作人无权限");
        }

        //开始抢单
        RLock lock = redisson
                .getLock(DispatchOrderConstants.DISPATCH_ORDER_ACCEPTED + dispatchOrder.getOrderId());
        if (lock.tryLock()){
            try{

                //获取该派单库存
                Object cacheMapValue = redisCache.getCacheMapValue(DispatchOrderConstants.DISPATCH_ORDER_COUNT,
                        dispatchOrder.getOrderId().toString());
                if (cacheMapValue == null || Integer.valueOf(cacheMapValue.toString()) < 0) {
                    //抢单失败
                    throw new ServiceException("抱歉, 订单已流失");
                }


                //抢单成功
                //修改订单状态为已接单
                dispatchOrder.setDispatchedStatus(2);
                //接单时间
                dispatchOrder.setAcceptTime(LocalDateTime.now());
                this.updateById(dispatchOrder);
                // TODO: 4/1/25  给管理员发送短信伴宠师已经接单了


                //修改原始订单状态为已接单？


                //重置伴宠师连续拒绝次数
                staff.setContinuousRejectCount(0);
                staffService.updateById(staff);

                //设置其它单为已流失
                LambdaUpdateWrapper<DispatchOrder> updateWrapper = Wrappers.lambdaUpdate(DispatchOrder.class)
                        .eq(DispatchOrder::getOrderId, dispatchOrder.getOrderId())
                        .ne(DispatchOrder::getId, dispatchOrderId)
                        .set(DispatchOrder::getDispatchedStatus, 3);
                this.update(updateWrapper);

                //扣减库存，需要清空吗？最好是定时任务做清空，此处设置库存为0，保存操作记录
                redisCache.setCacheMapValue(DispatchOrderConstants.DISPATCH_ORDER_COUNT,
                        dispatchOrder.getOrderId().toString(),0);
            }finally {

                //释放
                lock.unlock();
            }
        }else {
            throw new ServiceException("抢单失败,请重试");
        }

        return true;
    }


    @Override
    @Transactional(rollbackFor = Throwable.class)
    public boolean rejectDispatch(String dispatchOrderId) {

        //校验操作人信息
        Staff staff = (Staff)LocalDataUtil.getVar(Constants.STAFF_INFO);
        Long staffId = staff.getId();
        DispatchOrder dispatchOrder = this.getById(dispatchOrderId);
        if (staffId != dispatchOrder.getStaffId()) {
            //操作人无权限
            throw new ServiceException("操作人无权限");
        }
        //修改订单状态为已流失
        dispatchOrder.setDispatchedStatus(3);
        this.updateById(dispatchOrder);


        //修改伴宠师连续拒绝次数
        Integer rejectCount = staff.getContinuousRejectCount();
        if (rejectCount >= 2) {
            //连续拒单3次，清退
            staff.setStatus(2);
        }else {
            //连续拒单次数加1
            staff.setContinuousRejectCount(++rejectCount);
        }

        staffService.updateById(staff);

        return true;
    }

    @Override
    @Transactional(rollbackFor = Throwable.class)
    public void expire(String dispatchOrderId) {
        DispatchOrder order = this.getById(dispatchOrderId);
        if (order.getDispatchedStatus() == 1) {
            //伴宠师未处理，修改为已流失订单，伴宠师拒单次数加1
            order.setDispatchedStatus(3);
            //更新订单
            this.updateById(order);
            Long tmpStaffId = order.getStaffId();
            Staff tmpStaff = staffService.getById(tmpStaffId);
            //更新伴宠师信息
            Integer rejectCount = tmpStaff.getContinuousRejectCount();
            if (rejectCount >= 2) {
                //连续拒单3次，清退
                tmpStaff.setStatus(2);
            }else {
                //连续拒单次数加1
                tmpStaff.setContinuousRejectCount(++rejectCount);
            }
            //转为系统派单，需要排除当前指定伴宠师id
            dispatchWithoutSpecifiedStaff(order.getOrderInfo(),tmpStaffId);
        }else {
            //伴宠师已处理该订单，无需其它操作。
        }
    }

    @Override
    public void nextAlert(String dispatchOrderId) {
        DispatchOrder order = this.getById(dispatchOrderId);
        Staff staff = staffService.getById(order.getStaffId());
        if (order.getDispatchedStatus() == 1) {
            //未处理，再次发送短信提醒
            if (StringUtils.isNotEmpty(staff.getPhone())) {
                AliyunSmsUtils.sendBcshHasOrderSMS(staff.getPhone());
            }
        }else {
            //伴宠师已处理该订单，无需其它操作。
        }
    }


    /**
     * 指定派单
     * @param staffId
     * @param h5OrderVO
     */
    private void dispatchWithSpecifiedStaff(Long staffId, H5OrderVO h5OrderVO) {
        Staff staff = staffService.getById(staffId);
        if (staff == null || staff.getStatus() == 1) {
            log.error("未找到伴宠师或伴宠师状态异常,流转至系统派单,staffId:{}",staffId);
            dispatchWithoutSpecifiedStaff(h5OrderVO,null);
            return;
        }

        //组装地址
        String addressStr
                = h5OrderVO.getReceiverProvince() + h5OrderVO.getReceiverCity() + h5OrderVO.getReceiverDistrict();
        //计算报酬，支付金额的70%
        BigDecimal reward = h5OrderVO.getPayAmount().multiply(new BigDecimal("0.7"));

        //组装派单对象
        DispatchOrder dispatchOrder = DispatchOrder
                .builder()
                .orderInfo(h5OrderVO)
                //待接单
                .dispatchedStatus(1)
                //个人订单
                .dispatchedType(2)
                //订单编号
                .orderId(h5OrderVO.getOrderId())
                .staffId(staffId)
                //地址
                .serviceAddress(addressStr)
                //报酬
                .reward(reward)
                //派单时间
                .dispatchTime(LocalDateTime.now())
                .build();
        //创建时间
        dispatchOrder.setCreateTime(LocalDateTime.now());
        this.save(dispatchOrder);




        // TODO: 4/1/25  发送短信和小程序推送提醒
        if (StringUtils.isNotEmpty(staff.getPhone())) {
            AliyunSmsUtils.sendBcshHasOrderSMS(staff.getPhone());
        }

        String dispatchOrderId = dispatchOrder.getId();

        //此处应有定时任务检查所有订单过期情况，目前先用scheduler在60分钟后自触发。
        executor.schedule(()->{
            //检查是否已经接单。

            IDispatchOrderService service = SpringUtils.getBean(IDispatchOrderService.class);
            service.nextAlert(dispatchOrderId);

        },30, TimeUnit.MINUTES);

        executor.schedule(()->{
            //检查是否已经接单。

            IDispatchOrderService service = SpringUtils.getBean(IDispatchOrderService.class);
            service.expire(dispatchOrderId);

        },60, TimeUnit.MINUTES);


    }



    /**
     * 系统派单
     * @param h5OrderVO
     * @param excludeStaffId 需要排除派单的伴宠师id
     */
    private void dispatchWithoutSpecifiedStaff(H5OrderVO h5OrderVO,Long excludeStaffId) {
        //不给id为excludeStaffId的伴宠师派单，因为这是他拒单或者超时未处理造成的。
        //检查伴宠师是否符合接单条件，添加了字段serviceInfoCommitted，不符合的不派单

    }
}
