package com.xzzn.ems.service.impl;

import com.alibaba.fastjson2.JSON;
import com.xzzn.common.core.redis.RedisCache;
import com.xzzn.common.enums.PointType;
import com.xzzn.common.exception.ServiceException;
import com.xzzn.common.utils.StringUtils;
import com.xzzn.common.utils.bean.BeanValidators;
import com.xzzn.common.utils.poi.ExcelUtil;
import com.xzzn.ems.domain.EmsPointEnumMatch;
import com.xzzn.ems.domain.EmsPointMatch;
import com.xzzn.ems.domain.vo.DevicePointMatchExportVo;
import com.xzzn.ems.domain.vo.DevicePointMatchVo;
import com.xzzn.ems.domain.vo.ImportPointDataRequest;
import com.xzzn.ems.domain.vo.ImportPointTemplateRequest;
import com.xzzn.ems.enums.DeviceMatchTable;
import com.xzzn.ems.mapper.EmsPointEnumMatchMapper;
import com.xzzn.ems.mapper.EmsPointMatchMapper;
import com.xzzn.ems.service.IEmsPointMatchService;
import com.xzzn.ems.utils.DevicePointMatchDataProcessor;

import java.io.IOException;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;

import javax.validation.Validator;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.web.multipart.MultipartFile;

/**
 * 点位匹配Service业务层处理
 *
 * @author xzzn
 * @date 2025-11-04
 */
@Service
public class EmsPointMatchServiceImpl implements IEmsPointMatchService {
    private static final Logger log = LoggerFactory.getLogger(EmsPointMatchServiceImpl.class);
    private static final String SEPARATOR = "#";

    private static final String SITE_ID = "DEFAULT";
    @Autowired
    private EmsPointMatchMapper emsPointMatchMapper;
    @Autowired
    private EmsPointEnumMatchMapper emsPointEnumMatchMapper;
    @Autowired
    protected Validator validator;
    @Autowired
    private RedisCache redisCache;

    /**
     * 查询点位匹配列表
     *
     * @param emsPointMatch 点位匹配
     * @return 点位匹配
     */
    @Override
    public List<DevicePointMatchExportVo> selectEmsPointMatchList(EmsPointMatch emsPointMatch) {
        if (StringUtils.isBlank(emsPointMatch.getDeviceId())) {
            emsPointMatch.setSiteId(SITE_ID);
        }
        List<DevicePointMatchExportVo> devicePointMatchExportVos = emsPointMatchMapper.selectEmsPointMatchExportList(emsPointMatch);
        if (CollectionUtils.isEmpty(devicePointMatchExportVos)) {
            return devicePointMatchExportVos;
        }

        for (DevicePointMatchExportVo devicePointMatch : devicePointMatchExportVos) {
            List<EmsPointEnumMatch> pointEnumMatchList = emsPointEnumMatchMapper.selectList(emsPointMatch.getSiteId(),
                    emsPointMatch.getDeviceCategory(), devicePointMatch.getMatchField());
            if (!CollectionUtils.isEmpty(pointEnumMatchList)) {
                String enumName = pointEnumMatchList.stream().map(EmsPointEnumMatch::getEnumName).filter(StringUtils::isNotBlank).collect(Collectors.joining(SEPARATOR));
                String dataEnumCode = pointEnumMatchList.stream().map(EmsPointEnumMatch::getDataEnumCode).filter(StringUtils::isNotBlank).collect(Collectors.joining(SEPARATOR));
                devicePointMatch.setMatchFieldEnum(enumName);
                devicePointMatch.setDataEnum(dataEnumCode);
            }
            devicePointMatch.setA(devicePointMatch.getA() == null ? devicePointMatch.getA() : devicePointMatch.getA().stripTrailingZeros());
            devicePointMatch.setK(devicePointMatch.getK() == null ? devicePointMatch.getK() : devicePointMatch.getK().stripTrailingZeros());
            devicePointMatch.setB(devicePointMatch.getB() == null ? devicePointMatch.getB() : devicePointMatch.getB().stripTrailingZeros());
        }

        return devicePointMatchExportVos;
    }

    /**
     * 上传点位清单
     *
     * @param pointMatchList
     * @param isUpdateSupport
     * @param operName
     * @return
     */
    @Override
    public String importPoint(List<EmsPointMatch> pointMatchList, boolean isUpdateSupport, String operName) {
        if (StringUtils.isNull(pointMatchList) || pointMatchList.size() == 0) {
            throw new ServiceException("导入用户数据不能为空！");
        }
        int successNum = 0;
        int failureNum = 0;
        StringBuilder successMsg = new StringBuilder();
        StringBuilder failureMsg = new StringBuilder();
        for (EmsPointMatch pointMatch : pointMatchList) {
            try {
                // 验证点位是否存在
                EmsPointMatch point = emsPointMatchMapper.getUniquePoint(pointMatch.getSiteId(),
                        pointMatch.getDeviceCategory(), pointMatch.getDataPoint());
                if (StringUtils.isNull(point)) {
                    BeanValidators.validateWithException(validator, pointMatch);
                    pointMatch.setCreateBy(operName);
                    emsPointMatchMapper.insertEmsPointMatch(pointMatch);
                    successNum++;
                    successMsg.append("<br/>" + successNum + "、站点 " + pointMatch.getSiteId() + "、点位 " + pointMatch.getDataPoint() + " 导入成功");
                } else if (isUpdateSupport) {
                    BeanValidators.validateWithException(validator, pointMatch);
                    pointMatch.setUpdateBy(operName);
                    emsPointMatchMapper.updateEmsPointMatch(pointMatch);
                    successNum++;
                    successMsg.append("<br/>" + successNum + "、站点 " + pointMatch.getSiteId() + "、点位 " + pointMatch.getDataPoint() + " 更新成功");
                } else {
                    failureNum++;
                    failureMsg.append("<br/>" + failureNum + "、站点" + pointMatch.getSiteId() + "、点位 " + pointMatch.getDataPoint() + " 已存在");
                }
            } catch (Exception e) {
                failureNum++;
                String msg = "<br/>" + failureNum + "、站点" + pointMatch.getSiteId() + "、点位 " + pointMatch.getDataPoint() + " 导入失败：";
                failureMsg.append(msg + e.getMessage());
            }
        }
        if (failureNum > 0) {
            failureMsg.insert(0, "很抱歉，导入失败！共 " + failureNum + " 条数据格式不正确，错误如下：");
            throw new ServiceException(failureMsg.toString());
        } else {
            successMsg.insert(0, "恭喜您，数据已全部导入成功！共 " + successNum + " 条，数据如下：");
        }
        return successMsg.toString();
    }

    /**
     * 导入设备点位清单数据到数据库，并同步到Redis
     *
     * @param request
     * @param operName
     * @return
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public List<DevicePointMatchVo> importDataByDevice(ImportPointDataRequest request, String operName) {
        ExcelUtil<DevicePointMatchVo> util = new ExcelUtil<>(DevicePointMatchVo.class);
        List<DevicePointMatchVo> pointMatchList;
        MultipartFile file = request.getFile();
        if (file.isEmpty()) {
            throw new ServiceException("点位清单文件不能为空");
        }
        try {
            pointMatchList = util.importExcel(file.getInputStream());
        } catch (IOException e) {
            log.info("点位清单导入失败：{}", e.getMessage());
            throw new ServiceException("点位清单导入失败！");
        }
        if (CollectionUtils.isEmpty(pointMatchList)) {
            throw new ServiceException("导入用户数据不能为空！");
        }

        String siteId = request.getSiteId();
        String deviceId = request.getDeviceId();
        String deviceCategory = request.getDeviceCategory();
        List<DevicePointMatchVo> errorList = new ArrayList<>();
        for (DevicePointMatchVo pointMatch : pointMatchList) {
            try {
                //校验点位清单文件内容
                if (validDevicePointMatch(pointMatch, errorList)) {
                    continue;
                }
                EmsPointMatch savePoint = new EmsPointMatch();
                BeanUtils.copyProperties(pointMatch, savePoint);
                savePoint.setSiteId(siteId);
                savePoint.setDeviceId(deviceId);
                savePoint.setDeviceCategory(deviceCategory);
                if (StringUtils.isNotBlank(pointMatch.getIsAlarm()) && String.valueOf(PointType.YES.getCode()).equals(pointMatch.getIsAlarm())) {
                    savePoint.setMatchTable(DeviceMatchTable.getAlarmMatchTableByCode(deviceCategory));
                    savePoint.setIsAlarm(PointType.YES.getCode());
                } else {
                    savePoint.setMatchTable(DeviceMatchTable.getMatchTableByCode(deviceCategory));
                }
                savePoint.setPointName(DevicePointMatchDataProcessor.getFieldAnnotation(DeviceMatchTable.getClassByTable(savePoint.getMatchTable()), StringUtils.toCamelCase(savePoint.getMatchField())));
                savePoint.setCreateBy(operName);
                savePoint.setUpdateBy(operName);
                // 验证点位是否存在
                EmsPointMatch dbPoint = emsPointMatchMapper.getOnePointMatch(siteId, deviceId, deviceCategory, pointMatch.getDataPoint());
                if (Objects.isNull(dbPoint)) {
                    emsPointMatchMapper.insertEmsPointMatch(savePoint);
                } else {
                    savePoint.setId(dbPoint.getId());
                    emsPointMatchMapper.updateEmsPointMatch(savePoint);
                }
                // 保存点位枚举映射关系
                savePointMatchEnum(pointMatch.getMatchFieldEnum(), pointMatch.getDataEnum(), savePoint);

            } catch (Exception e) {
                e.printStackTrace();
                throw new ServiceException("点位清单导入失败！");
            }
        }
        // 同步到Redis
        syncToRedis(siteId, deviceId, deviceCategory);
        return errorList;
    }

    /**
     * 按站点导入模板点位
     *
     * @param request
     * @param operName
     * @return
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public String importTemplateBySite(ImportPointTemplateRequest request, String operName) {
        String targetSiteId = request.getSiteId();
        if (SITE_ID.equals(targetSiteId)) {
            throw new ServiceException("模板站点不支持作为导入目标站点");
        }

        int templatePointCount = emsPointMatchMapper.countBySiteId(SITE_ID);
        if (templatePointCount <= 0) {
            throw new ServiceException("模板点位数据不存在，无法导入");
        }

        boolean overwrite = Boolean.TRUE.equals(request.getOverwrite());
        int targetPointCount = emsPointMatchMapper.countBySiteId(targetSiteId);
        if (targetPointCount > 0 && !overwrite) {
            throw new ServiceException("目标站点已存在点位数据，请勾选“覆盖已存在数据”后重试");
        }

        if (targetPointCount > 0) {
            emsPointMatchMapper.deleteBySiteId(targetSiteId);
        }

        int targetEnumCount = emsPointEnumMatchMapper.countBySiteId(targetSiteId);
        if (targetEnumCount > 0) {
            emsPointEnumMatchMapper.deleteBySiteId(targetSiteId);
        }

        int importPointCount = emsPointMatchMapper.copyTemplateToSite(SITE_ID, targetSiteId, operName);
        int importEnumCount = 0;
        if (emsPointEnumMatchMapper.countBySiteId(SITE_ID) > 0) {
            importEnumCount = emsPointEnumMatchMapper.copyTemplateToSite(SITE_ID, targetSiteId, operName);
        }

        syncSiteToRedis(targetSiteId);
        return String.format("导入成功：站点 %s，点位 %d 条，枚举 %d 条", targetSiteId, importPointCount, importEnumCount);
    }

    @Override
    public List<EmsPointMatch> selectPointMatchConfigList(EmsPointMatch emsPointMatch) {
        return emsPointMatchMapper.selectEmsPointMatchList(emsPointMatch);
    }

    @Override
    public EmsPointMatch selectPointMatchById(Long id) {
        return emsPointMatchMapper.selectEmsPointMatchById(id);
    }

    @Override
    public int insertPointMatch(EmsPointMatch emsPointMatch) {
        int rows = emsPointMatchMapper.insertEmsPointMatch(emsPointMatch);
        if (rows > 0 && StringUtils.isNotBlank(emsPointMatch.getSiteId()) && StringUtils.isNotBlank(emsPointMatch.getDeviceCategory())) {
            syncToRedis(emsPointMatch.getSiteId(), emsPointMatch.getDeviceId(), emsPointMatch.getDeviceCategory());
        }
        return rows;
    }

    @Override
    public int updatePointMatch(EmsPointMatch emsPointMatch) {
        int rows = emsPointMatchMapper.updateEmsPointMatch(emsPointMatch);
        if (rows > 0 && StringUtils.isNotBlank(emsPointMatch.getSiteId()) && StringUtils.isNotBlank(emsPointMatch.getDeviceCategory())) {
            syncToRedis(emsPointMatch.getSiteId(), emsPointMatch.getDeviceId(), emsPointMatch.getDeviceCategory());
        }
        return rows;
    }

    @Override
    public int deletePointMatchByIds(Long[] ids) {
        List<EmsPointMatch> deletedList = new ArrayList<>();
        for (Long id : ids) {
            EmsPointMatch pointMatch = emsPointMatchMapper.selectEmsPointMatchById(id);
            if (pointMatch != null) {
                deletedList.add(pointMatch);
            }
        }
        int rows = emsPointMatchMapper.deleteEmsPointMatchByIds(ids);
        if (rows > 0) {
            deletedList.forEach(pointMatch -> {
                if (StringUtils.isNotBlank(pointMatch.getSiteId()) && StringUtils.isNotBlank(pointMatch.getDeviceCategory())) {
                    syncToRedis(pointMatch.getSiteId(), pointMatch.getDeviceId(), pointMatch.getDeviceCategory());
                }
            });
        }
        return rows;
    }

    private boolean validDevicePointMatch(DevicePointMatchVo pointMatch, List<DevicePointMatchVo> errorList) {
        StringBuilder errorMsg = new StringBuilder();
        if (StringUtils.isBlank(pointMatch.getMatchField())) {
            errorMsg.append("点位匹配字段不能为空;");
        }
        if (StringUtils.isBlank(pointMatch.getDataPoint())) {
            errorMsg.append("数据点位不能为空;");
        }
        if (StringUtils.isBlank(pointMatch.getDataPointName())) {
            errorMsg.append("数据点位名称不能为空;");
        }
        if (errorMsg.length() > 0) {
            pointMatch.setErrorMsg(errorMsg.toString());
            errorList.add(pointMatch);
            return true;
        }
        return false;
    }

    private void syncToRedis(String siteId, String deviceId, String deviceCategory) {
        // 点位匹配数据同步到Redis
        String pointMatchKey = DevicePointMatchDataProcessor.getPointMacthCacheKey(siteId, deviceId, deviceCategory);
        List<EmsPointMatch> pointMatchData = emsPointMatchMapper.getDevicePointMatchList(siteId, deviceId, deviceCategory);
//        log.info("同步点位匹配数据到Redis key:{} data:{}", pointMatchKey, pointMatchData);
        if (CollectionUtils.isEmpty(pointMatchData)) {
            if (redisCache.hasKey(pointMatchKey)) {
                redisCache.deleteObject(pointMatchKey);
            }
            return;
        }
        if (redisCache.hasKey(pointMatchKey)) {
            redisCache.deleteObject(pointMatchKey);
        }
        redisCache.setCacheList(pointMatchKey, pointMatchData);
        log.info("点位匹配数据同步完成 data:{}", JSON.toJSONString(redisCache.getCacheList(pointMatchKey)));

        // 点位枚举匹配数据同步到Redis
        syncPointEnumToRedis(siteId, deviceCategory);
    }

    private void syncPointEnumToRedis(String siteId, String deviceCategory) {
        String pointEnumMatchKey = DevicePointMatchDataProcessor.getPointEnumMacthCacheKey(siteId, deviceCategory);
        List<EmsPointEnumMatch> pointEnumMatchList = emsPointEnumMatchMapper.selectList(siteId, deviceCategory, null);
        if (!CollectionUtils.isEmpty(pointEnumMatchList)) {
            if (Boolean.TRUE.equals(redisCache.hasKey(pointEnumMatchKey))) {
                redisCache.deleteObject(pointEnumMatchKey);
            }
            redisCache.setCacheList(pointEnumMatchKey, pointEnumMatchList);
            log.info("点位枚举匹配数据同步完成 data:{}", JSON.toJSONString(redisCache.getCacheList(pointEnumMatchKey)));
        }
    }

    private void syncSiteToRedis(String siteId) {
        List<EmsPointMatch> sitePointList = emsPointMatchMapper.selectBySiteId(siteId);
        if (CollectionUtils.isEmpty(sitePointList)) {
            return;
        }
        Set<String> categorySet = new HashSet<>();
        for (EmsPointMatch pointMatch : sitePointList) {
            categorySet.add(pointMatch.getDeviceCategory());
            if (StringUtils.isBlank(pointMatch.getDeviceId())) {
                continue;
            }
            syncToRedis(siteId, pointMatch.getDeviceId(), pointMatch.getDeviceCategory());
        }
        categorySet.forEach(category -> syncPointEnumToRedis(siteId, category));
    }

    private void savePointMatchEnum(String matchFieldEnum, String dataEnum, EmsPointMatch savePoint) {
        if (StringUtils.isAllBlank(matchFieldEnum, dataEnum)) {
            return;
        }
        String siteId = savePoint.getSiteId();
        List<EmsPointEnumMatch> pointEnumMatchList = emsPointEnumMatchMapper.selectList(siteId, savePoint.getDeviceCategory(), savePoint.getMatchField());
        if (CollectionUtils.isEmpty(pointEnumMatchList)) {
            siteId = SITE_ID;
            pointEnumMatchList = emsPointEnumMatchMapper.selectList(siteId, savePoint.getDeviceCategory(), savePoint.getMatchField());
        }
        if (CollectionUtils.isEmpty(pointEnumMatchList)) {
            log.info("未查询到点位点位枚举匹配数据");
            return;
        }
        String[] matchFieldEnums = matchFieldEnum.split(SEPARATOR);
        String[] dataEnums = dataEnum.split(SEPARATOR);
        if (matchFieldEnums.length != dataEnums.length) {
            return;
        }
        String finalSiteId = siteId;
        for (int i = 0; i < matchFieldEnums.length; i++) {
            String enumName = matchFieldEnums[i];
            String dataEnumCode = dataEnums[i];
            pointEnumMatchList.forEach(pointEnumMatch -> {
                if (pointEnumMatch.getEnumName().equals(enumName)) {
                    pointEnumMatch.setDataEnumCode(dataEnumCode);
                    if (SITE_ID.equals(finalSiteId)) {
                        EmsPointEnumMatch insertPointEnumMatch = new EmsPointEnumMatch();
                        BeanUtils.copyProperties(pointEnumMatch, insertPointEnumMatch);
                        insertPointEnumMatch.setSiteId(savePoint.getSiteId());
                        emsPointEnumMatchMapper.insertEmsPointEnumMatch(insertPointEnumMatch);
                    } else {
                        emsPointEnumMatchMapper.updateDataEnumCodeById(pointEnumMatch);
                    }
                }
            });
        }
    }
}
