package com.fizz.business.comm.OPC;

import com.fizz.business.constants.enums.ExitCutTypeEnum;
import com.fizz.business.constants.enums.OpcMessageType;
import com.fizz.business.domain.msg.*;
import com.fizz.business.service.LogDataService;
import com.fizz.business.service.hanle.OpcReceiverHandler;
import com.kangaroohy.milo.model.ReadWriteEntity;
import com.kangaroohy.milo.service.MiloService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.beanutils.BeanUtils;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.lang.reflect.Field;
import java.time.Duration;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicBoolean;

import static com.fizz.business.service.manager.OpcMessageIdsManager.*;

@Component
@Slf4j
@ConditionalOnProperty(prefix = "kangaroohy.milo", name = "enabled", havingValue = "true")
public class MessageSubscriptionRunner implements ApplicationRunner {

    @Resource
    private MiloService miloService;
    @Resource
    private OpcReceiverHandler opcReceiverHandler;

    @Resource
    private LogDataService logDataService;

    @Override
    public void run(ApplicationArguments args) {
        sub();
    }

    private void sub() {

        try {
            LocalDateTime localDateTime = LocalDateTime.now();
            AtomicBoolean isFirstRun = new AtomicBoolean(false);
            //入口移动，剪切、出口移动、出口称重
            miloService.subscriptionFromOpcUa(msgTriggers, 500,(id, value) -> {
                if(!isFirstRun.get()){
                    Duration duration = Duration.between(localDateTime, LocalDateTime.now());
                    if(duration.getSeconds() > 10){
                        isFirstRun.set(true);
                    }else{
                        return;
                    }
                }
                String nodeName = id.getNodeId().toString();
                if (nodeName.contains(entryMoveIds.get("trigger"))) {
                    log.info("接收到Entry Move信号,subscription 点位：{} 订阅到消息：{}", nodeName, value.getValue().toString());
                    readEntryMove();
                } else if (nodeName.contains(exitCutIds.get("trigger"))) {
                    log.info("接收到Exit Cut信号,subscription 点位：{} 订阅到消息：{}", nodeName, value.getValue().toString());
                    readExitCut();
                }  else if (nodeName.contains(exitMoveIds.get("trigger"))) {
                    log.info("接收到Exit Move信号,subscription 点位：{} 订阅到消息：{}", nodeName, value.getValue().toString());
                    readExitMove();
                } else if (nodeName.contains(exitMeasureIds.get("trigger"))) {
                    log.info("接收到Weight Measure信号,subscription 点位：{} 订阅到消息：{}", nodeName, value.getValue().toString());
                    readExitMeasure();
                } else {
                    log.info("error ids,subscription 点位：{} 订阅到消息：{}", nodeName, value.getValue().toString());
                }
            });
        } catch (Exception e) {
            log.error("EntryMove 订阅失败：{0}",e.getMessage());
        }
    }

    private void readEntryMove()
    {
        try {
            EntryMovementMessage msg =new EntryMovementMessage();
            writeMessage( msg,entryMoveIds);
            log.info("接收入口移动信号：从 {} 移动到 {} ", msg.getMaterialPlaceSource(), msg.getMaterialPlaceDestination());
            logDataService.logInfo("TRACK","接收入口移动信号：从 {} 移动到 {} ", msg.getMaterialPlaceSource(), msg.getMaterialPlaceDestination());
            opcReceiverHandler.onMessageReceived(OpcMessageType.ENTRY_MOVEMENT,msg);
        } catch (Exception e) {
        }
    }

    private void readExitCut()
    {
        try {
            ExitCutMessage msg = new ExitCutMessage();
            writeMessage( msg,exitCutIds);
            log.info("接收到出口剪切信号：剪切类型 {}，剪切长度{} ", msg.getCutType().toString(), msg.getCutLength());
            logDataService.logInfo("TRACK","接收到出口剪切信号：剪切类型 {}，剪切长度{} ", msg.getCutType().toString(), msg.getCutLength());
            opcReceiverHandler.onMessageReceived(OpcMessageType.EXIT_CUT,msg);
        } catch (Exception e) {
        }
    }

    private void readExitMove()
    {
        try {
            ExitMovementMessage msg = new ExitMovementMessage();
            writeMessage( msg,exitMoveIds);
            log.info("接收出口移动信号：从 {} 移动到 {} ", msg.getExSrc(), msg.getExDesc());
            logDataService.logInfo("TRACK","接收出口移动信号：从 {} 移动到 {} ", msg.getExSrc(), msg.getExDesc());
            opcReceiverHandler.onMessageReceived(OpcMessageType.EXIT_MOVEMENT,msg);
        } catch (Exception e) {
        }
    }

    private void readExitMeasure()
    {
        try {
            ExitMeasureMessage msg = new ExitMeasureMessage();
            writeMessage(msg,exitMeasureIds);
            log.info("接收出口称重信号：重量 {} ", msg.getWeight());
            logDataService.logInfo("TRACK","接收出口称重信号：重量 {} ", msg.getWeight());
            opcReceiverHandler.onMessageReceived(OpcMessageType.EXIT_MEASURE,msg);
        } catch (Exception e) {
        }
    }

    private void writeMessage(OpcMessage msg,Map<String,String> msgIds) throws Exception {
        List<String> ids = new ArrayList<>(msgIds.keySet());
        ids.remove("trigger");
        List<ReadWriteEntity> nodes =  miloService.readFromOpcUa(ids);
        nodes.forEach(item->{
            String fieldName = msgIds.get(item.getIdentifier());
            if (fieldName != null) {
                try {
                    Field field = msg.getClass().getDeclaredField(fieldName);
                    if (field.getType() == ExitCutTypeEnum.class) {
                        // 使用枚举类的自定义方法
                        field.setAccessible(true);
                        ExitCutTypeEnum enumValue = ExitCutTypeEnum.fromCode((int)item.getValue());
                        field.set(msg, enumValue);
                    } else {
                        // 其他类型正常处理
                        BeanUtils.setProperty(msg, fieldName, item.getValue());
                    }
                } catch (Exception e) {
                    log.error("BeanUtils设置属性失败: {}", fieldName, e);
                }
            }
        });
    }

}