package net.northking.cctp.device.api.activity;


import cn.gjing.tools.auth.annotation.RequiredPermissions;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import net.northking.cctp.common.cache.EntityNameCache;
import net.northking.cctp.common.db.Pagination;
import net.northking.cctp.common.exception.PlatformRuntimeException;
import net.northking.cctp.common.http.QueryByPage;
import net.northking.cctp.common.http.ResultWrapper;
import net.northking.cctp.common.security.authentication.NKSecurityContext;
import net.northking.cctp.device.api.activity.service.DeviceActivityApiService;
import net.northking.cctp.device.api.mobile.service.CdMobileDeviceApiService;
import net.northking.cctp.device.api.pcDevice.service.CdPcDeviceApiService;
import net.northking.cctp.device.constants.DeviceConstants;
import net.northking.cctp.device.constants.DeviceError;
import net.northking.cctp.device.db.entity.CdDeviceToken;
import net.northking.cctp.device.db.entity.CdDeviceUsageLog;
import net.northking.cctp.device.db.service.CdDeviceTokenService;
import net.northking.cctp.device.dto.device.CdMobileDeviceExitDto;
import net.northking.cctp.device.dto.device.DeviceInfoDto;
import net.northking.cctp.device.dto.device.DeviceLogDto;
import net.northking.cctp.device.dto.pcDevice.CdDeviceRemoteDto;
import net.northking.cctp.device.dto.pcDevice.CdPcDeviceExitDto;
import net.northking.cctp.device.dto.pcDevice.CdPcDeviceRemoteDto;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.integration.redis.util.RedisLockRegistry;
import org.springframework.util.ObjectUtils;
import org.springframework.web.bind.annotation.*;

import javax.servlet.http.HttpServletRequest;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;

@Api(tags = "设备活动管理")
@RequestMapping("/v1")
@RestController
public class DeviceActivityCtrl {

    private final static Logger logger = LoggerFactory.getLogger(DeviceActivityCtrl.class);

    @Autowired
    private DeviceActivityApiService apiService;

    @Autowired
    private CdMobileDeviceApiService cdMobileDeviceApiService;

    @Autowired
    private CdPcDeviceApiService cdPcDeviceApiService;

    @Autowired
    private CdDeviceTokenService cdDeviceTokenService;

    @Autowired
    private RedisLockRegistry redisLockRegistry;

    @Autowired
    private EntityNameCache entityNameCache;

    @ApiOperation(value = "申请使用移动设备")
    @RequiredPermissions("ui-achieveMobDevice")
    @GetMapping(value = "/device/mobile/achieve/{uuid}",
            produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
    public ResultWrapper<DeviceInfoDto> remoteMobDevice(@PathVariable("uuid") String uuid, HttpServletRequest request) {
        ResultWrapper<DeviceInfoDto> wrapper = new ResultWrapper<>();
        CdDeviceRemoteDto remoteDto = new CdDeviceRemoteDto();
        remoteDto.setId(NKSecurityContext.getUserId());
        remoteDto.setDeviceId(uuid);
        remoteDto.setUserBy(DeviceConstants.USE_BY_USER);
        DeviceInfoDto device = this.cdMobileDeviceApiService.achieveMobDevice(remoteDto, request);
        return wrapper.success(device);
    }

    @ApiOperation(value = "申请使用PC设备")
    @RequiredPermissions({"ui-achievePCDevice","ui-sassMobileDeviceRemoteControl","ui-sassPCDeviceRemoteControl"})
    @GetMapping(value = "/device/pc/achieve/{uuid}")
    public ResultWrapper<CdPcDeviceRemoteDto> remotePcDevice(@PathVariable("uuid") String uuid, HttpServletRequest request) {
        ResultWrapper<CdPcDeviceRemoteDto> wrapper = new ResultWrapper<>();
        CdDeviceRemoteDto dto = new CdDeviceRemoteDto();
        dto.setDeviceId(uuid);
        dto.setId(NKSecurityContext.getUserId());
        dto.setUserBy(DeviceConstants.USE_BY_USER);
        CdPcDeviceRemoteDto device = this.cdPcDeviceApiService.achieve(dto, request);
        return wrapper.success(device);
    }

    @ApiOperation(value = "结束使用PC设备")
    @PostMapping(value = "/device/pc/release",
            produces = MediaType.APPLICATION_JSON_UTF8_VALUE,
            consumes = MediaType.APPLICATION_JSON_UTF8_VALUE)
    public ResultWrapper<String> release(@RequestBody CdPcDeviceExitDto dto) {
        ResultWrapper<String> wrapper = new ResultWrapper<>();
        String result = this.apiService.releasePcDevice(dto);
        return wrapper.success(result);
    }

    @ApiOperation(value = "结束使用移动设备")
    @PostMapping(value = "/device/mobile/release",
            produces = MediaType.APPLICATION_JSON_UTF8_VALUE,
            consumes = MediaType.APPLICATION_JSON_UTF8_VALUE)
    public ResultWrapper<String> release(@RequestBody CdMobileDeviceExitDto dto) {
        ResultWrapper<String> wrapper = new ResultWrapper<>();
        String result = this.apiService.releaseMobDevice(dto);
        return wrapper.success(result);
    }

    @ApiOperation(value = "根据设备锁获取设备信息")
    @GetMapping(value = "/device/info/{token}",
            produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
    public ResultWrapper<DeviceInfoDto> getInfoFromToken(@PathVariable("token") String token) {
        ResultWrapper<DeviceInfoDto> wrapper = new ResultWrapper<>();
        DeviceInfoDto result = this.apiService.getInfoFromToken(token);
        return wrapper.success(result);
    }

    @ApiOperation(value = "使用设备记录")
    @RequiredPermissions({"ui-deviceUsageLog","ui-DeviceUsageLogs","ui-sassUseRecord","ui-PCrecord"})
    @PostMapping(value = "/paging/query/device/usage-log",
            produces = MediaType.APPLICATION_JSON_UTF8_VALUE,
            consumes = MediaType.APPLICATION_JSON_UTF8_VALUE)
    public ResultWrapper<Pagination<CdDeviceUsageLog>> getUsageLog(
            @RequestBody QueryByPage<DeviceLogDto> queryByPage){
        ResultWrapper<Pagination<CdDeviceUsageLog>> wrapper = new ResultWrapper<>();
        Pagination<CdDeviceUsageLog> usageLog = this.apiService.getUsageLog(queryByPage);
        return wrapper.success(usageLog);
    }


    @ApiOperation(value = "feign:根据设备锁获取设备信息")
    @PostMapping(value = "/query/deviceInfo")
    public List<Map<String, Object>> queryDeviceInfo(@RequestBody Set<String> deviceList){
        List<Map<String, Object>> mapList = this.apiService.queryDeviceInfo(deviceList);
        return mapList;
    }

    @ApiOperation(value = "查询移动设备的系统类型")
    @PostMapping(value = "/query/mob/device/platform")
    public Map<String,Object> queryMobDevice(@RequestBody List<String> deviceList){
        return this.apiService.queryMobDevicePlatform(deviceList);
    }

    @ApiOperation(value = "feign:直播申请设备的token")
    @GetMapping(value = "/query/device/token/{deviceId}")
    public String queryDeviceToken(@PathVariable("deviceId") String deviceId){
        String token = this.apiService.queryDeviceToken(deviceId);
        return token;
    }

    @ApiOperation(value = "脚本申请使用PC设备")
    @RequiredPermissions("ui-saveScript")
    @GetMapping(value = "/script/achieve/pc/device/{uuid}")
    public ResultWrapper<CdPcDeviceRemoteDto> achieve(@PathVariable("uuid") String uuid,@RequestParam("projectId")String projectId, HttpServletRequest request) {
        ResultWrapper<CdPcDeviceRemoteDto> wrapper = new ResultWrapper<>();
        CdPcDeviceRemoteDto device = new CdPcDeviceRemoteDto();
        Lock lock = redisLockRegistry.obtain(DeviceConstants.PC_DEIVCE_LOCK + uuid);
        try {
            if (lock.tryLock(5000, TimeUnit.MILLISECONDS)) {
                logger.info("tryLock:pc:device:{} ------------------------------------------> start", uuid);
                CdDeviceRemoteDto dto = new CdDeviceRemoteDto();
                dto.setDeviceId(uuid);
                dto.setId(NKSecurityContext.getUserId());
                dto.setUserBy(DeviceConstants.USE_BY_USER);
                device = this.apiService.scriptPcDevice(dto, projectId, request);
                logger.info("tryLock:pc:device:{} ------------------------------------------> end",uuid);
            }
        } catch (PlatformRuntimeException e) {
            throw e;
        } catch (InterruptedException e) {
            logger.error("脚本申请PC设备锁异常:", e);
            Thread.currentThread().interrupt();
            throw new PlatformRuntimeException(DeviceError.REMOTE_FAIL);
        } finally {
            lock.unlock();
        }
        return wrapper.success(device);
    }


    @ApiOperation(value = "脚本申请使用移动设备")
    @RequiredPermissions("ui-saveScript")
    @GetMapping(value = "/script/achieve/mobile/device/{uuid}",
            produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
    public ResultWrapper<DeviceInfoDto> remoteStart(@PathVariable("uuid") String uuid,@RequestParam("projectId")String projectId, HttpServletRequest request) {
        ResultWrapper<DeviceInfoDto> wrapper = new ResultWrapper<>();
        DeviceInfoDto device = new DeviceInfoDto();
        Lock lock = redisLockRegistry.obtain(DeviceConstants.MOB_DEVICE_LOCK + uuid);
        try {
            if (lock.tryLock(5000,TimeUnit.MILLISECONDS)) {
                logger.info("tryLock:mobile:device:{} ------------------------------------------> start",uuid);
                CdDeviceRemoteDto remoteDto = new CdDeviceRemoteDto();
                remoteDto.setId(NKSecurityContext.getUserId());
                remoteDto.setDeviceId(uuid);
                remoteDto.setUserBy(DeviceConstants.USE_BY_USER);
                device = this.apiService.scriptMobDevice(remoteDto, projectId, request);
                logger.info("tryLock:mobile:device:{} ------------------------------------------> end",uuid);
            }
        } catch (PlatformRuntimeException e) {
            throw e;
        } catch (InterruptedException e) {
            logger.error("脚本申请PC设备锁异常:", e);
            Thread.currentThread().interrupt();
            throw new PlatformRuntimeException(DeviceError.REMOTE_FAIL);
        } finally {
            lock.unlock();
        }
        return wrapper.success(device);
    }

    @ApiOperation(value = "释放移动设备(private)")
    @RequiredPermissions("ui-sassRelease")
    @GetMapping(value = "/exit/mobile/device/{uuid}")
    public ResultWrapper<String> exitMobDevice(@PathVariable("uuid") String uuid){
        ResultWrapper<String> wrapper = new ResultWrapper<>();
        CdDeviceToken token = cdDeviceTokenService.findByPrimaryKey(uuid);
        if (!ObjectUtils.isEmpty(token)){
            CdMobileDeviceExitDto dto = new CdMobileDeviceExitDto();
            dto.setDeviceId(uuid);
            dto.setToken(token.getToken());
            apiService.releaseMobDevice(dto);
        }
        return wrapper.success("true");
    }

    @ApiOperation(value = "释放pc设备(private)")
    @RequiredPermissions("ui-PCrelease")
    @GetMapping(value = "/exit/pc/device/{uuid}")
    public ResultWrapper<String> exitPcDevice(@PathVariable("uuid") String uuid){
        ResultWrapper<String> wrapper = new ResultWrapper<>();
        CdDeviceToken token = cdDeviceTokenService.findByPrimaryKey(uuid);
        if (!ObjectUtils.isEmpty(token)){
            CdPcDeviceExitDto dto = new CdPcDeviceExitDto();
            dto.setDeviceId(uuid);
            dto.setToken(token.getToken());
            apiService.releasePcDevice(dto);
        }
        return wrapper.success("true");
    }
}
