package com.fuyuanshen.web.service.device;

import cn.hutool.core.collection.CollectionUtil;
import com.alibaba.fastjson2.JSONArray;
import com.alibaba.fastjson2.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.fuyuanshen.app.domain.AppDeviceBindRecord;
import com.fuyuanshen.app.domain.AppDeviceShare;
import com.fuyuanshen.app.domain.dto.APPReNameDTO;
import com.fuyuanshen.app.domain.dto.AppRealTimeStatusDto;
import com.fuyuanshen.app.domain.dto.DeviceInstructDto;
import com.fuyuanshen.app.domain.vo.APPDeviceTypeVo;
import com.fuyuanshen.app.domain.vo.AppUserVo;
import com.fuyuanshen.app.mapper.AppDeviceBindRecordMapper;
import com.fuyuanshen.app.mapper.AppDeviceShareMapper;
import com.fuyuanshen.app.mapper.AppUserMapper;
import com.fuyuanshen.app.mapper.equipment.APPDeviceMapper;
import com.fuyuanshen.common.core.exception.ServiceException;
import com.fuyuanshen.common.core.utils.ObjectUtils;
import com.fuyuanshen.common.core.utils.StringUtils;
import com.fuyuanshen.common.mybatis.core.page.PageQuery;
import com.fuyuanshen.common.mybatis.core.page.TableDataInfo;
import com.fuyuanshen.common.redis.utils.RedisUtils;
import com.fuyuanshen.common.satoken.utils.AppLoginHelper;
import com.fuyuanshen.equipment.domain.Device;
import com.fuyuanshen.equipment.domain.dto.AppDeviceBo;
import com.fuyuanshen.equipment.domain.dto.InstructionRecordDto;
import com.fuyuanshen.equipment.domain.query.DeviceQueryCriteria;
import com.fuyuanshen.equipment.domain.vo.*;
import com.fuyuanshen.equipment.enums.BindingStatusEnum;
import com.fuyuanshen.equipment.enums.CommunicationModeEnum;
import com.fuyuanshen.equipment.mapper.DeviceLogMapper;
import com.fuyuanshen.equipment.mapper.DeviceMapper;
import com.fuyuanshen.global.mqtt.constants.DeviceRedisKeyConstants;
import com.fuyuanshen.web.service.device.status.base.DeviceStatusRule;
import com.fuyuanshen.web.service.device.status.base.RealTimeStatusEngine;
import com.google.common.primitives.Ints;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

import java.time.*;
import java.util.*;

import static com.fuyuanshen.common.core.constant.GlobalConstants.GLOBAL_REDIS_KEY;
import static com.fuyuanshen.global.mqtt.constants.DeviceRedisKeyConstants.*;


@Slf4j
@Service
@RequiredArgsConstructor
public class DeviceBizService {

    private final APPDeviceMapper appDeviceMapper;
    private final DeviceMapper deviceMapper;
    private final AppDeviceBindRecordMapper appDeviceBindRecordMapper;
    private final RealTimeStatusEngine realTimeStatusEngine;
    private final DeviceLogMapper deviceLogMapper;
    private final AppDeviceShareMapper appDeviceShareMapper;
    private final AppUserMapper appUserMapper;
    ;


    public List<APPDeviceTypeVo> getTypeList() {
        Long userId = AppLoginHelper.getUserId();
        return appDeviceMapper.getTypeList(userId);
    }

    public void reName(APPReNameDTO reNameDTO) {
        UpdateWrapper<Device> updateWrapper = new UpdateWrapper<>();
        updateWrapper.eq("id", reNameDTO.getId())
                .eq("binding_user_id", AppLoginHelper.getUserId())
                .set("device_name", reNameDTO.getDeviceName());
        deviceMapper.update(updateWrapper);
    }


    public TableDataInfo<AppDeviceVo> queryAppDeviceList(DeviceQueryCriteria bo, PageQuery pageQuery) {
        if (bo.getBindingUserId() == null) {
            Long userId = AppLoginHelper.getUserId();
            bo.setBindingUserId(userId);
        }
        Page<AppDeviceVo> result = deviceMapper.queryAppBindDeviceList(pageQuery.build(), bo);
        List<AppDeviceVo> records = result.getRecords();
        if (records != null && !records.isEmpty()) {
            records.forEach(item -> {
                if (item.getCommunicationMode() != null && (item.getCommunicationMode() == 0 || item.getCommunicationMode() == 2)) {

                    // 设备在线状态
                    String onlineStatus = RedisUtils.getCacheObject(GLOBAL_REDIS_KEY + DEVICE_KEY_PREFIX + item.getDeviceImei() + DeviceRedisKeyConstants.DEVICE_ONLINE_STATUS_KEY_PREFIX);
                    if ("1".equals(onlineStatus)) {
                        item.setOnlineStatus(1);
                    } else if ("2".equals(onlineStatus)) {
                        item.setOnlineStatus(2);
                    } else {
                        item.setOnlineStatus(0);
                    }

                    String deviceStatus = RedisUtils.getCacheObject(GLOBAL_REDIS_KEY + DEVICE_KEY_PREFIX + item.getDeviceImei() + DEVICE_STATUS_KEY_PREFIX);
                    // 获取电量
                    if (StringUtils.isNotBlank(deviceStatus)) {
                        JSONObject jsonObject = JSONObject.parseObject(deviceStatus);
                        item.setBattery(jsonObject.getString("batteryPercentage"));
                    } else {
                        item.setBattery("0");
                    }

                    String location = RedisUtils.getCacheObject(GLOBAL_REDIS_KEY + DEVICE_KEY_PREFIX + item.getDeviceImei() + DEVICE_LOCATION_KEY_PREFIX);
                    if (StringUtils.isNotBlank(location)) {
                        JSONObject jsonObject = JSONObject.parseObject(location);
                        item.setLatitude(jsonObject.getString("latitude"));
                        item.setLongitude(jsonObject.getString("longitude"));
                    }

                    String alarmStatus = RedisUtils.getCacheObject(GLOBAL_REDIS_KEY + DEVICE_KEY_PREFIX + item.getDeviceImei() + DEVICE_ALARM_KEY_PREFIX);
                    if (StringUtils.isNotBlank(alarmStatus)) {
                        item.setAlarmStatus(alarmStatus);
                    }
                }
            });
        }
        return TableDataInfo.build(result);
    }

    public TableDataInfo<WebDeviceVo> queryWebDeviceList(DeviceQueryCriteria bo, PageQuery pageQuery) {
        Page<WebDeviceVo> result = deviceMapper.queryWebDeviceList(pageQuery.build(), bo);
        List<WebDeviceVo> records = result.getRecords();
        if (records != null && !records.isEmpty()) {
            records.forEach(item -> {
                if (item.getCommunicationMode() != null && Ints.asList(0, 2).contains(item.getCommunicationMode())) {

                    // 设备在线状态
//                    String onlineStatus = RedisUtils.getCacheObject(GLOBAL_REDIS_KEY+ DEVICE_KEY_PREFIX+ item.getDeviceImei() + DeviceRedisKeyConstants.DEVICE_ONLINE_STATUS_KEY_PREFIX);
//                    if("1".equals(onlineStatus)){
//                        item.setOnlineStatus(1);
//                    }else if("2".equals(onlineStatus)){
//                        item.setOnlineStatus(2);
//                    }else{
//                        item.setOnlineStatus(0);
//                    }
                    String deviceStatus = RedisUtils.getCacheObject(GLOBAL_REDIS_KEY + DEVICE_KEY_PREFIX + item.getDeviceImei() + DEVICE_STATUS_KEY_PREFIX);
                    // 获取电量
                    if (StringUtils.isNotBlank(deviceStatus)) {
                        JSONObject jsonObject = JSONObject.parseObject(deviceStatus);
                        item.setBattery(jsonObject.getString("batteryPercentage"));
                    } else {
                        item.setBattery("0");
                    }

                    String location = RedisUtils.getCacheObject(GLOBAL_REDIS_KEY + DEVICE_KEY_PREFIX + item.getDeviceImei() + DEVICE_LOCATION_KEY_PREFIX);
                    if (StringUtils.isNotBlank(location)) {
                        JSONObject jsonObject = JSONObject.parseObject(location);
                        item.setLatitude(jsonObject.getString("latitude"));
                        item.setLongitude(jsonObject.getString("longitude"));
                    }

                    String alarmStatus = RedisUtils.getCacheObject(GLOBAL_REDIS_KEY + DEVICE_KEY_PREFIX + item.getDeviceImei() + DEVICE_ALARM_KEY_PREFIX);
                    if (StringUtils.isNotBlank(alarmStatus)) {
                        item.setAlarmStatus(alarmStatus);
                    }
                }
            });
        }
        return TableDataInfo.build(result);
    }


    public int bindDevice(AppDeviceBo bo) {
        Integer mode = bo.getCommunicationMode();
        Long userId = AppLoginHelper.getUserId();
        if (mode == CommunicationModeEnum.FOUR_G.getValue()) {

            String deviceImei = bo.getDeviceImei();
            QueryWrapper<Device> qw = new QueryWrapper<Device>()
                    .eq("device_imei", deviceImei);
            List<Device> devices = deviceMapper.selectList(qw);
            if (devices.isEmpty()) {
                throw new RuntimeException("请先将设备入库！！！");
            }
            Device device = devices.get(0);
            if (device.getBindingStatus() != null && device.getBindingStatus() == BindingStatusEnum.BOUND.getCode()) {
                throw new RuntimeException("设备已经被绑定！");
            }

            QueryWrapper<AppDeviceBindRecord> bindRecordQueryWrapper = new QueryWrapper<>();
            bindRecordQueryWrapper.eq("device_id", device.getId());
            bindRecordQueryWrapper.eq("communication_mode", 0);
            AppDeviceBindRecord appDeviceBindRecord = appDeviceBindRecordMapper.selectOne(bindRecordQueryWrapper);
            if (appDeviceBindRecord != null) {
                UpdateWrapper<AppDeviceBindRecord> deviceUpdateWrapper = new UpdateWrapper<>();
                deviceUpdateWrapper.eq("device_id", device.getId())
                        .set("binding_user_id", userId)
                        .set("communication_mode", 0)
                        .set("update_time", new Date())
                        .set("binding_time", new Date());
                return appDeviceBindRecordMapper.update(null, deviceUpdateWrapper);
            } else {
                AppDeviceBindRecord bindRecord = new AppDeviceBindRecord();
                bindRecord.setDeviceId(device.getId());
                bindRecord.setBindingUserId(userId);
                bindRecord.setBindingTime(new Date());
                bindRecord.setCommunicationMode(0);
                bindRecord.setCreateBy(userId);
                appDeviceBindRecordMapper.insert(bindRecord);
            }

            UpdateWrapper<Device> deviceUpdateWrapper = new UpdateWrapper<>();
            deviceUpdateWrapper.eq("id", device.getId())
                    .set("binding_status", BindingStatusEnum.BOUND.getCode())
                    .set("binding_user_id", userId)
                    .set("binding_time", new Date());
            return deviceMapper.update(null, deviceUpdateWrapper);
        } else if (mode == CommunicationModeEnum.BLUETOOTH.getValue()) {
            String deviceMac = bo.getDeviceMac();
            QueryWrapper<Device> qw = new QueryWrapper<Device>()
                    .eq("device_mac", deviceMac);
            List<Device> devices = deviceMapper.selectList(qw);
            if (devices.isEmpty()) {
                throw new RuntimeException("请先将设备入库！！！");
            }
            Device device = devices.get(0);

            QueryWrapper<AppDeviceBindRecord> bindRecordQueryWrapper = new QueryWrapper<>();
            bindRecordQueryWrapper.eq("device_id", device.getId());
            bindRecordQueryWrapper.eq("binding_user_id", userId);
            AppDeviceBindRecord appDeviceBindRecord = appDeviceBindRecordMapper.selectOne(bindRecordQueryWrapper);
            if (appDeviceBindRecord != null) {
                UpdateWrapper<AppDeviceBindRecord> deviceUpdateWrapper = new UpdateWrapper<>();
                deviceUpdateWrapper.eq("device_id", device.getId())
                        .eq("binding_user_id", userId)
                        .set("binding_user_id", userId)
                        .set("communication_mode", 1)
                        .set("binding_time", new Date());
                return appDeviceBindRecordMapper.update(null, deviceUpdateWrapper);
            } else {
                AppDeviceBindRecord bindRecord = new AppDeviceBindRecord();
                bindRecord.setDeviceId(device.getId());
                bindRecord.setBindingUserId(userId);
                bindRecord.setBindingTime(new Date());
                bindRecord.setCommunicationMode(1);
                bindRecord.setCreateBy(userId);
                appDeviceBindRecordMapper.insert(bindRecord);
            }

            UpdateWrapper<Device> deviceUpdateWrapper = new UpdateWrapper<>();
            deviceUpdateWrapper.eq("id", device.getId())
                    .set("binding_status", BindingStatusEnum.BOUND.getCode())
                    .set("binding_user_id", userId)
                    .set("binding_time", new Date());
            return deviceMapper.update(null, deviceUpdateWrapper);
        } else {
            throw new RuntimeException("通讯方式错误");
        }

    }


    public int unBindDevice(Long id) {
        return unBindDevice(id, null, 1);
    }

    public int unBindDevice(Long id, Long userId, int type) {
        Device device = deviceMapper.selectById(id);
        if (device == null) {
            throw new RuntimeException("请先将设备入库！！！");
        }

        // type: 0  app,1 web
        if (type == 0) {
            QueryWrapper<AppDeviceBindRecord> bindRecordQueryWrapper = new QueryWrapper<>();
            bindRecordQueryWrapper.eq("device_id", device.getId());
//            bindRecordQueryWrapper.eq("binding_user_id", userId);
            Long count = appDeviceBindRecordMapper.selectCount(bindRecordQueryWrapper);
            if (count == 0) {
                throw new RuntimeException("请先绑定设备！！！");
            }
            if (count <= 1) {
                UpdateWrapper<Device> deviceUpdateWrapper = new UpdateWrapper<>();
                deviceUpdateWrapper.eq("id", device.getId())
                        .set("binding_user_id", null)
                        .set("binding_status", BindingStatusEnum.UNBOUND.getCode())
                        .set("binding_time", null);
                deviceMapper.update(null, deviceUpdateWrapper);
            }
            QueryWrapper<AppDeviceBindRecord> brqWrapper = new QueryWrapper<>();
            brqWrapper.eq("device_id", device.getId());
            brqWrapper.eq("binding_user_id", userId);
            List<AppDeviceBindRecord> appDeviceBindRecordList = appDeviceBindRecordMapper.selectList(brqWrapper);
            if (CollectionUtil.isNotEmpty(appDeviceBindRecordList)) {
                appDeviceBindRecordList.forEach(appDeviceBindRecord ->
                        appDeviceBindRecordMapper.deleteById(appDeviceBindRecord.getId()));
            }
            AppUserVo appUserVo = appUserMapper.selectVoById(userId);
            if (appUserVo != null) {
                QueryWrapper<AppDeviceShare> appDeviceShareQueryWrapper = new QueryWrapper<>();
                appDeviceShareQueryWrapper.eq("device_id", device.getId());
                appDeviceShareQueryWrapper.eq("phonenumber", appUserVo.getPhonenumber());
                List<AppDeviceShare> appDeviceShareList = appDeviceShareMapper.selectList(appDeviceShareQueryWrapper);
                if (CollectionUtil.isNotEmpty(appDeviceShareList)) {
                    appDeviceShareList.forEach(appDeviceShare ->
                            appDeviceShareMapper.deleteById(appDeviceShare.getId()));
                }
            }
        } else {
            QueryWrapper<AppDeviceBindRecord> bindRecordQueryWrapper = new QueryWrapper<>();
            bindRecordQueryWrapper.eq("device_id", device.getId());
            Long count = appDeviceBindRecordMapper.selectCount(bindRecordQueryWrapper);
            if (count == 0) {
                throw new RuntimeException("请先绑定设备！！！");
            }
            UpdateWrapper<Device> deviceUpdateWrapper = new UpdateWrapper<>();
            deviceUpdateWrapper.eq("id", device.getId())
                    .set("binding_user_id", null)
                    .set("binding_status", BindingStatusEnum.UNBOUND.getCode())
                    .set("binding_time", null);
            deviceMapper.update(null, deviceUpdateWrapper);

            QueryWrapper<AppDeviceBindRecord> brqWrapper = new QueryWrapper<>();
            brqWrapper.eq("device_id", device.getId());
            List<AppDeviceBindRecord> appDeviceBindRecordList = appDeviceBindRecordMapper.selectList(brqWrapper);
            if (CollectionUtil.isNotEmpty(appDeviceBindRecordList)) {
                appDeviceBindRecordList.forEach(appDeviceBindRecord ->
                        appDeviceBindRecordMapper.deleteById(appDeviceBindRecord.getId()));
            }

            QueryWrapper<AppDeviceShare> appDeviceShareQueryWrapper = new QueryWrapper<>();
            appDeviceShareQueryWrapper.eq("device_id", device.getId());
            List<AppDeviceShare> appDeviceShareList = appDeviceShareMapper.selectList(appDeviceShareQueryWrapper);
            if (CollectionUtil.isNotEmpty(appDeviceShareList)) {
                appDeviceShareList.forEach(appDeviceShare ->
                        appDeviceShareMapper.deleteById(appDeviceShare.getId()));
            }
        }

        return 1;
    }

    public String mapReverseGeocoding(DeviceInstructDto params) {
//        Long deviceId = params.getDeviceId();
//        Device device = deviceMapper.selectById(deviceId);
        QueryWrapper<Device> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("device_imei", params.getDeviceImei());
        List<Device> devices = deviceMapper.selectList(queryWrapper);
        if (ObjectUtils.length(devices) == 0) {
            throw new ServiceException("设备不存在");
        }
        return RedisUtils.getCacheObject("device:location:" + devices.get(0).getDeviceImei());
    }

    public Map<String, Object> getRealTimeStatus(AppRealTimeStatusDto statusDto) {
        try {
//            String commandType = statusDto.getTypeName()+"_" + statusDto.getFunctionMode();"FunctionAccessBatchStatusRule"
            DeviceStatusRule rule = realTimeStatusEngine.getDeviceStatusRule(statusDto.getTypeName());
            if (rule == null) {
                throw new ServiceException("未匹配到处理命令");
            }
            return rule.getStatus(statusDto);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    public AppDeviceVo getDeviceInfo(String deviceMac) {
//        QueryWrapper<Device> queryWrapper = new QueryWrapper<>();
//        queryWrapper.eq("device_mac", deviceMac);
//        List<Device> devices = deviceMapper.selectList(queryWrapper);
        return deviceMapper.getDeviceInfo(deviceMac);
    }

    public TableDataInfo<InstructionRecordVo> getInstructionRecord(InstructionRecordDto bo, PageQuery pageQuery) {
        Page<InstructionRecordVo> result = deviceLogMapper.getInstructionRecord(pageQuery.build(), bo);
        return TableDataInfo.build(result);
    }

    public TableDataInfo<LocationHistoryVo> getLocationHistory(InstructionRecordDto bo, PageQuery pageQuery) {
        Page<LocationHistoryVo> result = deviceMapper.getLocationHistory(pageQuery.build(), bo);
        return TableDataInfo.build(result);
    }

    public List<LocationHistoryDetailVo> getLocationHistoryDetail(Long id) {
        Device device = deviceMapper.selectById(id);
        if (device == null) {
            throw new ServiceException("设备不存在");
        }

        // 计算七天前的凌晨时间戳
        LocalDateTime sevenDaysAgo = LocalDateTime.of(LocalDate.now().minusDays(7), LocalTime.MIDNIGHT);
        long startTime = sevenDaysAgo.atZone(ZoneId.systemDefault()).toInstant().toEpochMilli();

        // 计算今天的凌晨时间戳
        LocalDateTime today = LocalDateTime.of(LocalDate.now(), LocalTime.MAX);
        long endTime = today.atZone(ZoneId.systemDefault()).toInstant().toEpochMilli();

        String deviceImei = device.getDeviceImei();
        String a = GLOBAL_REDIS_KEY + DEVICE_KEY_PREFIX + deviceImei + DEVICE_LOCATION_KEY_PREFIX + ":history";
        Collection<String> list = RedisUtils.zRangeByScoreDesc(a, startTime, endTime);
        if (CollectionUtil.isEmpty(list)) {
            return null;
        }


        Map<String, List<JSONObject>> map = new LinkedHashMap<>();
        for (String obj : list) {
            JSONObject jsonObject = JSONObject.parseObject(obj);
            Long timestamp = jsonObject.getLong("timestamp");
            LocalDate date = LocalDateTime.ofInstant(Instant.ofEpochMilli(timestamp), ZoneId.systemDefault()).toLocalDate();
            String address = jsonObject.getString("address");
            if (StringUtils.isBlank(address) || "[]".equals(address)) {
                continue;
            }
            if (map.containsKey(date.toString())) {
                map.get(date.toString()).add(jsonObject);
            } else {
                ArrayList<JSONObject> jsonList = new ArrayList<>();
                jsonList.add(jsonObject);
                map.put(date.toString(), jsonList);
            }
        }

        List<LocationHistoryDetailVo> result = new ArrayList<>();
        for (Map.Entry<String, List<JSONObject>> entry : map.entrySet()) {
            LocationHistoryDetailVo detailVo = new LocationHistoryDetailVo();
            detailVo.setDate(entry.getKey());
            detailVo.setDeviceName(device.getDeviceName());
            detailVo.setStartLocation(entry.getValue().get(0).getString("address"));
            detailVo.setEndLocation(entry.getValue().get(entry.getValue().size() - 1).getString("address"));
            String jsonString = JSONArray.toJSONString(entry.getValue());
            List<Object> strings = JSONArray.parseArray(jsonString);
            detailVo.setDetailList(strings);
            result.add(detailVo);
        }


        return result;
    }

}
