package com.ruoyi.web.app;

import java.time.LocalDateTime;
import java.util.List;
import java.util.function.Supplier;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import com.ruoyi.bst.order.domain.OrderQuery;
import com.ruoyi.bst.order.domain.OrderVO;
import com.ruoyi.bst.order.domain.dto.OrderCalcFeeDTO;
import com.ruoyi.bst.order.domain.dto.OrderCalcPrePriceDTO;
import com.ruoyi.bst.order.domain.dto.OrderCalcRideFeeDTO;
import com.ruoyi.bst.order.domain.dto.OrderChangeDeviceDTO;
import com.ruoyi.bst.order.domain.dto.OrderCloseDeviceDTO;
import com.ruoyi.bst.order.domain.dto.OrderCreateDTO;
import com.ruoyi.bst.order.domain.dto.OrderDeductDTO;
import com.ruoyi.bst.order.domain.dto.OrderEndDTO;
import com.ruoyi.bst.order.domain.dto.OrderOpenDeviceDTO;
import com.ruoyi.bst.order.domain.dto.OrderPayRideFeeDTO;
import com.ruoyi.bst.order.domain.dto.OrderSeatDTO;
import com.ruoyi.bst.order.domain.enums.OrderReturnType;
import com.ruoyi.bst.order.domain.enums.OrderStatus;
import com.ruoyi.bst.order.domain.vo.OrderEndVO;
import com.ruoyi.bst.order.domain.vo.OrderFeeVO;
import com.ruoyi.bst.order.domain.vo.OrderPrePriceVO;
import com.ruoyi.bst.order.service.OrderConverter;
import com.ruoyi.bst.order.service.OrderService;
import com.ruoyi.bst.order.service.OrderValidator;
import com.ruoyi.common.annotation.Anonymous;
import com.ruoyi.common.annotation.Log;
import com.ruoyi.common.core.controller.BaseController;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.core.page.TableDataInfo;
import com.ruoyi.common.core.redis.RedisLock;
import com.ruoyi.common.core.redis.enums.RedisLockKey;
import com.ruoyi.common.enums.BusinessType;
import com.ruoyi.common.enums.LogBizType;
import com.ruoyi.common.utils.LogParamHolder;
import com.ruoyi.common.utils.ServiceUtil;

import io.swagger.annotations.ApiOperation;



@RestController
@RequestMapping("/app/order")
public class AppOrderController extends BaseController {

    @Autowired
    private OrderService orderService;

    @Autowired
    private OrderConverter orderConverter;

    @Autowired
    private OrderValidator orderValidator;

    @Autowired
    private RedisLock redisLock;

    /**
     * 执行带设备锁的操作
     * @param deviceId 设备ID
     * @param operation 具体操作
     * @return 操作结果
     */
    private AjaxResult executeWithLock(Long deviceId, Supplier<AjaxResult> operation) {
        ServiceUtil.assertion(deviceId == null, "设备ID不能为空");
        Long lockKey = deviceId;
        boolean lock = redisLock.lock(RedisLockKey.DEVICE_IOT_OPERATION, lockKey);
        ServiceUtil.assertion(!lock, "设备操作过于频繁，请稍后再试");
        try {
            return operation.get();
        } finally {
            redisLock.unlock(RedisLockKey.DEVICE_IOT_OPERATION, lockKey);
        }
    }

    @ApiOperation("获取我的订单列表")
    @GetMapping("/mineList")
    public TableDataInfo getMineList(OrderQuery query) {
        query.setUserId(getUserId());
        startPage();
        List<OrderVO> list = orderService.selectOrderList(query);
        return getDataTable(list);
    }

    @ApiOperation("获取我的订单详情")
    @GetMapping("/mineDetail")
    public AjaxResult getMineDetail(Long id) {
        OrderQuery query = new OrderQuery();
        query.setId(id);
        query.setUserId(getUserId());
        return success(orderService.selectOne(query));
    }

    @ApiOperation("获取我正在进行的订单")
    @GetMapping("/mineProcessing")
    public AjaxResult getMineProcessing() {
        OrderQuery query = new OrderQuery();
        query.setUserId(getUserId());
        query.setStatus(OrderStatus.PROCESSING.getCode());
        return success(orderService.selectOne(query));
    }

    @ApiOperation("支付前计算订单价格")
    @PostMapping("/calculatePrice")
    @Log(title = "支付前计算订单价格", businessType = BusinessType.OTHER, bizIdName = "arg0", bizType = LogBizType.SUIT)
    @Anonymous
    public AjaxResult calculatePrice(@RequestBody @Validated OrderCalcPrePriceDTO dto) {
        OrderPrePriceVO vo = orderConverter.toOrderPrePriceVO(dto);
        if (vo == null) {
            return error("计算价格失败，请检查");
        }
        return success(vo);
    }

    @ApiOperation("下单并支付")
    @PostMapping
    @Log(title = "下单并支付", businessType = BusinessType.OTHER, bizIdName = "arg0", bizType = LogBizType.SUIT)
    public AjaxResult createOrder(@RequestBody @Validated OrderCreateDTO dto) {
        dto.setUserId(getUserId());
        return success(orderService.createOrder(dto));
    }

    @ApiOperation("结束订单（还车）")
    @PutMapping("/end")
    @Log(title = "结束订单（还车）", businessType = BusinessType.OTHER, bizIdName = "arg0", bizType = LogBizType.ORDER)
    public AjaxResult endOrder(@RequestBody @Validated OrderEndDTO dto) {
        // 设置日志参数
        LogParamHolder.set(LogParamHolder.PARAM_LON, dto.getLon());
        LogParamHolder.set(LogParamHolder.PARAM_LAT, dto.getLat());

        OrderVO order = orderService.selectOrderById(dto.getOrderId());
        ServiceUtil.assertion(!orderValidator.canEnd(order, getUserId()), "您无权结束ID为%s的订单", order.getId());
        dto.setReturnType(OrderReturnType.NORMAL.getCode());
        dto.setEndReason("用户【" + getNickName() + "】手动还车");
        dto.setNeedDispatchFee(true);
        if (dto.getRequiredIot() == null) {
            dto.setRequiredIot(true);
        }
        OrderEndVO vo = orderService.endOrder(dto);
        if (vo.getDb() > 0) {
            return success(vo);
        }
        return error("关闭订单失败");
    }

    @ApiOperation("计算订单费用")
    @PostMapping("/calcFee")
    @Log(title = "计算订单费用", businessType = BusinessType.OTHER, bizIdName = "arg0", bizType = LogBizType.ORDER)
    public AjaxResult calcFee(@RequestBody @Validated OrderCalcFeeDTO dto) {
        // 设置日志参数
        LogParamHolder.set(LogParamHolder.PARAM_LON, dto.getLon());
        LogParamHolder.set(LogParamHolder.PARAM_LAT, dto.getLat());

        OrderFeeVO fee = orderService.calcFee(dto);
        ServiceUtil.assertion(fee == null, "价格计算失败");
        return success(fee);
    }

    @ApiOperation("计算预估骑行费")
    @GetMapping("/calcPreRideFee")
    public AjaxResult calcPreRideFee(Long orderId) {
        return success(orderService.calcPreRideFee(orderId, LocalDateTime.now()));
    }

    @ApiOperation("订单开锁")
    @PutMapping("/openDevice")
    @Log(title = "订单开锁", businessType = BusinessType.OTHER, bizIdName = "arg0", bizType = LogBizType.ORDER)
    public AjaxResult openDevice(@Validated OrderOpenDeviceDTO dto) {
        // 设置日志参数
        LogParamHolder.set(LogParamHolder.PARAM_LON, dto.getLon());
        LogParamHolder.set(LogParamHolder.PARAM_LAT, dto.getLat());

        OrderVO order = orderService.selectOrderById(dto.getOrderId());
        ServiceUtil.assertion(order == null, "订单不存在");
        ServiceUtil.assertion(!orderValidator.canOpenDevice(order, getUserId()), "您无权操作ID为%s的订单设备开启", order.getId());
        if (dto.getRequiredIot() == null) {
            dto.setRequiredIot(true);
        }
        return executeWithLock(order.getDeviceId(), () -> {
            return success(orderService.openDevice(dto));
        });
    }

    @ApiOperation("临时锁车")
    @PutMapping("/closeDevice")
    @Log(title = "临时锁车", businessType = BusinessType.OTHER, bizIdName = "arg0", bizType = LogBizType.ORDER)
    public AjaxResult closeDevice(@Validated OrderCloseDeviceDTO dto) {
        // 设置日志参数
        LogParamHolder.set(LogParamHolder.PARAM_LON, dto.getLon());
        LogParamHolder.set(LogParamHolder.PARAM_LAT, dto.getLat());

        OrderVO order = orderService.selectOrderById(dto.getOrderId());
        ServiceUtil.assertion(order == null, "订单不存在");
        ServiceUtil.assertion(!orderValidator.canCloseDevice(order, getUserId()), "您无权操作ID为%s的订单设备关闭", order.getId());
        if (dto.getRequiredIot() == null) {
            dto.setRequiredIot(true);
        }
        return executeWithLock(order.getDeviceId(), () -> {
            return success(orderService.closeDevice(dto));
        });
    }

    @ApiOperation("换车")
    @PutMapping("/changeDevice")
    @Log(title = "换车", businessType = BusinessType.OTHER, bizIdName = "arg0", bizType = LogBizType.ORDER)
    public AjaxResult changeDevice(@RequestBody @Validated OrderChangeDeviceDTO dto) {
        // 设置日志参数
        LogParamHolder.set(LogParamHolder.PARAM_LON, dto.getLon());
        LogParamHolder.set(LogParamHolder.PARAM_LAT, dto.getLat());

        OrderVO order = orderService.selectOrderById(dto.getOrderId());
        ServiceUtil.assertion(!orderValidator.canChangeDevice(order, getUserId()), "您无权操作ID为%s的订单换车", order.getId());
        dto.setUserId(getUserId());
        dto.setUserName(getNickName());
        return toAjax(orderService.changeDevice(dto));
    }

    @ApiOperation("打开坐垫锁")
    @PutMapping("/seat")
    @Log(title = "打开坐垫锁", businessType = BusinessType.OTHER, bizIdName = "arg0", bizType = LogBizType.ORDER)
    public AjaxResult seat(@RequestBody @Validated OrderSeatDTO dto) {
        // 设置日志参数
        LogParamHolder.set(LogParamHolder.PARAM_LON, dto.getLon());
        LogParamHolder.set(LogParamHolder.PARAM_LAT, dto.getLat());

        OrderVO order = orderService.selectOrderById(dto.getOrderId());
        ServiceUtil.assertion(!orderValidator.isUser(order, getUserId()), "您无权操作ID为%s的订单打开坐垫锁", order.getId());
        if (dto.getRequiredIot() == null) {
            dto.setRequiredIot(false);
        }
        return success(orderService.seat(dto));
    }

    @ApiOperation("支付骑行费")
    @PutMapping("/payRideFee")
    @Log(title = "支付骑行费", businessType = BusinessType.OTHER, bizIdName = "arg0", bizType = LogBizType.ORDER)
    public AjaxResult payRideFee(@RequestBody @Validated OrderPayRideFeeDTO dto) {
        dto.setUserId(getUserId());
        return success(orderService.payRideFee(dto));
    }

    @ApiOperation("计算应付骑行费")
    @PostMapping("/calcRideFee")
    public AjaxResult calcRideFee(@RequestBody @Validated OrderCalcRideFeeDTO dto) {
        return success(orderService.calcRideFee(dto));
    }

    @ApiOperation("押金抵扣")
    @Log(title = "用户押金抵扣", businessType = BusinessType.OTHER, bizIdName = "arg0", bizType = LogBizType.ORDER)
    @PutMapping("/deduct")
    public AjaxResult deduct(@RequestBody @Validated OrderDeductDTO dto) {
        OrderVO order = orderService.selectOrderById(dto.getId());
        ServiceUtil.assertion(!orderValidator.isUser(order, getUserId()), "您无权操作ID为%s的订单押金抵扣", order.getId());
        dto.setIsAdmin(false);
        return success(orderService.deduct(dto));
    }
}
