package com.zksy.base.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.zksy.base.domain.EquipmentBase;
import com.zksy.base.domain.EquipmentPointRel;
import com.zksy.base.domain.MonitorPoint;
import com.zksy.base.domain.PipeNetworkBase;
import com.zksy.base.dto.MonitorPointPageInDTO;
import com.zksy.base.mapper.EquipmentBaseMapper;
import com.zksy.base.mapper.EquipmentPointRelMapper;
import com.zksy.base.mapper.MonitorPointMapper;
import com.zksy.base.mapper.PipeNetworkBaseMapper;
import com.zksy.base.service.MonitorPointService;
import com.zksy.common.exception.ServiceException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

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

@Slf4j
@Service
public class MonitorPointServiceImpl extends ServiceImpl<MonitorPointMapper, MonitorPoint>
        implements MonitorPointService {

    @Autowired
    private EquipmentPointRelMapper equipmentPointRelMapper;

    @Autowired
    private EquipmentBaseMapper equipmentBaseMapper;

    @Autowired
    private PipeNetworkBaseMapper pipeNetworkBaseMapper;

    @Override
    public Page<MonitorPoint> queryByPage(MonitorPointPageInDTO dto) {
        Page<MonitorPoint> page = new Page<>(dto.getPageNum(), dto.getPageSize());
        LambdaQueryWrapper<MonitorPoint> wrapper = new LambdaQueryWrapper<>();
        wrapper.like(dto.getPointName() != null && !dto.getPointName().isEmpty(), MonitorPoint::getPointName, dto.getPointName());
        wrapper.eq(dto.getPointType() != null && !dto.getPointType().isEmpty(), MonitorPoint::getPointType, dto.getPointType());
        wrapper.orderByDesc(MonitorPoint::getCreateTime);
        return this.page(page, wrapper);
    }

    @Override
    public Page<MonitorPoint> findByPage(long pageNum, long pageSize,
                                          String pointCode, String pointName,
                                          String pointType, String status) {
        Page<MonitorPoint> page = new Page<>(pageNum, pageSize);
        LambdaQueryWrapper<MonitorPoint> queryWrapper = new LambdaQueryWrapper<>();
        if (pointCode != null && !pointCode.isEmpty()) {
            queryWrapper.like(MonitorPoint::getPointCode, pointCode);
        }
        if (pointName != null && !pointName.isEmpty()) {
            queryWrapper.like(MonitorPoint::getPointName, pointName);
        }
        if (pointType != null && !pointType.isEmpty()) {
            queryWrapper.eq(MonitorPoint::getPointType, pointType);
        }
        if (status != null && !status.isEmpty()) {
            queryWrapper.eq(MonitorPoint::getStatus, status);
        }
        queryWrapper.orderByDesc(MonitorPoint::getCreateTime);
        return this.page(page, queryWrapper);
    }

    @Override
    public boolean saveWithCheck(MonitorPoint entity) {
        if (entity.getNetworkId() != null) {
            PipeNetworkBase network = pipeNetworkBaseMapper.selectById(entity.getNetworkId());
            if (network == null) {
                throw new ServiceException("关联管网设施不存在：" + entity.getNetworkId());
            }
        }
        LambdaQueryWrapper<MonitorPoint> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(MonitorPoint::getPointCode, entity.getPointCode());
        long count = this.count(queryWrapper);
        if (count > 0) {
            throw new ServiceException("监测点编码已存在，请更换：" + entity.getPointCode());
        }
        return this.save(entity);
    }

    @Override
    public boolean saveBatchWithCheck(List<MonitorPoint> entityList) {
        Set<String> codeSet = new HashSet<>();
        for (MonitorPoint entity : entityList) {
            String code = entity.getPointCode();
            if (codeSet.contains(code)) {
                throw new ServiceException("列表中存在重复编码：" + code);
            }
            codeSet.add(code);
        }

        for (MonitorPoint entity : entityList) {
            if (entity.getNetworkId() != null) {
                if (pipeNetworkBaseMapper.selectById(entity.getNetworkId()) == null) {
                    throw new ServiceException("关联管网设施不存在：" + entity.getNetworkId());
                }
            }
            LambdaQueryWrapper<MonitorPoint> wrapper = new LambdaQueryWrapper<>();
            wrapper.eq(MonitorPoint::getPointCode, entity.getPointCode());
            if (this.count(wrapper) > 0) {
                throw new ServiceException("监测点编码已存在：" + entity.getPointCode());
            }
        }
        return this.saveBatch(entityList);
    }

    @Override
    @Transactional
    public boolean updateWithCheck(MonitorPoint entity) {
        String pointId = entity.getPointId();
        if (this.getById(pointId) == null) {
            throw new ServiceException("监测点不存在，无法修改：" + pointId);
        }

        if (entity.getNetworkId() != null) {
            if (pipeNetworkBaseMapper.selectById(entity.getNetworkId()) == null) {
                throw new ServiceException("关联管网设施不存在：" + entity.getNetworkId());
            }
        }

        String newCode = entity.getPointCode();
        if (newCode != null) {
            LambdaQueryWrapper<MonitorPoint> wrapper = new LambdaQueryWrapper<>();
            wrapper.eq(MonitorPoint::getPointCode, newCode)
                    .ne(MonitorPoint::getPointId, pointId);
            if (this.count(wrapper) > 0) {
                throw new ServiceException("监测点编码已被使用：" + newCode);
            }
        }
        return this.updateById(entity);
    }

    @Override
    @Transactional
    public boolean removeWithCheck(String pointId) {
        MonitorPoint point = this.getById(pointId);
        if (point == null) {
            throw new ServiceException("监测点不存在：" + pointId);
        }

        LambdaQueryWrapper<EquipmentPointRel> relWrapper = new LambdaQueryWrapper<>();
        relWrapper.eq(EquipmentPointRel::getPointId, pointId);
        equipmentPointRelMapper.delete(relWrapper);
        log.info("已删除监测点[{}]的设备关联关系", pointId);

        return this.removeById(pointId);
    }

    @Override
    @Transactional
    public boolean removeBatchWithCheck(List<String> pointIds) {
        if (CollectionUtils.isEmpty(pointIds)) {
            throw new ServiceException("批量删除失败：监测点ID列表不能为空");
        }

        List<MonitorPoint> existPoints = this.listByIds(pointIds);
        if (existPoints.isEmpty()) {
            throw new ServiceException("批量删除失败：所有监测点ID均不存在");
        }

        List<String> existIds = existPoints.stream()
                .map(MonitorPoint::getPointId)
                .collect(Collectors.toList());

        List<String> invalidIds = pointIds.stream()
                .filter(id -> !existIds.contains(id))
                .collect(Collectors.toList());
        if (!invalidIds.isEmpty()) {
            log.warn("批量删除中存在无效监测点ID：{}", invalidIds);
        }

        LambdaQueryWrapper<EquipmentPointRel> relWrapper = new LambdaQueryWrapper<>();
        relWrapper.in(EquipmentPointRel::getPointId, existIds);
        equipmentPointRelMapper.delete(relWrapper);
        log.info("已批量删除{}条监测点的设备关联关系", existIds.size());

        return this.removeByIds(existIds);
    }

    @Override
    @Transactional
    public boolean bindEquipment(String pointId, List<String> equipmentIds) {
        MonitorPoint point = this.getById(pointId);
        if (point == null) {
            throw new ServiceException("监测点不存在：" + pointId);
        }

        for (String equipmentId : equipmentIds) {
            EquipmentBase equipment = equipmentBaseMapper.selectById(equipmentId);
            if (equipment == null) {
                throw new ServiceException("设备不存在：" + equipmentId);
            }
            LambdaQueryWrapper<EquipmentPointRel> checkWrapper = new LambdaQueryWrapper<>();
            checkWrapper.eq(EquipmentPointRel::getPointId, pointId)
                    .eq(EquipmentPointRel::getEquipmentId, equipmentId);
            if (equipmentPointRelMapper.selectCount(checkWrapper) > 0) {
                throw new ServiceException("设备已绑定此监测点：" + equipmentId);
            }
        }

        List<EquipmentPointRel> relList = new ArrayList<>();
        for (String equipmentId : equipmentIds) {
            EquipmentPointRel rel = new EquipmentPointRel();
            rel.setPointId(pointId);
            rel.setEquipmentId(equipmentId);
            relList.add(rel);
        }

        // 逐个插入或批量插入，因为 relList 类型和 this.saveBatch 不匹配
        int count = 0;
        for (EquipmentPointRel rel : relList) {
            count += equipmentPointRelMapper.insert(rel);
        }
        return count > 0;
    }

    @Override
    @Transactional
    public boolean unbindEquipment(String pointId, List<String> equipmentIds) {
        LambdaQueryWrapper<EquipmentPointRel> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(EquipmentPointRel::getPointId, pointId)
                .in(EquipmentPointRel::getEquipmentId, equipmentIds);
        return equipmentPointRelMapper.delete(wrapper) > 0;
    }

    @Override
    public Map<String, Object> getPointDetail(String pointId) {
        MonitorPoint point = this.getById(pointId);
        if (point == null) {
            throw new ServiceException("监测点不存在：" + pointId);
        }

        Map<String, Object> result = new HashMap<>();
        result.put("pointInfo", point);

        if (point.getNetworkId() != null) {
            PipeNetworkBase network = pipeNetworkBaseMapper.selectById(point.getNetworkId());
            result.put("networkInfo", network);
        }

        LambdaQueryWrapper<EquipmentPointRel> relWrapper = new LambdaQueryWrapper<>();
        relWrapper.eq(EquipmentPointRel::getPointId, pointId);
        List<EquipmentPointRel> relList = equipmentPointRelMapper.selectList(relWrapper);
        if (!relList.isEmpty()) {
            List<String> equipmentIds = relList.stream()
                    .map(EquipmentPointRel::getEquipmentId)
                    .collect(Collectors.toList());
            List<EquipmentBase> equipmentList = equipmentBaseMapper.selectBatchIds(equipmentIds);
            result.put("equipmentList", equipmentList);
        }

        return result;
    }

    @Override
    public List<MonitorPoint> getPointsByNetwork(String networkId) {
        LambdaQueryWrapper<MonitorPoint> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(MonitorPoint::getNetworkId, networkId);
        wrapper.orderByDesc(MonitorPoint::getCreateTime);
        return this.list(wrapper);
    }
}
