package com.ruoyi.web.bst;

import java.util.function.Supplier;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.PutMapping;
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.areaJoin.domain.enums.AreaJoinPermission;
import com.ruoyi.bst.device.domain.DeviceVO;
import com.ruoyi.bst.device.domain.enums.DeviceUnLockType;
import com.ruoyi.bst.device.service.DeviceAssembler;
import com.ruoyi.bst.device.service.DeviceIotService;
import com.ruoyi.bst.device.service.DeviceService;
import com.ruoyi.bst.device.service.DeviceValidator;
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.ServiceUtil;
import com.ruoyi.iot.constants.IotConstants;

import lombok.extern.slf4j.Slf4j;

@RestController
@RequestMapping("/bst/device/iot")
@Slf4j
public class DeviceIotController extends BaseController {

    @Autowired
    private DeviceIotService deviceIotService;

    @Autowired
    private DeviceService deviceService;

    @Autowired
    private DeviceValidator deviceValidator;

    @Autowired
    private DeviceAssembler deviceAssembler;

    @Autowired
    private RedisLock redisLock;

    private DeviceVO getDevice(Long id, String sn) {
        return deviceService.selectDevice(id, sn, true, AreaJoinPermission.DEVICE_EDIT);
    }

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

    @PreAuthorize("@ss.hasPermi('bst:device:unlock')")
    @Log(title = "管理员开锁", businessType = BusinessType.OTHER, bizIdName = "arg0", bizType = LogBizType.DEVICE)
    @PutMapping("/unlock")
    public AjaxResult unlock(@RequestParam(required = false) Long id, @RequestParam(required = false) String sn) {
        DeviceVO device = this.getDevice(id, sn);
        return executeWithLock(device,
                () -> success(deviceIotService.unlock(device, DeviceUnLockType.ADMIN, "管理员开锁", true)));
    }

    @PreAuthorize("@ss.hasPermi('bst:device:lock')")
    @Log(title = "管理员锁车", businessType = BusinessType.OTHER, bizIdName = "arg0", bizType = LogBizType.DEVICE)
    @PutMapping("/lock")
    public AjaxResult lock(@RequestParam(required = false) Long id, @RequestParam(required = false) String sn) {
        DeviceVO device = this.getDevice(id, sn);
        return executeWithLock(device, () -> success(deviceIotService.lock(device, true, "管理员锁车", true)));
    }

    @PreAuthorize("@ss.hasPermi('bst:device:ring')")
    @Log(title = "管理员响铃寻车", businessType = BusinessType.OTHER, bizIdName = "arg0", bizType = LogBizType.DEVICE)
    @PutMapping("/ring")
    public AjaxResult ring(@RequestParam(required = false) Long id, @RequestParam(required = false) String sn) {
        DeviceVO device = this.getDevice(id, sn);
        return executeWithLock(device,
                () -> success(deviceIotService.play(device, IotConstants.PLAY_WARNING, "管理员响铃寻车", true)));
    }

    @PreAuthorize("@ss.hasPermi('bst:device:reboot')")
    @Log(title = "管理员重启", businessType = BusinessType.OTHER, bizIdName = "arg0", bizType = LogBizType.DEVICE)
    @PutMapping("/reboot")
    public AjaxResult reboot(@RequestParam(required = false) Long id, @RequestParam(required = false) String sn) {
        DeviceVO device = this.getDevice(id, sn);
        return executeWithLock(device, () -> success(deviceIotService.reboot(device, "管理员重启", true)));
    }

    @PreAuthorize("@ss.hasPermi('bst:device:unlockSeat')")
    @Log(title = "管理员开坐垫锁", businessType = BusinessType.OTHER, bizIdName = "arg0", bizType = LogBizType.DEVICE)
    @PutMapping("/unlockSeat")
    public AjaxResult unlockSeat(@RequestParam(required = false) Long id, @RequestParam(required = false) String sn) {
        DeviceVO device = this.getDevice(id, sn);
        return executeWithLock(device, () -> success(deviceIotService.unlockSeat(device, "管理员开坐垫锁", true)));
    }

    @PreAuthorize("@ss.hasPermi('bst:device:refresh')")
    @Log(title = "管理员刷新", businessType = BusinessType.OTHER, bizIdName = "arg0", bizType = LogBizType.DEVICE)
    @PutMapping("/refresh")
    public AjaxResult refresh(@RequestParam(required = false) Long id, @RequestParam(required = false) String sn) {
        DeviceVO device = this.getDevice(id, sn);
        return executeWithLock(device, () -> {
            deviceIotService.refresh(device, IotConstants.ONLINE_TYPE_COMMAND);
            return success(device);
        });
    }

    @PreAuthorize("@ss.hasPermi('bst:device:music')")
    @Log(title = "管理员设置声音", businessType = BusinessType.OTHER, bizIdName = "arg0", bizType = LogBizType.DEVICE)
    @PutMapping("/music")
    public AjaxResult music(@RequestParam(required = false) Long id, @RequestParam(required = false) String sn,
            @RequestParam String music) {
        DeviceVO device = this.getDevice(id, sn);
        return executeWithLock(device, () -> success(deviceIotService.setMusic(device, music, "管理员设置声音", true)));
    }
}
