package com.usky.meeting.service.impl;

import cn.hutool.core.lang.Snowflake;
import cn.hutool.core.util.IdUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.usky.common.core.bean.CommonPage;
import com.usky.common.core.exception.BusinessException;
import com.usky.common.core.util.UUIDUtils;
import com.usky.common.security.utils.SecurityUtils;
import com.usky.iot.RemoteIotTaskService;
import com.usky.meeting.domain.*;
import com.usky.meeting.mapper.*;
import com.usky.meeting.repository.MeetingRoomRepository;
import com.usky.meeting.service.MeetingDeviceService;
import com.usky.common.mybatis.core.AbstractCrudService;
import com.usky.meeting.service.MeetingRoomService;
import com.usky.meeting.service.vo.MeetingDeviceBindFacePersonQueryVO;
import com.usky.meeting.service.vo.MeetingDeviceBindFacePersonVO;
import com.usky.meeting.service.vo.MeetingDeviceBindPersonRequest;
import com.usky.meeting.service.vo.MeetingDeviceRequestVO;
import net.bytebuddy.implementation.bytecode.Throw;
import org.aspectj.weaver.patterns.ThisOrTargetAnnotationPointcut;
import org.bouncycastle.jcajce.provider.symmetric.AES;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.util.Optionals;
import org.springframework.stereotype.Repository;
import org.springframework.stereotype.Service;

import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;

/**
 * <p>
 * 设备表 服务实现类
 * </p>
 *
 * @author zyj
 * @since 2024-03-08
 */
@Service
public class MeetingDeviceServiceImpl extends AbstractCrudService<MeetingDeviceMapper, MeetingDevice> implements MeetingDeviceService {
    @Autowired
    private MeetingRoomMapper meetingRoomMapper;
    @Autowired
    private EgDeviceMapper egDeviceMapper;
    // @Autowired
    // private RemoteIotTaskService remoteIotTaskService;
    @Autowired
    private MeetingRoomRepository dmMeetingRoomRepository;
    @Autowired
    private MeetingDeviceMapper deviceMapper;
    @Autowired
    private MeetingDeviceHeartbeatMapper heartbeatMapper;
    @Autowired
    private MeetingTemplateMapper meetingTemplateMapper;
    @Autowired
    private MeetingDevicePersonBindMapper meetingDevicePersonBindMapper;
    @Autowired
    private SysPersonMapper sysPersonMapper;

    @Override
    public CommonPage<MeetingDevice> meetingDeviceList(MeetingDeviceRequestVO requestVO) {
        LocalDateTime now = LocalDateTime.now();
        Integer current = requestVO.getCurrent();
        Integer size = requestVO.getSize();
        IPage<MeetingDevice> page = new Page<>(current, size);

        String deviceCode = requestVO.getDeviceCode();
        Integer tenantId;

        if (StringUtils.isNotBlank(deviceCode)) {
            LambdaQueryWrapper<MeetingDevice> queryWrapper = new LambdaQueryWrapper<>();
            queryWrapper.eq(MeetingDevice::getDeviceCode, requestVO.getDeviceCode());
            tenantId = deviceMapper.selectOne(queryWrapper).getTenantId();
        } else {
            tenantId = SecurityUtils.getTenantId();
        }

        // 根据输入所属会议室名称刷选
        List<Long> roomIdList = new ArrayList<>();
        List<MeetingRoom> meetingRoomsList = null;
        LambdaQueryWrapper<MeetingRoom> queryWrapper = Wrappers.lambdaQuery();
        if (StringUtils.isNotBlank(requestVO.getRoomName())) {
            queryWrapper.like(MeetingRoom::getRoomName, requestVO.getRoomName()).eq(MeetingRoom::getTenantId, tenantId);
            meetingRoomsList = meetingRoomMapper.selectList(queryWrapper);
            if (CollectionUtils.isNotEmpty(meetingRoomsList)) {
                for (int i = 0; i < meetingRoomsList.size(); i++) {
                    roomIdList.add(meetingRoomsList.get(i).getRoomId());
                }
            }
        } else {
            queryWrapper.eq(MeetingRoom::getTenantId, tenantId);
            meetingRoomsList = meetingRoomMapper.selectList(queryWrapper);
        }
        // 根据输入所属门禁设备名称刷选
        List<Integer> egDeviceIdList = new ArrayList<>();
        List<EgDevice> egDeviceList = null;
        LambdaQueryWrapper<EgDevice> queryWrapper1 = Wrappers.lambdaQuery();
        if (StringUtils.isNotBlank(requestVO.getEgDeviceName())) {
            queryWrapper1.like(EgDevice::getDeviceName, requestVO.getEgDeviceName()).eq(EgDevice::getTenantId, tenantId);
            egDeviceList = egDeviceMapper.selectList(queryWrapper1);
            if (CollectionUtils.isNotEmpty(egDeviceList)) {
                for (EgDevice egDevice : egDeviceList) {
                    egDeviceIdList.add(egDevice.getId());
                }
            }
        } else {
            queryWrapper1.eq(EgDevice::getTenantId, tenantId);
            egDeviceList = egDeviceMapper.selectList(queryWrapper1);
        }

        LambdaQueryWrapper<MeetingDevice> queryWrapper3 = Wrappers.lambdaQuery();
        queryWrapper3.eq(requestVO.getDeviceCode() != null, MeetingDevice::getDeviceCode, requestVO.getDeviceCode())
                .eq(requestVO.getDeviceStatus() != null, MeetingDevice::getDeviceStatus, requestVO.getDeviceStatus())
                .eq(requestVO.getDeviceDirection() != null, MeetingDevice::getDeviceDirection, requestVO.getDeviceDirection())
                .like(StringUtils.isNotBlank(requestVO.getDeviceName()), MeetingDevice::getDeviceName, requestVO.getDeviceName())
                .like(StringUtils.isNotBlank(requestVO.getNameplate()), MeetingDevice::getNameplate, requestVO.getNameplate())
                .like(StringUtils.isNotBlank(requestVO.getMaintainer()), MeetingDevice::getMaintainer, requestVO.getMaintainer())
                .like(StringUtils.isNotBlank(requestVO.getContacts()), MeetingDevice::getContacts, requestVO.getContacts())
                .like(StringUtils.isNotBlank(requestVO.getRemark()), MeetingDevice::getRemark, requestVO.getRemark())
                .like(requestVO.getRoomId() != null, MeetingDevice::getRoomId, requestVO.getRoomId())
                .in(CollectionUtils.isNotEmpty(roomIdList), MeetingDevice::getRoomId, roomIdList)
                .in(CollectionUtils.isNotEmpty(egDeviceIdList), MeetingDevice::getEgDeviceId, egDeviceIdList)
                .eq(MeetingDevice::getTenantId, tenantId)
                .orderByAsc(MeetingDevice::getDeviceUuid);
        page = this.page(page, queryWrapper3);

        LambdaQueryWrapper<MeetingTemplate> queryWrapper4 = Wrappers.lambdaQuery();
        queryWrapper4.eq(MeetingTemplate::getTenantId, tenantId);
        List<MeetingTemplate> meetingTemplateList = meetingTemplateMapper.selectList(queryWrapper4);

        if (!page.getRecords().isEmpty()) {
            // 1. 提取设备编码并过滤空值
            List<String> validDeviceCodes = page.getRecords().stream()
                    .map(MeetingDevice::getDeviceCode)
                    .filter(Objects::nonNull)
                    .collect(Collectors.toList());

            // 2. 批量查询心跳信息并构建Map
            Map<String, LocalDateTime> deviceHeartbeatMap;
            if (CollectionUtils.isEmpty(validDeviceCodes)) {
                deviceHeartbeatMap = Collections.emptyMap();
            } else {
                deviceHeartbeatMap = heartbeatMapper.selectList(Wrappers.<MeetingDeviceHeartbeat>lambdaQuery()
                                .in(MeetingDeviceHeartbeat::getDeviceCode, validDeviceCodes))
                        .stream()
                        .collect(Collectors.toMap(
                                MeetingDeviceHeartbeat::getDeviceCode,
                                MeetingDeviceHeartbeat::getCreateTime,
                                (existing, replacement) -> existing
                        ));
            }

            // 3. 设备状态赋值
            page.getRecords().forEach(meetingDevice -> {
                String deviceCode1 = meetingDevice.getDeviceCode();
                boolean isHeartbeatValid = Objects.nonNull(deviceCode1)
                        && deviceHeartbeatMap.containsKey(deviceCode1)
                        && now.isBefore(deviceHeartbeatMap.get(deviceCode1).plusMinutes(5));
                meetingDevice.setDeviceStatus(isHeartbeatValid ? 1 : 0);
            });

            // 4. 通用映射关联数据
            mapAssociatedData(page.getRecords(),
                    MeetingDevice::getRoomId,
                    buildIdMap(meetingRoomsList, MeetingRoom::getRoomId),
                    MeetingDevice::setMeetingRoom);

            mapAssociatedData(page.getRecords(),
                    MeetingDevice::getEgDeviceId,
                    buildIdMap(egDeviceList, EgDevice::getId),
                    MeetingDevice::setEgDevice);

            mapAssociatedData(page.getRecords(),
                    MeetingDevice::getTemplateId,
                    buildIdMap(meetingTemplateList, MeetingTemplate::getId),
                    MeetingDevice::setMeetingTemplate);

            // 5. 填充绑定人员
            fillBindPerson(page.getRecords());
        }

        return new CommonPage<>(page.getRecords(), page.getTotal(), size, current);
    }

    /**
     * 根据设备UUID集合查询 meeting_device_person_bind，将 person_id 按逗号拼接后写入 bindPerson
     */
    private void fillBindPerson(List<MeetingDevice> devices) {
        if (CollectionUtils.isEmpty(devices)) {
            return;
        }
        List<Long> deviceUuids = devices.stream()
                .map(MeetingDevice::getDeviceUuid)
                .filter(Objects::nonNull)
                .distinct()
                .collect(Collectors.toList());
        if (deviceUuids.isEmpty()) {
            return;
        }
        List<MeetingDevicePersonBind> bindList = meetingDevicePersonBindMapper.selectByDeviceUuids(deviceUuids);
        Map<Long, List<String>> devicePersonMap = new HashMap<>();
        if (!CollectionUtils.isEmpty(bindList)) {
            for (MeetingDevicePersonBind bind : bindList) {
                if (bind.getDeviceUuid() == null || bind.getPersonId() == null) {
                    continue;
                }
                devicePersonMap
                        .computeIfAbsent(bind.getDeviceUuid(), k -> new ArrayList<>())
                        .add(String.valueOf(bind.getPersonId()));
            }
        }
        for (MeetingDevice device : devices) {
            if (device == null || device.getDeviceUuid() == null) {
                continue;
            }
            List<String> personIds = devicePersonMap.get(device.getDeviceUuid());
            if (personIds != null && !personIds.isEmpty()) {
                device.setBindPerson(String.join(",", personIds));
            } else {
                device.setBindPerson("");
            }
        }
    }

    /**
     * 构建ID到对象的Map，用于快速查找
     * @param dataList 待处理的列表
     * @param idExtractor ID提取器
     * @return ID到对象的Map
     */
    private <T, ID> Map<ID, T> buildIdMap(List<T> dataList, java.util.function.Function<T, ID> idExtractor) {
        if (CollectionUtils.isEmpty(dataList)) {
            return Collections.emptyMap();
        }
        return dataList.stream()
                .filter(Objects::nonNull)
                .collect(Collectors.toMap(
                        idExtractor,
                        t -> t,
                        (existing, replacement) -> existing
                ));
    }

    /**
     * 通用关联数据映射方法
     * @param mainList 主列表（MeetingDevice列表）
     * @param keyExtractor 主对象的关联ID提取器
     * @param dataMap 关联数据的ID->对象Map
     * @param setter 主对象的关联数据设置器
     */
    private <T, ID, D> void mapAssociatedData(List<T> mainList,
                                              java.util.function.Function<T, ID> keyExtractor,
                                              Map<ID, D> dataMap,
                                              java.util.function.BiConsumer<T, D> setter) {
        if (CollectionUtils.isEmpty(mainList) || CollectionUtils.isEmpty(dataMap)) {
            return;
        }
        mainList.forEach(item -> {
            ID key = keyExtractor.apply(item);
            if (Objects.nonNull(key) && dataMap.containsKey(key)) {
                setter.accept(item, dataMap.get(key));
            }
        });
    }

    @Override
    public void add(MeetingDevice meetingDevice) {
        if (checkNameUnique(meetingDevice)) {
            throw new BusinessException("新增会议室设备失败，" + meetingDevice.getDeviceName() + "已经存在");
        }
//        Snowflake snowflake = IdUtil.createSnowflake(1,1);
//        meetingDevice.setDeviceId(snowflake.nextId());
        LocalDateTime now = LocalDateTime.now();
        meetingDevice.setCreateBy(SecurityUtils.getUsername());
        meetingDevice.setCreateTime(now);
        meetingDevice.setTenantId(SecurityUtils.getTenantId());

        String ipAddr = meetingDevice.getIpAddr();
        LambdaQueryWrapper<MeetingDeviceHeartbeat> queryWrapper = Wrappers.lambdaQuery();
        queryWrapper.eq(MeetingDeviceHeartbeat::getIpAddr, ipAddr);
        MeetingDeviceHeartbeat meetingDeviceHeartbeat = heartbeatMapper.selectOne(queryWrapper);
        if (meetingDeviceHeartbeat == null) {
            throw new BusinessException("设备离线!请重新输入");
        } else {
            LocalDateTime createTime = meetingDeviceHeartbeat.getCreateTime();
            if (createTime.isAfter(now) || !createTime.plusMinutes(6).isAfter(now)) {
                throw new BusinessException("设备离线!请重新输入");
            }
        }
        meetingDevice.setDeviceCode(meetingDeviceHeartbeat.getDeviceCode());

        this.save(meetingDevice);

        // remoteIotTaskService.addDeviceInfo("505_USKY", meetingDevice.getDeviceUuid().toString(), "", meetingDevice.getDeviceName(), meetingDevice.getInstallAddress(), 1);
    }

    @Override
    public void edit(MeetingDevice meetingDevice) {
        LocalDateTime now = LocalDateTime.now();
        if (checkNameUnique(meetingDevice)) {
            throw new BusinessException("修改会议室设备失败，" + meetingDevice.getDeviceName() + "已经存在");
        }
        meetingDevice.setUpdateBy(SecurityUtils.getUsername());
        meetingDevice.setUpdateTime(now);

        String ipAddr = meetingDevice.getIpAddr();
        LambdaQueryWrapper<MeetingDeviceHeartbeat> queryWrapper = Wrappers.lambdaQuery();
        queryWrapper.eq(MeetingDeviceHeartbeat::getIpAddr, ipAddr);
        MeetingDeviceHeartbeat meetingDeviceHeartbeat = heartbeatMapper.selectOne(queryWrapper);
        if (meetingDeviceHeartbeat == null) {
            throw new BusinessException("设备离线!请重新输入");
        } else {
            LocalDateTime createTime = meetingDeviceHeartbeat.getCreateTime();
            if (createTime.isAfter(now) || !createTime.plusMinutes(6).isAfter(now)) {
                throw new BusinessException("设备离线!请重新输入");
            }
        }
        meetingDevice.setDeviceCode(meetingDeviceHeartbeat.getDeviceCode());

        this.updateById(meetingDevice);
    }

    @Override
    public void remove(Long deviceId) {
        MeetingDevice meetingDevice = this.getById(deviceId);
        Optional.ofNullable(meetingDevice).orElseThrow(() -> new BusinessException("该会议室设备不存在"));
        this.removeById(deviceId);

        // remoteIotTaskService.deleteDeviceInfo(deviceId.toString());
    }

    @Override
    public void bindPerson(MeetingDeviceBindPersonRequest request) {
        if (request == null || request.getDeviceUuid() == null) {
            throw new BusinessException("设备UUID不能为空");
        }
        Long deviceUuid = request.getDeviceUuid();
        MeetingDevice device = this.getById(deviceUuid);
        Optional.ofNullable(device).orElseThrow(() -> new BusinessException("会议设备信息不存在"));

        // 覆盖式：先清空原有绑定
        meetingDevicePersonBindMapper.deleteByDeviceUuid(deviceUuid);

        String personIds = request.getPersonIds();
        // 空字符串视为解除全部授权
        if (!StringUtils.isNotBlank(personIds)) {
            return;
        }

        String[] personIdArr = personIds.split(",");
        for (String personIdStr : personIdArr) {
            if (!StringUtils.isNotBlank(personIdStr)) {
                continue;
            }
            MeetingDevicePersonBind bind = new MeetingDevicePersonBind();
            bind.setDeviceUuid(deviceUuid);
            bind.setPersonId(Integer.parseInt(personIdStr.trim()));
            bind.setIsLoginNotify(0);
            meetingDevicePersonBindMapper.insert(bind);
        }
    }

    @Override
    public CommonPage<MeetingDeviceBindFacePersonVO> pageBindFacePersons(MeetingDeviceBindFacePersonQueryVO queryVO) {
        Integer cur = queryVO.getCurrent();
        Integer sz = queryVO.getSize();
        IPage<SysPerson> page = new Page<>(cur, sz);

        List<Long> deviceUuids = new ArrayList<>();
        if (StringUtils.isNotBlank(queryVO.getDeviceCode())) {
            MeetingDevice tempDevice = deviceMapper.selectOne(Wrappers.<MeetingDevice>lambdaQuery()
                    .eq(MeetingDevice::getDeviceCode, queryVO.getDeviceCode()));
            if (tempDevice == null) {
                return new CommonPage<>(new ArrayList<>(), 0L, sz, cur);
            }
            MeetingDevice device = deviceMapper.selectOne(Wrappers.<MeetingDevice>lambdaQuery()
                    .eq(MeetingDevice::getDeviceCode, queryVO.getDeviceCode())
                    .eq(MeetingDevice::getTenantId, tempDevice.getTenantId()));
            if (device == null || device.getDeviceUuid() == null) {
                return new CommonPage<>(new ArrayList<>(), 0L, sz, cur);
            }
            deviceUuids.add(device.getDeviceUuid());
        } else {
            List<MeetingDevice> devices = deviceMapper.selectList(Wrappers.<MeetingDevice>lambdaQuery()
                    .eq(MeetingDevice::getTenantId, SecurityUtils.getTenantId()));
            if (!CollectionUtils.isEmpty(devices)) {
                for (MeetingDevice d : devices) {
                    if (d != null && d.getDeviceUuid() != null) {
                        deviceUuids.add(d.getDeviceUuid());
                    }
                }
            }
        }

        if (deviceUuids.isEmpty()) {
            return new CommonPage<>(new ArrayList<>(), 0L, sz, cur);
        }

        List<MeetingDevicePersonBind> bindList = meetingDevicePersonBindMapper.selectByDeviceUuids(deviceUuids);
        LinkedHashSet<Integer> personIdSet = new LinkedHashSet<>();
        if (!CollectionUtils.isEmpty(bindList)) {
            for (MeetingDevicePersonBind b : bindList) {
                if (b.getPersonId() != null) {
                    personIdSet.add(b.getPersonId());
                }
            }
        }
        if (personIdSet.isEmpty()) {
            return new CommonPage<>(new ArrayList<>(), 0L, sz, cur);
        }
        List<Integer> personIds = new ArrayList<>(personIdSet);

        LambdaQueryWrapper<SysPerson> personWrapper = Wrappers.lambdaQuery();
        personWrapper.in(SysPerson::getId, personIds);
        if (StringUtils.isNotBlank(queryVO.getFullName())) {
            personWrapper.like(SysPerson::getFullName, queryVO.getFullName());
        }
        Integer faceStatusFilter = queryVO.getFaceStatus();
        if (faceStatusFilter != null) {
            personWrapper.eq(SysPerson::getFaceStatus, faceStatusFilter.byteValue());
        }
        personWrapper.orderByAsc(SysPerson::getId);
        page = sysPersonMapper.selectPage(page, personWrapper);

        List<SysPerson> records = page.getRecords();
        List<Integer> pagePersonIds = new ArrayList<>();
        for (SysPerson p : records) {
            if (p != null && p.getId() != null) {
                pagePersonIds.add(p.getId());
            }
        }
        Map<Integer, Long> personUserIdMap = buildPersonUserIdMap(pagePersonIds);
        List<MeetingDeviceBindFacePersonVO> rows = new ArrayList<>(records.size());
        for (SysPerson p : records) {
            MeetingDeviceBindFacePersonVO vo = new MeetingDeviceBindFacePersonVO();
            vo.setId(p.getId());
            vo.setUserId(personUserIdMap.get(p.getId()));
            vo.setFullName(p.getFullName());
            vo.setAge(p.getAge());
            vo.setGender(p.getGender());
            vo.setLinkPhone(p.getLinkPhone());
            vo.setFaceBase(p.getFaceBase());
            vo.setVefNum(p.getVefNum());
            vo.setFaceStatus(p.getFaceStatus());
            vo.setCardNum(p.getCardNum());
            rows.add(vo);
        }
        return new CommonPage<>(rows, page.getTotal(), sz, cur);
    }

    private Map<Integer, Long> buildPersonUserIdMap(List<Integer> personIds) {
        Map<Integer, Long> map = new HashMap<>();
        if (CollectionUtils.isEmpty(personIds)) {
            return map;
        }
        List<Map<String, Object>> rows = sysPersonMapper.selectUserIdsByPersonIds(personIds);
        if (CollectionUtils.isEmpty(rows)) {
            return map;
        }
        for (Map<String, Object> row : rows) {
            if (row == null) {
                continue;
            }
            Object personIdObj = row.get("personId");
            Object userIdObj = row.get("userId");
            if (personIdObj == null || userIdObj == null) {
                continue;
            }
            map.put(Integer.valueOf(personIdObj.toString()), Long.valueOf(userIdObj.toString()));
        }
        return map;
    }

    public boolean checkNameUnique(MeetingDevice meetingDevice) {
        Long deviceId = null == meetingDevice.getDeviceUuid() ? -1 : meetingDevice.getDeviceUuid();
        LambdaQueryWrapper<MeetingDevice> queryWrapper = Wrappers.lambdaQuery();
        queryWrapper.eq(MeetingDevice::getDeviceName, meetingDevice.getDeviceName())
                .eq(MeetingDevice::getTenantId, SecurityUtils.getTenantId());
        MeetingDevice one = this.getOne(queryWrapper);
        return null != one && !Objects.equals(one.getDeviceUuid(), deviceId);
    }
}
