package com.ruoyi.web.app;

import java.math.BigDecimal;
import java.util.function.Supplier;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.validation.annotation.Validated;
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.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import com.ruoyi.bst.device.domain.DeviceVO;
import com.ruoyi.bst.device.domain.dto.DeviceBltUploadDTO;
import com.ruoyi.bst.device.service.DeviceIotService;
import com.ruoyi.bst.device.service.DeviceService;
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.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.MathUtils;
import com.ruoyi.common.utils.ServiceUtil;
import com.ruoyi.common.utils.map.GeoUtils;
import com.ruoyi.iot.constants.IotConstants;

import io.swagger.annotations.ApiOperation;

@RestController
@RequestMapping("/app/device/iot")
public class AppDeviceIotController extends BaseController {

    @Autowired
    private DeviceIotService deviceIotService;

    @Autowired
    private DeviceService deviceService;

    @Autowired
    private RedisLock redisLock;

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

    private DeviceVO getDevice(Long id, String sn) {
        if (id != null) {
            return deviceService.selectDeviceById(id);
        } else {
            return deviceService.selectDeviceBySn(sn);
        }
    }

    @ApiOperation("用户响铃寻车")
    @PutMapping("/ring")
    @Log(title = "用户响铃寻车", businessType = BusinessType.OTHER, bizIdName = "arg0", bizType = LogBizType.DEVICE)
    public AjaxResult ring(@RequestParam(required = false) Long id,
        @RequestParam(required = false) String sn,
        @RequestParam(required = false) BigDecimal lon,
        @RequestParam(required = false) BigDecimal lat) {
        DeviceVO device = getDevice(id, sn);
        ServiceUtil.assertion(device == null, "当前车辆不存在，无法响铃寻车");

        if (device.getAreaRequiredRingRadius() != null && device.getAreaRequiredRingRadius()) {
            ServiceUtil.assertion(lon == null || lat == null, "请开启定位后重试");
            BigDecimal distance = GeoUtils.calculateDistance(device.getLatitude(), device.getLongitude(), lat, lon);
            ServiceUtil.assertion(MathUtils.biggerThan(distance, device.getAreaRingRadius()),
            "您当前距离车辆%s米，无法响铃寻车。请距离车辆%s米以内再试", distance, device.getAreaRingRadius());
        }

        return executeWithLock(device.getId(), () -> {
            return toAjax(deviceIotService.play(device, IotConstants.PLAY_WARNING, "用户响铃寻车", true));
        });
    }

    @ApiOperation("蓝牙上传设备信息")
    @PutMapping("/bltUpload")
    @Log(title = "蓝牙上传设备信息", businessType = BusinessType.OTHER, bizType = LogBizType.DEVICE)
    public AjaxResult bltUpload(@RequestBody @Validated DeviceBltUploadDTO dto) {
        return toAjax(deviceIotService.bltUpload(dto));
    }
}
