package com.usky.ems.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.usky.common.security.utils.SecurityUtils;
import com.usky.ems.domain.*;
import com.usky.ems.mapper.*;
import com.usky.ems.service.EmsModelService;
import com.usky.ems.service.vo.DmpGatewayDetailResponse;
import com.usky.ems.service.vo.EnergyTypeWrapperProductVO;
import com.usky.ems.service.vo.EmsAttributePointVO;
import com.usky.ems.service.vo.EmsEnergyTypeVO;
import com.usky.ems.service.vo.EmsGatewayDeviceTreeNode;
import com.usky.ems.service.vo.EmsModelSaveRequest;
import com.usky.ems.service.vo.SimpleProductVO;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Collections;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;

/**
 * 基础建模服务实现（结构树、能源类型、建筑/区域/网关/通道/设备/属性点位 CRUD）
 * 空间：base_space；建筑/区域实体：base_build、base_area；关联：base_space_build、base_space_area、base_space_gateway；网关：dmp_gateway
 */
@Service
public class EmsModelServiceImpl implements EmsModelService {

    /** 热循环，建模与概览页均不展示 */
    private static final int EXCLUDED_ENERGY_TYPE = 5;

    @Autowired
    private BaseSpaceMapper baseSpaceMapper;
    @Autowired
    private BaseBuildMapper baseBuildMapper;
    @Autowired
    private BaseAreaMapper baseAreaMapper;
    @Autowired
    private BaseSpaceBuildMapper baseSpaceBuildMapper;
    @Autowired
    private BaseSpaceAreaMapper baseSpaceAreaMapper;
    @Autowired
    private BaseSpaceGatewayMapper baseSpaceGatewayMapper;
    @Autowired
    private DmpGatewayMapper dmpGatewayMapper;
    @Autowired
    private EmsChannelMapper emsChannelMapper;
    @Autowired
    private EmsDeviceMapper emsDeviceMapper;
    @Autowired
    private EmsDeviceFunctionMapper emsDeviceFunctionMapper;
    @Autowired
    private DmpProductMapper dmpProductMapper;
    @Autowired
    private EmsProductEnergyTypeMapper emsProductEnergyTypeMapper;
    @Autowired
    private DmpDeviceMapper dmpDeviceMapper;
    @Autowired
    private DmpDeviceStatusMapper dmpDeviceStatusMapper;
    @Autowired
    private BaseSpaceServiceImpl baseSpaceService;
    @Autowired
    private EmsEnergyItemCodeMapper emsEnergyItemCodeMapper;

    private static final int SPACE_TYPE_PROJECT = 1;
    private static final int SPACE_TYPE_REGION = 2;
    private static final int SPACE_TYPE_BUILDING = 3;

    /** base_build.create_by / update_by 为 varchar(25) */
    private static String trimAuditUser(String username) {
        if (username == null) {
            return null;
        }
        return username.length() > 25 ? username.substring(0, 25) : username;
    }

//    private EmsProject firstProject() {
//        List<EmsProject> list = emsProjectMapper.selectList(null);
//        return list.isEmpty() ? null : list.get(0);
//    }
//
//    @Override
//    public EmsStructureTreeNode getStructureTree(Long projectId, Boolean includeGateway) {
//        EmsProject project = projectId != null ? emsProjectMapper.selectById(projectId) : firstProject();
//        if (project == null) {
//            return null;
//        }
//        EmsStructureTreeNode root = new EmsStructureTreeNode();
//        root.setId(project.getId());
//        root.setName(project.getName());
//        root.setType("project");
//        root.setChildren(buildSpaceChildren(project.getSpaceId(), includeGateway == null || includeGateway));
//        return root;
//    }
//
//    private List<EmsStructureTreeNode> buildSpaceChildren(Long parentId, boolean includeGateway) {
//        if (parentId == null) {
//            return Collections.emptyList();
//        }
//        List<BaseSpace> list = baseSpaceMapper.selectList(new LambdaQueryWrapper<BaseSpace>().eq(BaseSpace::getParentId, parentId));
//        List<EmsStructureTreeNode> children = new ArrayList<>();
//        for (BaseSpace s : list) {
//            EmsStructureTreeNode node = new EmsStructureTreeNode();
//            node.setId(s.getId());
//            node.setName(s.getName());
//            if (s.getType() != null && s.getType() == SPACE_TYPE_BUILDING) {
//                node.setType("building");
//                BaseSpaceBuild link = baseSpaceBuildMapper.selectOne(
//                        new LambdaQueryWrapper<BaseSpaceBuild>().eq(BaseSpaceBuild::getSpaceId, s.getId()).last("LIMIT 1"));
//                if (link != null && link.getBuildId() != null) {
//                    BaseBuild b = baseBuildMapper.selectById(link.getBuildId());
//                    if (b != null && b.getBuildName() != null) {
//                        node.setName(b.getBuildName());
//                    }
//                }
//            } else if (s.getType() != null && s.getType() == SPACE_TYPE_REGION) {
//                node.setType("region");
//                BaseSpaceArea sa = baseSpaceAreaMapper.selectOne(
//                        new LambdaQueryWrapper<BaseSpaceArea>().eq(BaseSpaceArea::getSpaceId, s.getId()).last("LIMIT 1"));
//                if (sa != null && sa.getAreaId() != null) {
//                    BaseArea a = baseAreaMapper.selectById(sa.getAreaId());
//                    if (a != null && a.getName() != null) {
//                        node.setName(a.getName());
//                    }
//                }
//            } else {
//                node.setType("space");
//            }
//            List<EmsStructureTreeNode> sub = buildSpaceChildren(s.getId(), includeGateway);
//            if (includeGateway && (s.getType() == null || s.getType() != SPACE_TYPE_BUILDING)) {
//                List<BaseSpaceGateway> links = baseSpaceGatewayMapper.selectList(
//                        new LambdaQueryWrapper<BaseSpaceGateway>().eq(BaseSpaceGateway::getSpaceId, s.getId()));
//                for (BaseSpaceGateway link : links) {
//                    if (link.getGatewayUuid() == null) {
//                        continue;
//                    }
//                    DmpGateway g = dmpGatewayMapper.selectOne(
//                            new LambdaQueryWrapper<DmpGateway>().eq(DmpGateway::getDeviceUuid, link.getGatewayUuid()).last("LIMIT 1"));
//                    if (g == null) {
//                        continue;
//                    }
//                    EmsStructureTreeNode gw = new EmsStructureTreeNode();
//                    gw.setId(g.getDeviceUuid());
//                    gw.setName(g.getName());
//                    gw.setType("gateway");
//                    sub.add(gw);
//                }
//            }
//            node.setChildren(sub);
//            children.add(node);
//        }
//        return children;
//    }

    @Override
    public List<EmsEnergyTypeVO> getEnergyTypeList() {
        List<DmpProduct> products = dmpProductMapper.selectList(
                Wrappers.<DmpProduct>lambdaQuery()
                        .eq(DmpProduct::getTenantId, SecurityUtils.getTenantId())
                        .eq(DmpProduct::getDeleteFlag, 0)
        );
        if (products.isEmpty()) {
            return Collections.emptyList();
        }

        List<Long> productIds = products.stream()
                .map(DmpProduct::getId)
                .filter(Objects::nonNull)
                .map(Integer::longValue)
                .collect(Collectors.toList());
        if (productIds.isEmpty()) {
            return Collections.emptyList();
        }

        List<EmsProductEnergyType> energyTypeList = emsProductEnergyTypeMapper.selectList(
                Wrappers.<EmsProductEnergyType>lambdaQuery()
                        .in(EmsProductEnergyType::getProductId, productIds)
        );
        if (energyTypeList.isEmpty()) {
            return Collections.emptyList();
        }

        Map<Integer, EmsEnergyItemCode> rootItemByEnergyType = emsEnergyItemCodeMapper.selectList(
                Wrappers.<EmsEnergyItemCode>lambdaQuery()
                        .eq(EmsEnergyItemCode::getParentCode, "0")
        ).stream()
                .filter(item -> item.getEnergyType() != null)
                .collect(Collectors.toMap(EmsEnergyItemCode::getEnergyType, item -> item, (a, b) -> a));

        AtomicInteger sortOrder = new AtomicInteger(0);
        return energyTypeList.stream()
                .map(EmsProductEnergyType::getEnergyType)
                .filter(Objects::nonNull)
                .filter(type -> type != EXCLUDED_ENERGY_TYPE)
                .distinct()
                .sorted()
                .map(type -> {
                    EmsEnergyItemCode itemCode = rootItemByEnergyType.get(type);
                    if (itemCode == null) {
                        return null;
                    }
                    EmsEnergyTypeVO vo = new EmsEnergyTypeVO();
                    vo.setId(type.longValue());
                    vo.setName(itemCode.getName());
                    vo.setCode(itemCode.getIdentifier());
                    vo.setUnit(StringUtils.isNotBlank(itemCode.getUnit()) ? itemCode.getUnit() : energyTypeUnit(type));
                    vo.setSortOrder(sortOrder.getAndIncrement());
                    return vo;
                })
                .filter(Objects::nonNull)
                .collect(Collectors.toList());
    }

    private static String energyTypeUnit(Integer energyType) {
        if (energyType == null) {
            return "";
        }
        switch (energyType) {
            case 1:
                return "kWh";
            case 2:
                return "m³";
            case 3:
            case 4:
                return "kWh";
            default:
                return "";
        }
    }

    @Override
    public List<EnergyTypeWrapperProductVO> showAssociatedEnergyTypes() {
        List<DmpProduct> products = dmpProductMapper.selectList(
                new LambdaQueryWrapper<DmpProduct>()
                        .eq(DmpProduct::getDeleteFlag, 0)
                        .eq(DmpProduct::getTenantId, SecurityUtils.getTenantId())
        );
        if (products == null || products.isEmpty()) {
            return Collections.emptyList();
        }

        Map<Integer, List<SimpleProductVO>> energyMaps = products.stream()
                .collect(Collectors.groupingBy(
                        DmpProduct::getDeviceType,
                        Collectors.mapping(item -> new SimpleProductVO(
                                item.getProductCode(),
                                item.getProductName()
                        ), Collectors.toList())
                ));

        List<EnergyTypeWrapperProductVO> result = new ArrayList<>();
        energyMaps.forEach((type, list) -> {
            String typeName = deviceTypeName(type);
            EnergyTypeWrapperProductVO vo = new EnergyTypeWrapperProductVO(type, typeName);
            vo.setProductList(list);
            result.add(vo);
        });

        result.sort((o1, o2) -> {
            if (o1.getEnergyType() == null) {
                return -1;
            }
            if (o2.getEnergyType() == null) {
                return 1;
            }
            return o1.getEnergyType().compareTo(o2.getEnergyType());
        });
        return result;
    }

    private String deviceTypeName(Integer deviceType) {
        if (deviceType == null) {
            return "";
        }
        switch (deviceType) {
            case 501:
                return "监控系统";
            case 502:
                return "门禁系统";
            case 503:
                return "梯控系统";
            case 504:
                return "机房系统";
            case 509:
                return "环境系统";
            case 510:
                return "照明系统";
            default:
                return "";
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Long createBuilding(EmsModelSaveRequest request) {
        Long parentSpaceId = request.getParentSpaceId();
        LocalDateTime now = LocalDateTime.now();
        String auditUser = trimAuditUser(SecurityUtils.getUsername());
        Integer tenantId = SecurityUtils.getTenantId();
        BaseBuild build = new BaseBuild();
        build.setBuildName(request.getBuildName());
        build.setAddress(request.getAddress());
        build.setAboveFloor(request.getAboveFloor());
        build.setUnderFloor(request.getUnderFloor());
        build.setBuildArea(request.getBuildArea());
        build.setCoverArea(request.getCoverArea());
        build.setBuildHigh(request.getBuildHigh());
        build.setCompleteYear(request.getCompleteYear());
        build.setBuildDesc(request.getBuildDesc());
        build.setLongitude(request.getLongitude());
        build.setLatitude(request.getLatitude());
        build.setTenantId(tenantId);
        build.setCreateBy(auditUser);
        build.setUpdateBy(auditUser);
        build.setCreateTime(now);
        build.setUpdateTime(now);
        baseBuildMapper.insert(build);

        BaseSpace space = new BaseSpace();
        space.setName(request.getBuildName());
        space.setParentId(request.getParentSpaceId());
        space.setType(SPACE_TYPE_BUILDING);
        BaseSpace parent = parentSpaceId != null ? baseSpaceMapper.selectById(parentSpaceId) : null;
        space.setRootId(parent != null ? parent.getRootId() : parentSpaceId);
        space.setTenantId(tenantId);
        space.setCreatedBy(auditUser);
        space.setUpdatedBy(auditUser);
        space.setCreateTime(now);
        space.setUpdateTime(now);
        if(parent.getParentId() == 0) {
            space.setPath(String.valueOf(parentSpaceId));
        }else{
            space.setPath(parent.getPath()+"/"+parentSpaceId);
        }

        space.setDeep(parent.getDeep()+1);
        baseSpaceMapper.insert(space);

        BaseSpaceBuild rel = new BaseSpaceBuild();
        rel.setSpaceId(space.getId());
        rel.setBuildId(build.getId());
        rel.setCreatedBy(auditUser);
        rel.setCreatedTime(now);
        baseSpaceBuildMapper.insert(rel);
        return space.getId();
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateBuilding(Long id, EmsModelSaveRequest request) {
        String name = request.getBuildName();
        BaseSpace space = baseSpaceMapper.selectById(id);
        if (space != null && name != null) {
            space.setName(name);
            space.setUpdatedBy(SecurityUtils.getUsername());
            space.setUpdateTime(LocalDateTime.now());
            baseSpaceMapper.updateById(space);
        }
        BaseSpaceBuild b = baseSpaceBuildMapper.selectOne(new LambdaQueryWrapper<BaseSpaceBuild>().eq(BaseSpaceBuild::getSpaceId, id).last("LIMIT 1"));
        if (b != null && b.getBuildId() != null && name != null) {
            BaseBuild build = baseBuildMapper.selectById(b.getBuildId());
            if (build != null) {
                if(StringUtils.isNotBlank(request.getBuildName())){
                    build.setBuildName(request.getBuildName());
                }
                if(StringUtils.isNotBlank(request.getAddress())){
                    build.setAddress(request.getAddress());
                }
                if(request.getAboveFloor() != null){
                    build.setAboveFloor(request.getAboveFloor());
                }
                if(request.getUnderFloor() != null){
                    build.setUnderFloor(request.getUnderFloor());
                }
                if(request.getBuildArea() != null){
                    build.setBuildArea(request.getBuildArea());
                }
                if(request.getCoverArea() != null){
                    build.setCoverArea(request.getCoverArea());
                }
                if(request.getBuildHigh() != null){
                    build.setBuildHigh(request.getBuildHigh());
                }
                if(request.getCompleteYear() != null){
                    build.setCompleteYear(request.getCompleteYear());
                }
                build.setBuildDesc(request.getBuildDesc());
                build.setLongitude(request.getLongitude());
                build.setLatitude(request.getLatitude());
                build.setUpdateBy(trimAuditUser(SecurityUtils.getUsername()));
                build.setUpdateTime(LocalDateTime.now());
                baseBuildMapper.updateById(build);
            }
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void deleteBuilding(Long id) {
        List<BaseSpaceBuild> links = baseSpaceBuildMapper.selectList(
                new LambdaQueryWrapper<BaseSpaceBuild>().eq(BaseSpaceBuild::getSpaceId, id));
        for (BaseSpaceBuild link : links) {
            if (link.getBuildId() != null) {
                baseBuildMapper.deleteById(link.getBuildId());
            }
        }
        baseSpaceBuildMapper.delete(new LambdaQueryWrapper<BaseSpaceBuild>().eq(BaseSpaceBuild::getSpaceId, id));
        baseSpaceMapper.deleteById(id);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Long createRegion(EmsModelSaveRequest request) {
        Long parentSpaceId = request.getParentSpaceId();
        String name = request.getName();
        LocalDateTime now = LocalDateTime.now();
        Integer tenantId = SecurityUtils.getTenantId();
        String auditUser = trimAuditUser(SecurityUtils.getUsername());

        BaseArea area = new BaseArea();
        area.setName(name);
        area.setType(request.getType());
        if (request.getArea() != null) {
            area.setArea(request.getArea());
        }
        if(request.getCommonArea() != null) {
            area.setCommonArea(request.getCommonArea());
        }
        if(request.getAirConditionedArea() != null) {
            area.setAirConditionedArea(request.getAirConditionedArea());
        }
        if(request.getResidentPopulation() != null) {
            area.setResidentPopulation(request.getResidentPopulation());
        }
        area.setCreatedBy(auditUser);
        area.setUpdatedBy(auditUser);
        area.setCreateTime(now);
        area.setUpdateTime(now);
        area.setTenantId(tenantId);
        baseAreaMapper.insert(area);

        BaseSpace space = new BaseSpace();
        space.setName(name);
        space.setParentId(parentSpaceId);
        BaseSpace parent = parentSpaceId != null ? baseSpaceMapper.selectById(parentSpaceId) : null;
        space.setRootId(parent != null ? parent.getRootId() : parentSpaceId);
        space.setType(SPACE_TYPE_REGION);
        if(parent.getParentId() == 0) {
            space.setPath(String.valueOf(parentSpaceId));
        }else{
            space.setPath(parent.getPath()+"/"+parentSpaceId);
        }
        space.setDeep(parent.getDeep()+1);
        space.setCreatedBy(auditUser);
        space.setUpdatedBy(auditUser);
        space.setCreateTime(now);
        space.setUpdateTime(now);
        space.setTenantId(tenantId);
        baseSpaceMapper.insert(space);

        BaseSpaceArea rel = new BaseSpaceArea();
        rel.setSpaceId(space.getId());
        rel.setAreaId(area.getId());
        rel.setCreatedBy(auditUser);
        rel.setCreatedTime(now);
        baseSpaceAreaMapper.insert(rel);
        return space.getId();
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateRegion(Long id, EmsModelSaveRequest request) {
        String name = request.getName();
        BaseSpace space = baseSpaceMapper.selectById(id);
        if (space != null && name != null) {
            space.setName(name);
            space.setUpdateTime(LocalDateTime.now());
            baseSpaceMapper.updateById(space);
        }
        BaseSpaceArea sa = baseSpaceAreaMapper.selectOne(new LambdaQueryWrapper<BaseSpaceArea>().eq(BaseSpaceArea::getSpaceId, id).last("LIMIT 1"));
        if (sa != null && sa.getAreaId() != null) {
            BaseArea area = baseAreaMapper.selectById(sa.getAreaId());
            if (area != null) {
                if (name != null) {
                    area.setName(name);
                }

                if(request.getType() != null) {
                    area.setType(request.getType());
                }

                if (request.getArea() != null) {
                    area.setArea(request.getArea());
                }
                if(request.getCommonArea() != null) {
                    area.setCommonArea(request.getCommonArea());
                }
                if(request.getAirConditionedArea() != null) {
                    area.setAirConditionedArea(request.getAirConditionedArea());
                }
                if(request.getResidentPopulation() != null) {
                    area.setResidentPopulation(request.getResidentPopulation());
                }
                area.setUpdateTime(LocalDateTime.now());
                baseAreaMapper.updateById(area);
            }
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void deleteRegion(Long id) {
        BaseSpaceArea sa = baseSpaceAreaMapper.selectOne(new LambdaQueryWrapper<BaseSpaceArea>().eq(BaseSpaceArea::getSpaceId, id).last("LIMIT 1"));
        baseSpaceAreaMapper.delete(new LambdaQueryWrapper<BaseSpaceArea>().eq(BaseSpaceArea::getSpaceId, id));
        if (sa != null && sa.getAreaId() != null) {
            baseAreaMapper.deleteById(sa.getAreaId());
        }
        baseSpaceMapper.deleteById(id);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public String createGateway(EmsModelSaveRequest request) {
        Long spaceId = request.getSpaceId();
        String name = request.getName();
        String uuid = UUID.randomUUID().toString().replace("-", "");
        if (uuid.length() > 50) {
            uuid = uuid.substring(0, 50);
        }
        LocalDateTime now = LocalDateTime.now();
        DmpGateway g = new DmpGateway();
        g.setDeviceUuid(uuid);
        g.setName(name != null ? name : "gateway");
        g.setCommStatus(0);
        g.setVirtualDevice(0);
        g.setCreateTime(now);
        g.setUpdateTime(now);
        dmpGatewayMapper.insert(g);

        if (spaceId != null) {
            BaseSpaceGateway rel = new BaseSpaceGateway();
            rel.setSpaceId(spaceId);
            rel.setGatewayUuid(uuid);
            rel.setCreatedTime(now);
            baseSpaceGatewayMapper.insert(rel);
        }
        return uuid;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateGateway(String id, EmsModelSaveRequest request) {
        DmpGateway g = dmpGatewayMapper.selectOne(
                new LambdaQueryWrapper<DmpGateway>().eq(DmpGateway::getDeviceUuid, id).last("LIMIT 1"));
        if (g == null) {
            return;
        }
        if (request.getName() != null) {
            g.setName(request.getName());
        }
        g.setUpdateTime(LocalDateTime.now());
        dmpGatewayMapper.updateById(g);

        if (request.getSpaceId() != null) {
            baseSpaceGatewayMapper.delete(new LambdaQueryWrapper<BaseSpaceGateway>().eq(BaseSpaceGateway::getGatewayUuid, id));
            BaseSpaceGateway rel = new BaseSpaceGateway();
            rel.setSpaceId(request.getSpaceId());
            rel.setGatewayUuid(id);
            rel.setCreatedTime(LocalDateTime.now());
            baseSpaceGatewayMapper.insert(rel);
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void deleteGateway(String id) {
        baseSpaceGatewayMapper.delete(new LambdaQueryWrapper<BaseSpaceGateway>().eq(BaseSpaceGateway::getGatewayUuid, id));
        dmpGatewayMapper.delete(new LambdaQueryWrapper<DmpGateway>().eq(DmpGateway::getDeviceUuid, id));
    }

//    @Override
//    @Transactional(rollbackFor = Exception.class)
//    public Long createChannel(EmsModelSaveRequest request) {
//        String gatewayId = request.getGatewayId();
//        String name = request.getName();
//        EmsChannel c = new EmsChannel();
//        c.setGatewayId(gatewayId);
//        c.setName(name);
//        c.setChannelTypeId(request.getChannelTypeId());
//        emsChannelMapper.insert(c);
//        return c.getId();
//    }
//
//    @Override
//    @Transactional(rollbackFor = Exception.class)
//    public void updateChannel(Long id, EmsModelSaveRequest request) {
//        EmsChannel c = emsChannelMapper.selectById(id);
//        if (c == null) {
//            return;
//        }
//        if (request.getName() != null) {
//            c.setName(request.getName());
//        }
//        if (request.getGatewayId() != null) {
//            c.setGatewayId(request.getGatewayId());
//        }
//        emsChannelMapper.updateById(c);
//    }
//
//    @Override
//    @Transactional(rollbackFor = Exception.class)
//    public void deleteChannel(Long id) {
//        emsChannelMapper.deleteById(id);
//    }
//
//    @Override
//    @Transactional(rollbackFor = Exception.class)
//    public String createDevice(EmsModelSaveRequest request) {
//        Long channelId = request.getChannelId();
//        String name = request.getName();
//        String devId = "DV" + System.currentTimeMillis();
//        if (devId.length() > 13) {
//            devId = devId.substring(0, 13);
//        }
//        EmsDevice d = new EmsDevice();
//        d.setId(devId);
//        d.setName(name);
//        d.setProjectId(0L);
//        d.setChannelId(channelId != null ? channelId : 0L);
//        d.setGatewayId("");
//        EmsChannel ch = channelId != null ? emsChannelMapper.selectById(channelId) : null;
//        if (ch != null) {
//            d.setGatewayId(ch.getGatewayId());
//        }
//        d.setProductId(0L);
//        d.setInstallationLocation(0L);
//        d.setMonitoringLocation(0L);
//        d.setCommAddress("");
//        d.setVirtualDevice(0);
//        d.setFocus(0);
//        d.setDeviceSystem(0);
//        d.setStatus(1);
//        d.setCommStatus(0);
//        emsDeviceMapper.insert(d);
//        return d.getId();
//    }
//
//    @Override
//    @Transactional(rollbackFor = Exception.class)
//    public void updateDevice(String id, EmsModelSaveRequest request) {
//        EmsDevice d = emsDeviceMapper.selectById(id);
//        if (d == null) {
//            return;
//        }
//        if (request.getName() != null) {
//            d.setName(request.getName());
//        }
//        if (request.getChannelId() != null) {
//            d.setChannelId(request.getChannelId());
//        }
//        emsDeviceMapper.updateById(d);
//    }
//
//    @Override
//    @Transactional(rollbackFor = Exception.class)
//    public void deleteDevice(String id) {
//        emsDeviceMapper.deleteById(id);
//    }
//
//    @Override
//    @Transactional(rollbackFor = Exception.class)
//    public Long createAttributePoint(EmsModelSaveRequest request) {
//        String deviceId = request.getDeviceId();
//        String name = request.getName();
//        String code = request.getCode();
//        EmsDeviceFunction f = new EmsDeviceFunction();
//        f.setDeviceId(deviceId);
//        f.setName(name);
//        f.setIdentifier(code != null ? code : name);
//        f.setProductId(0L);
//        f.setProductTemplateId(0L);
//        f.setPreservable(1);
//        f.setBindingAcq(0);
//        emsDeviceFunctionMapper.insert(f);
//        return f.getId();
//    }
//
//    @Override
//    @Transactional(rollbackFor = Exception.class)
//    public void updateAttributePoint(Long id, EmsModelSaveRequest request) {
//        EmsDeviceFunction f = emsDeviceFunctionMapper.selectById(id);
//        if (f == null) {
//            return;
//        }
//        if (request.getName() != null) {
//            f.setName(request.getName());
//        }
//        if (request.getCode() != null) {
//            f.setIdentifier(request.getCode());
//        }
//        emsDeviceFunctionMapper.updateById(f);
//    }
//
//    @Override
//    @Transactional(rollbackFor = Exception.class)
//    public void deleteAttributePoint(Long id) {
//        emsDeviceFunctionMapper.deleteById(id);
//    }

    @Override
    public List<EmsGatewayDeviceTreeNode> getGatewayDeviceTree(Long spaceId, String keyword) {
        LambdaQueryWrapper<DmpGateway> gwWrapper = new LambdaQueryWrapper<>();
        if (spaceId != null) {
            List<BaseSpaceGateway> links = baseSpaceGatewayMapper.selectList(
                    new LambdaQueryWrapper<BaseSpaceGateway>().eq(BaseSpaceGateway::getSpaceId, spaceId));
            Set<String> uuids = links.stream()
                    .map(BaseSpaceGateway::getGatewayUuid)
                    .filter(Objects::nonNull)
                    .collect(Collectors.toSet());
            if (uuids.isEmpty()) {
                return Collections.emptyList();
            }
            gwWrapper.in(DmpGateway::getDeviceUuid, uuids);
        }
        if (keyword != null && !keyword.trim().isEmpty()) {
            gwWrapper.like(DmpGateway::getName, keyword.trim());
        }
        List<DmpGateway> gateways = dmpGatewayMapper.selectList(gwWrapper);

        List<EmsGatewayDeviceTreeNode> result = new ArrayList<>();
        for (DmpGateway gw : gateways) {
            EmsGatewayDeviceTreeNode gwNode = new EmsGatewayDeviceTreeNode();
            gwNode.setId(gw.getDeviceUuid());
            gwNode.setName(gw.getName());
            gwNode.setType("gateway");

            List<EmsChannel> channels = emsChannelMapper.selectList(
                    new LambdaQueryWrapper<EmsChannel>().eq(EmsChannel::getGatewayId, gw.getDeviceUuid()));
            List<EmsGatewayDeviceTreeNode> channelNodes = new ArrayList<>();
            for (EmsChannel ch : channels) {
                EmsGatewayDeviceTreeNode chNode = new EmsGatewayDeviceTreeNode();
                chNode.setId(ch.getId());
                chNode.setName(ch.getName());
                chNode.setType("channel");

                List<EmsDevice> devices = emsDeviceMapper.selectList(
                        new LambdaQueryWrapper<EmsDevice>().eq(EmsDevice::getChannelId, ch.getId()));
                List<EmsGatewayDeviceTreeNode> deviceNodes = new ArrayList<>();
                for (EmsDevice dev : devices) {
                    EmsGatewayDeviceTreeNode devNode = new EmsGatewayDeviceTreeNode();
                    devNode.setId(dev.getId());
                    devNode.setName(dev.getName());
                    devNode.setType("device");
                    deviceNodes.add(devNode);
                }

                if (!deviceNodes.isEmpty()) {
                    chNode.setChildren(deviceNodes);
                    channelNodes.add(chNode);
                }
            }

            if (!channelNodes.isEmpty()) {
                gwNode.setChildren(channelNodes);
                result.add(gwNode);
            }
        }
        return result;
    }

    @Override
    public List<EmsAttributePointVO> getAttributePointList(String deviceId) {
        if (deviceId == null || deviceId.trim().isEmpty()) {
            return Collections.emptyList();
        }
        List<EmsDeviceFunction> functions = emsDeviceFunctionMapper.selectList(
                new LambdaQueryWrapper<EmsDeviceFunction>().eq(EmsDeviceFunction::getDeviceId, deviceId));
        List<EmsAttributePointVO> result = new ArrayList<>();
        for (EmsDeviceFunction f : functions) {
            EmsAttributePointVO vo = new EmsAttributePointVO();
            vo.setId(f.getId());
            vo.setProductId(f.getProductId());
            vo.setProductTemplateId(f.getProductTemplateId());
            vo.setDeviceId(f.getDeviceId());
            vo.setName(f.getName());
            vo.setIdentifier(f.getIdentifier());
            vo.setValue(f.getValue());
            vo.setAcqTime(f.getAcqTime());
            vo.setRatio(f.getRatio());
            vo.setPreservable(f.getPreservable());
            vo.setBindingAcq(f.getBindingAcq());
            vo.setUpdatedBy(f.getUpdatedBy());
            vo.setUpdateTime(f.getUpdateTime());
            vo.setCreatedBy(f.getCreatedBy());
            vo.setCreateTime(f.getCreateTime());
            result.add(vo);
        }
        return result;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void bindSpaceGateways(Long spaceId, List<String> gatewayUuids) {
        if (spaceId == null) {
            return;
        }
        baseSpaceGatewayMapper.delete(new LambdaQueryWrapper<BaseSpaceGateway>().eq(BaseSpaceGateway::getSpaceId, spaceId));
        if (gatewayUuids == null || gatewayUuids.isEmpty()) {
            return;
        }

        Set<String> normalizedUuids = gatewayUuids.stream()
                .filter(Objects::nonNull)
                .map(String::trim)
                .filter(s -> !s.isEmpty())
                .collect(Collectors.toCollection(LinkedHashSet::new));
        if (normalizedUuids.isEmpty()) {
            return;
        }

        List<DmpDevice> gateways = dmpDeviceMapper.selectList(
                new LambdaQueryWrapper<DmpDevice>().in(DmpDevice::getDeviceUuid, normalizedUuids));
        Set<String> existsGatewayUuids = gateways.stream()
                .map(DmpDevice::getDeviceUuid)
                .filter(Objects::nonNull)
                .collect(Collectors.toSet());
        if (existsGatewayUuids.isEmpty()) {
            return;
        }

        LocalDateTime now = LocalDateTime.now();
        String auditUser = trimAuditUser(SecurityUtils.getUsername());
        for (String gatewayUuid : normalizedUuids) {
            if (!existsGatewayUuids.contains(gatewayUuid)) {
                continue;
            }
            BaseSpaceGateway relation = new BaseSpaceGateway();
            relation.setSpaceId(spaceId);
            relation.setGatewayUuid(gatewayUuid);
            relation.setCreatedBy(auditUser);
            relation.setCreatedTime(now);
            baseSpaceGatewayMapper.insert(relation);
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void unbindSpaceGateways(Long spaceId, List<String> gatewayUuids) {
        if (spaceId == null || gatewayUuids == null || gatewayUuids.isEmpty()) {
            return;
        }
        Set<String> normalizedUuids = gatewayUuids.stream()
                .filter(Objects::nonNull)
                .map(String::trim)
                .filter(s -> !s.isEmpty())
                .collect(Collectors.toCollection(LinkedHashSet::new));
        if (normalizedUuids.isEmpty()) {
            return;
        }

        baseSpaceGatewayMapper.delete(new LambdaQueryWrapper<BaseSpaceGateway>()
                .eq(BaseSpaceGateway::getSpaceId, spaceId)
                .in(BaseSpaceGateway::getGatewayUuid, normalizedUuids));
    }

    @Override
    public List<DmpGatewayDetailResponse> getGatewayListBySpaceId(Long spaceId) {
        if (spaceId == null) {
            return Collections.emptyList();
        }

        // 1. 授权空间
        List<Long> spaceIds = baseSpaceService.getAuthorizedSpaceIds(spaceId);

        List<BaseSpaceGateway> links = baseSpaceGatewayMapper.selectList(
                new LambdaQueryWrapper<BaseSpaceGateway>().in(BaseSpaceGateway::getSpaceId, spaceIds));
        if (links == null || links.isEmpty()) {
            return Collections.emptyList();
        }
        Set<String> gatewayUuids = links.stream()
                .map(BaseSpaceGateway::getGatewayUuid)
                .filter(Objects::nonNull)
                .collect(Collectors.toCollection(LinkedHashSet::new));
        if (gatewayUuids.isEmpty()) {
            return Collections.emptyList();
        }

        List<DmpDevice> gateways = dmpDeviceMapper.selectList(
                new LambdaQueryWrapper<DmpDevice>()
                        .in(DmpDevice::getDeviceUuid, gatewayUuids)
                        .eq(DmpDevice::getDeleteFlag, 0));
        if (gateways == null || gateways.isEmpty()) {
            return Collections.emptyList();
        }

        List<DmpDeviceStatus> statusList = dmpDeviceStatusMapper.selectList(
                Wrappers.<DmpDeviceStatus>lambdaQuery().in(DmpDeviceStatus::getDeviceUuid, gatewayUuids));
        Map<String, DmpDeviceStatus> statusByUuid = statusList == null ? Collections.emptyMap()
                : statusList.stream()
                .filter(s -> s.getDeviceUuid() != null)
                .collect(Collectors.toMap(DmpDeviceStatus::getDeviceUuid, s -> s, (a, b) -> a));

        return gateways.stream().map(item -> {
            DmpGatewayDetailResponse resp = new DmpGatewayDetailResponse();
            resp.setId(item.getId());
            resp.setDeviceUuid(item.getDeviceUuid());
            resp.setName(item.getDeviceName());
            resp.setInstallAddress(item.getInstallAddress());
            DmpDeviceStatus status = statusByUuid.get(item.getDeviceUuid());
            if (status != null) {
                resp.setCommunicationStatus(status.getDeviceStatus());
                resp.setOnlineTime(status.getLastOnlineTime());
                resp.setOfflineTime(status.getLastOfflineTime());
            }
            return resp;
        }).collect(Collectors.toList());
    }
}
