package com.ycwl.basic.repository;

import com.alibaba.fastjson.JSONObject;
import com.ycwl.basic.mapper.DeviceMapper;
import com.ycwl.basic.model.pc.device.entity.DeviceConfigEntity;
import com.ycwl.basic.model.pc.device.entity.DeviceEntity;
import com.ycwl.basic.utils.SnowFlakeUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;

import java.util.Date;
import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

@Component
public class DeviceRepository {
    @Autowired
    private DeviceMapper deviceMapper;
    @Autowired
    private RedisTemplate<String, String> redisTemplate;

    public static final String DEVICE_ONLINE_CACHE_KEY = "device:online_status:%s";
    public static final String DEVICE_CACHE_KEY = "device:%s";
    public static final String DEVICE_CONFIG_CACHE_KEY = "device:%s:config";

    public DeviceEntity getDevice(Long deviceId) {
        if (redisTemplate.hasKey(String.format(DEVICE_CACHE_KEY, deviceId))) {
            return JSONObject.parseObject(redisTemplate.opsForValue().get(String.format(DEVICE_CACHE_KEY, deviceId)), DeviceEntity.class);
        }
        DeviceEntity device = deviceMapper.getByDeviceId(deviceId);
        if (null != device) {
            redisTemplate.opsForValue().set(String.format(DEVICE_CACHE_KEY, deviceId), JSONObject.toJSONString(device));
        }
        return device;
    }

    public DeviceEntity getDeviceByDeviceNo(String deviceNo) {
        if (redisTemplate.hasKey(String.format(DEVICE_CACHE_KEY, deviceNo))) {
            return JSONObject.parseObject(redisTemplate.opsForValue().get(String.format(DEVICE_CACHE_KEY, deviceNo)), DeviceEntity.class);
        }
        DeviceEntity device = deviceMapper.getByDeviceNo(deviceNo);
        if (null == device) {
            device = deviceMapper.getByDeviceNo2(deviceNo);
        }
        if (null != device) {
            redisTemplate.opsForValue().set(String.format(DEVICE_CACHE_KEY, deviceNo), JSONObject.toJSONString(device));
            redisTemplate.opsForValue().set(String.format(DEVICE_CACHE_KEY, device.getId()), JSONObject.toJSONString(device));
        } else {
            redisTemplate.opsForValue().set(String.format(DEVICE_CACHE_KEY, deviceNo), "null", 60L, TimeUnit.SECONDS);
        }
        return device;
    }

    public DeviceConfigEntity getDeviceConfig(Long deviceId) {
        if (redisTemplate.hasKey(String.format(DEVICE_CONFIG_CACHE_KEY, deviceId))) {
            return JSONObject.parseObject(redisTemplate.opsForValue().get(String.format(DEVICE_CONFIG_CACHE_KEY, deviceId)), DeviceConfigEntity.class);
        }
        DeviceConfigEntity deviceConfig = deviceMapper.getConfigByDeviceId(deviceId);
        if (null == deviceConfig) {
            deviceConfig = new DeviceConfigEntity();
            deviceConfig.setId(SnowFlakeUtil.getLongId());
            deviceConfig.setDeviceId(deviceId);
            deviceMapper.addConfig(deviceConfig);
        }
        redisTemplate.opsForValue().set(String.format(DEVICE_CONFIG_CACHE_KEY, deviceId), JSONObject.toJSONString(deviceConfig));
        return deviceConfig;
    }

    public boolean clearDeviceCache(String deviceNo) {
        if (deviceNo == null) {
            return true;
        }
        if (redisTemplate.hasKey(String.format(DEVICE_CACHE_KEY, deviceNo))) {
            DeviceEntity device = getDeviceByDeviceNo(deviceNo);
            if (device != null) {
                redisTemplate.delete(String.format(DEVICE_CACHE_KEY, device.getNo()));
                clearDeviceCache(device.getId());
            } else {
                redisTemplate.delete(String.format(DEVICE_CACHE_KEY, deviceNo));
            }
        }
        redisTemplate.delete(String.format(DEVICE_CACHE_KEY, deviceNo));
        return true;
    }
    public boolean clearDeviceCache(Long deviceId) {
        if (redisTemplate.hasKey(String.format(DEVICE_CACHE_KEY, deviceId))) {
            DeviceEntity device = getDevice(deviceId);
            redisTemplate.delete(String.format(DEVICE_CACHE_KEY, device.getNo()));
            redisTemplate.delete(String.format(DEVICE_CONFIG_CACHE_KEY, device.getNo()));
        }
        redisTemplate.delete(String.format(DEVICE_CACHE_KEY, deviceId));
        redisTemplate.delete(String.format(DEVICE_CONFIG_CACHE_KEY, deviceId));
        return true;
    }

    public void updateOnlineStatus(String deviceNo, int online, Date keepaliveAt) {
        DeviceEntity device = getDeviceByDeviceNo(deviceNo);
        if (null == device) {
            return;
        }
        updateOnlineStatus(device.getId(), null, online, keepaliveAt);
    }

    public void updateOnlineStatus(Long deviceId, String ipAddr, int online, Date keepaliveAt) {
        DeviceEntity device = getDevice(deviceId);
        if (null == device) {
            return;
        }
        device.setOnline(online);
        device.setKeepaliveAt(keepaliveAt);
        device.setIpAddr(ipAddr);
        redisTemplate.opsForValue().set(String.format(DEVICE_ONLINE_CACHE_KEY, deviceId), JSONObject.toJSONString(device), 2, TimeUnit.DAYS);
//        deviceMapper.updateOnlineStatus(deviceId, ipAddr, online, keepaliveAt);
        updateDeviceCache(device);
    }

    public DeviceEntity getOnlineStatus(Long deviceId) {
        if (redisTemplate.hasKey(String.format(DEVICE_ONLINE_CACHE_KEY, deviceId))) {
            return JSONObject.parseObject(redisTemplate.opsForValue().get(String.format(DEVICE_ONLINE_CACHE_KEY, deviceId)), DeviceEntity.class);
        } else {
            return null;
        }
    }

    private void updateDeviceCache(DeviceEntity device) {
        redisTemplate.opsForValue().set(String.format(DEVICE_CACHE_KEY, device.getId()), JSONObject.toJSONString(device));
        redisTemplate.opsForValue().set(String.format(DEVICE_CACHE_KEY, device.getNo()), JSONObject.toJSONString(device));
    }

    public List<DeviceEntity> getAllDeviceByScenicId(Long scenicId) {
        List<Long> deviceIdList = deviceMapper.listAllByScenicId(scenicId);
        return deviceIdList.stream().map(this::getDevice).collect(Collectors.toList());
    }
}
