package com.fizz.business.comm.OPC;

import com.fizz.business.domain.msg.OpcMessage;
import com.fizz.business.domain.msg.PdiSetup;
import com.kangaroohy.milo.model.ReadWriteEntity;
import com.kangaroohy.milo.service.MiloService;
import lombok.extern.log4j.Log4j2;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import static com.fizz.business.service.manager.OpcMessageIdsManager.findIdentifierByFieldName;
import static com.fizz.business.service.manager.OpcMessageIdsManager.pdiSetupIds;

@Service
@Log4j2
@ConditionalOnProperty(prefix = "kangaroohy.milo", name = "enabled", havingValue = "true")
public class OpcMessageSend {

    @Resource
    private MiloService miloService;

    private void sendPdiSetup(PdiSetup pdiSetup) {
        try {
            List<ReadWriteEntity> entities =  getWriteEntities(pdiSetup, pdiSetupIds);
            miloService.writeToOpcUa(entities);
        } catch (Exception e) {
            log.error("发送设定失败：{0}",e.getMessage());
        }

    }

    /**
     * 单独写入当前钢卷号到 OPC
     * 使用 pdiSetupIds 中 coilId 对应的节点
     */
    public void sendCoilId(String coilId) {
        try {
            String coilNode = pdiSetupIds.entrySet().stream()
                    .filter(e -> "coilId".equals(e.getValue()))
                    .map(Map.Entry::getKey)
                    .findFirst()
                    .orElse(null);
            if (coilNode == null) {
                log.warn("未找到 coilId 对应的 OPC 节点，写入跳过");
                return;
            }
            List<ReadWriteEntity> entities = new ArrayList<>();
            entities.add(ReadWriteEntity.builder()
                    .identifier(coilNode)
                    .value(coilId)
                    .build());
            miloService.writeToOpcUa(entities);
            log.info("写入 OPC coilId 成功，node={} value={}", coilNode, coilId);
        } catch (Exception e) {
            log.error("写入 OPC coilId 失败，coilId={}，原因：{}", coilId, e.getMessage(), e);
        }
    }

    /**
     * 通用写数据方法：通过字段名向指定点位写入单个值
     * @param fieldName 字段名（对象属性名），例如：coilId
     * @param value 要写入的值
     * @return 是否写入成功
     */
    public boolean writeDataByFieldName(String fieldName, Object value) {
        try {
            String identifier = findIdentifierByFieldName(fieldName);
            if (identifier == null) {
                log.error("未找到字段名对应的 OPC 节点路径，fieldName={}", fieldName);
                return false;
            }
            List<ReadWriteEntity> entities = new ArrayList<>();
            entities.add(ReadWriteEntity.builder()
                    .identifier(identifier)
                    .value(value)
                    .build());
            miloService.writeToOpcUa(entities);
            log.info("写入 OPC 数据成功，fieldName={}, identifier={}, value={}", fieldName, identifier, value);
            return true;
        } catch (Exception e) {
            log.error("写入 OPC 数据失败，fieldName={}, value={}，原因：{}", fieldName, value, e.getMessage(), e);
            return false;
        }
    }

    /**
     * 通用写数据方法：向指定点位写入单个值（直接使用节点路径）
     * @param identifier OPC 点位标识符（节点路径）
     * @param value 要写入的值
     * @return 是否写入成功
     */
    public boolean writeData(String identifier, Object value) {
        try {
            List<ReadWriteEntity> entities = new ArrayList<>();
            entities.add(ReadWriteEntity.builder()
                    .identifier(identifier)
                    .value(value)
                    .build());
            miloService.writeToOpcUa(entities);
            log.info("写入 OPC 数据成功，identifier={}, value={}", identifier, value);
            return true;
        } catch (Exception e) {
            log.error("写入 OPC 数据失败，identifier={}, value={}，原因：{}", identifier, value, e.getMessage(), e);
            return false;
        }
    }

    /**
     * 批量写数据方法：通过字段名向多个点位写入数据
     * @param fieldDataMap 字段名和值的映射，key 为 fieldName，value 为要写入的值
     * @return 是否全部写入成功
     */
    public boolean batchWriteDataByFieldName(Map<String, Object> fieldDataMap) {
        try {
            if (fieldDataMap == null || fieldDataMap.isEmpty()) {
                log.warn("批量写数据：数据映射为空，跳过写入");
                return false;
            }
            List<ReadWriteEntity> entities = new ArrayList<>();
            for (Map.Entry<String, Object> entry : fieldDataMap.entrySet()) {
                String fieldName = entry.getKey();
                String identifier = findIdentifierByFieldName(fieldName);
                if (identifier == null) {
                    log.warn("未找到字段名对应的 OPC 节点路径，fieldName={}，跳过该字段", fieldName);
                    continue;
                }
                entities.add(ReadWriteEntity.builder()
                        .identifier(identifier)
                        .value(entry.getValue())
                        .build());
            }
            if (entities.isEmpty()) {
                log.warn("批量写数据：没有有效的字段映射，跳过写入");
                return false;
            }
            miloService.writeToOpcUa(entities);
            log.info("批量写入 OPC 数据成功，共 {} 个点位", entities.size());
            return true;
        } catch (Exception e) {
            log.error("批量写入 OPC 数据失败，原因：{}", e.getMessage(), e);
            return false;
        }
    }

    /**
     * 批量写数据方法：向多个点位写入数据（直接使用节点路径）
     * @param dataMap 点位标识符和值的映射，key 为 identifier，value 为要写入的值
     * @return 是否全部写入成功
     */
    public boolean batchWriteData(Map<String, Object> dataMap) {
        try {
            if (dataMap == null || dataMap.isEmpty()) {
                log.warn("批量写数据：数据映射为空，跳过写入");
                return false;
            }
            List<ReadWriteEntity> entities = new ArrayList<>();
            for (Map.Entry<String, Object> entry : dataMap.entrySet()) {
                entities.add(ReadWriteEntity.builder()
                        .identifier(entry.getKey())
                        .value(entry.getValue())
                        .build());
            }
            miloService.writeToOpcUa(entities);
            log.info("批量写入 OPC 数据成功，共 {} 个点位", entities.size());
            return true;
        } catch (Exception e) {
            log.error("批量写入 OPC 数据失败，原因：{}", e.getMessage(), e);
            return false;
        }
    }

    private List<ReadWriteEntity> getWriteEntities(OpcMessage msg, Map<String,String> msgIds) {
        List<ReadWriteEntity> entities = new ArrayList<>();
        for (String key : msgIds.keySet()) {
            String fieldName = msgIds.get(key);
            Object value = null;
            try {
                // 获取OpcMessage类中对应字段的Field对象
                Field field = msg.getClass().getDeclaredField(fieldName);
                // 设置可访问以处理私有字段
                field.setAccessible(true);
                // 获取msg对象中该字段的值
                value = field.get(msg);
            } catch (NoSuchFieldException | IllegalAccessException e) {
                // 处理字段不存在或访问异常，可记录日志或设置默认值
                e.printStackTrace();
               return new ArrayList<>();
            }

            ReadWriteEntity entity = ReadWriteEntity.builder()
                    .identifier(key)
                    .value(value)
                    .build();
            entities.add(entity);
        }
        return entities;
    }
}
