package com.fizz.business.service.strip;

import cn.hutool.json.JSONUtil;
import com.fizz.business.constants.enums.DeviceEnum;
import com.fizz.business.constants.enums.L1OperateMatEnum;
import com.fizz.business.domain.msg.AppMeasureCoatMessage;
import com.fizz.business.domain.msg.AppMeasureEntryMessage;
import com.fizz.business.domain.msg.AppMeasureExitMessage;
import com.fizz.business.domain.msg.AppMeasureFurnaceMessage;
import com.fizz.business.dto.MatmapDTO;
import com.fizz.business.dto.SegValue;
import com.fizz.business.dto.SegmentDTO;
import com.fizz.business.form.L1OperateMatForm;
import com.fizz.business.service.LogDataService;
import com.fizz.business.service.ProMatmapService;
import com.fizz.business.service.TrackService;
import com.fizz.business.service.impl.SegmentService;
import com.fizz.business.utils.MatmapUtil;
import com.fizz.business.utils.WebSocketUtil;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;

import java.lang.reflect.Method;
import java.lang.reflect.Field;
import java.math.BigDecimal;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentLinkedDeque;
import java.util.concurrent.ConcurrentMap;

import static com.fizz.business.constants.enums.DeviceEnum.FUR4;

@Service
@Slf4j
@RequiredArgsConstructor
public class SegmentTrackerService {

    private final StripPositionService stripPositionService;
    private final TrackService trackService;
    private final SegmentService segmentService; // 注入新创建的段服务
    private final LogDataService logDataService; // 注入新创建的段服务
    private final ProMatmapService proMatmapService; // 注入新创建的段服务

    private static final long intervalTime = 10000; // 每 10 秒写一次
    private int traceCount = 0;
    private boolean firstMeasure = true;
    private BigDecimal weldLength = BigDecimal.ZERO;
    private int coilSegNum = 1;

    private final Deque<SegmentDTO> listSegment = new ConcurrentLinkedDeque<>();
    private static final double SEGSTRIPLEN = 2.0;
    private static final int MAX_SEG_COUNT = 2000;
    private final ConcurrentMap<String, Set<DeviceEnum>> coilReachedDevices = new ConcurrentHashMap<>();
    private static final double LOWSPEEDLIMIT = 0.05;

    // 定义一个缓存记录上次写入日志的时间
    private final Map<String, Long> lastLogTimeMap = new ConcurrentHashMap<>();

    // --- 定义一个缓存，用于控制同一 CoilId 新卷日志写入频率 ---
    private final Map<String, Long> lastNewCoilLogTimeMap = new ConcurrentHashMap<>();

    // 放在类的字段中，全局共享
    private final Map<String, Long> lastTrackLogTimeMap = new ConcurrentHashMap<>();

    /**
     * 【主入口】
     * 负责处理新的测量数据，生成新段，并对新到达的设备做一次性快照。
     */
    public void handleMeasure(String coilId,
                              BigDecimal entryLengthAtWelder,
                              AppMeasureEntryMessage entry,
                              AppMeasureFurnaceMessage furnace,
                              AppMeasureCoatMessage coat,
                              AppMeasureExitMessage exit) {

        long now = System.currentTimeMillis();

        try {
            log.warn(">>> trackCoilHeadPosition start, thread: {}, coilId: {}", Thread.currentThread().getName(), coilId);
            trackCoilHeadPosition(coilId, entryLengthAtWelder, entry, exit);
        } catch (Exception e) {
            log.error("trackCoilHeadPosition 执行异常，coilId=" + coilId, e);
        }

        Long lastTime = lastLogTimeMap.get(coilId);
        if (lastTime == null || now - lastTime > intervalTime) {
            lastLogTimeMap.put(coilId, now);
            log.info("【TRACK-START】接收到测量数据 -> CoilId: {}, LengthAtWelder: {}", coilId, entryLengthAtWelder);
            logDataService.logInfo("WELDER", "接收到测量数据 -> CoilId: " + coilId + ", LengthAtWelder: " + entryLengthAtWelder);
        }
        // 4. trackCoilHeadPosition 同步执行

        // 1. 新卷或首次测量初始化
        BigDecimal weldDev;
        if (firstMeasure || entryLengthAtWelder.compareTo(weldLength) < 0) {
            weldDev = entryLengthAtWelder;
            coilSegNum = 1;
            weldLength = entryLengthAtWelder;
            firstMeasure = false;
            listSegment.clear();
            coilReachedDevices.remove(coilId);

            long now1 = System.currentTimeMillis();
            Long lastTime1 = lastNewCoilLogTimeMap.get(coilId);
            if (lastTime1 == null || now1 - lastTime1 > intervalTime) {
                lastNewCoilLogTimeMap.put(coilId, now1);
                log.info("【WELDER】检测到新钢卷或初始化 -> CoilId: {}", coilId);
                logDataService.logInfo("WELDER", "检测到新钢卷或初始化 -> CoilId: " + coilId);
            }
        } else {
            weldDev = entryLengthAtWelder.subtract(weldLength);
        }

        weldLength = entryLengthAtWelder;

        // 2. 新段生成逻辑
        BigDecimal segThreshold = BigDecimal.valueOf(coilSegNum * SEGSTRIPLEN);
        if (entryLengthAtWelder.compareTo(segThreshold) >= 0) {
            SegmentDTO newSeg = createNewSegment(coilId, entryLengthAtWelder);
            listSegment.addLast(newSeg);
            if (listSegment.size() > MAX_SEG_COUNT) listSegment.removeFirst();
            log.info("【TRACK】新段生成 -> CoilId: {}, SegmentNo: {}, StartLen: {}", coilId, coilSegNum, entryLengthAtWelder);
            logDataService.logInfo("WELDER", "新段生成 -> CoilId: " + coilId + ", SegmentNo: " + coilSegNum + ", StartLen: " + entryLengthAtWelder);
            coilSegNum++;
        }

        // 3. 更新已有段
        treatSeg(entry, furnace, coat, exit, weldDev);


        // 5. 完成日志
        log.info("【TRACK-END】CoilId: {}, 当前长度: {}, 已生成段数: {}", coilId, entryLengthAtWelder, coilSegNum);
        logDataService.logInfo("TRACK", "处理完成 -> CoilId: " + coilId + ", 当前长度: " + entryLengthAtWelder + ", 已生成段数: " + coilSegNum);
    }


    /**
     * 【核心功能】
     * 遍历所有已存在的段，更新其位置，并累积其在设备区域内的实时数据。
     * 这部分逻辑只负责“移动”和“累积”。
     */
    public void treatSeg(AppMeasureEntryMessage entry,
                         AppMeasureFurnaceMessage furnace,
                         AppMeasureCoatMessage coat,
                         AppMeasureExitMessage exit,
                         BigDecimal weldDev) {
        if (listSegment.isEmpty()) {
            // === 【处理开始日志】===
            log.warn("【TRACK-TREAT】,listSegment为空，直接返回");

            return;
        }

        BigDecimal celLength = entry != null ? entry.getCelLength() : BigDecimal.ZERO;
        BigDecimal cxlLength = exit != null ? exit.getCxlLength() : BigDecimal.ZERO;

        // === 【处理开始日志】===
        log.info("【TRACK-TREAT】开始处理段数据，共 {} 段，weldDev={}，celLength = {}，cxlLength = {}。 ", listSegment.size(), weldDev, celLength, cxlLength);
        logDataService.logInfo("TRACK", "开始处理段数据，共 " + listSegment.size() + " 段，weldDev=" + weldDev);

        Iterator<SegmentDTO> iterator = listSegment.descendingIterator();
        while (iterator.hasNext()) {
            SegmentDTO segment = iterator.next();

            // 更新段在产线上的位置
            segment.setHeadPos(segment.getHeadPos().add(weldDev));
            segment.setTailPos(segment.getTailPos().add(weldDev));

            // 根据 SourceType 读取对应测量对象数据，写入段的 totalValues（Map 形式）
            EnumMap<DeviceEnum.SourceType, Object> sourceMessageMap = new EnumMap<>(DeviceEnum.SourceType.class);
            sourceMessageMap.put(DeviceEnum.SourceType.ENTRY, entry);
            sourceMessageMap.put(DeviceEnum.SourceType.FURNACE, furnace);
            sourceMessageMap.put(DeviceEnum.SourceType.COAT, coat);
            sourceMessageMap.put(DeviceEnum.SourceType.EXIT, exit);
            sourceMessageMap.values().forEach(message -> accumulateMessageFields(segment, message));

            // 遍历所有设备，累积段在设备区域内的数据
            for (DeviceEnum device : DeviceEnum.values()) {
                double currentDevicePos = stripPositionService.calculate(device, celLength, cxlLength);

                // 判断段是否位于设备的“长区域”内
                if (segment.getHeadPos().compareTo(BigDecimal.valueOf(currentDevicePos)) > 0 &&
                        segment.getTailPos().compareTo(BigDecimal.valueOf(currentDevicePos)) < 0) {

                    log.info("【TRACK-TREAT】段 {} 进入设备区域 [{}]，当前设备长度：{}", segment.getSegNo(), device.name(), currentDevicePos);
                    logDataService.logInfo("TRACK",
                            "段 " + segment.getSegNo() + " 进入设备区域 [" + device.name() + "]，当前设备长度：" + currentDevicePos);
                    double currentSpeed = getSpeedForDevice(device, entry, coat, exit);
                    if (currentSpeed > LOWSPEEDLIMIT) {
                        log.info("【TRACK-TREAT】段 {} 速度大于基准速度， 进入设备区域 [{}]，当前速度：{}", segment.getSegNo(), device.name(), currentSpeed);
                        logDataService.logInfo("TRACK",
                                "段 " + segment.getSegNo() + " 进入设备区域 [" + device.name() + "]，当前速度：" + currentSpeed);
                        for (String fieldName : device.getParamFields()) {
                            Object message = getMessageForDevice(device, entry, furnace, coat, exit);
                            if (message != null) {
                                BigDecimal value = getFieldFromMessage(message, fieldName);
                                setSegValue(segment.getTotalValues(), fieldName, value);
                            }
                        }
                    }
                }
            }


            double exitPlantPos = stripPositionService.calculate(DeviceEnum.TR, celLength, cxlLength);
            if (segment.getTailPos().compareTo(BigDecimal.valueOf(exitPlantPos)) >= 0) {
                log.info("【TRACK-END】钢卷 {} 的段号 {} 已离开产线，开始持久化数据{}。",
                        segment.getEnCoilID(), segment.getSegNo(), JSONUtil.toJsonStr(segment.getTotalValues()));
                logDataService.logInfo("SEGMENT",
                        "钢卷 " + segment.getEnCoilID() + " 的段号 " + segment.getSegNo() + " 已离开产线，开始持久化数据。");
                segmentService.saveTotalSegment(segment); // 调用服务进行持久化
                iterator.remove();
            }
        }
    }

    // --- 新增的辅助方法，将逻辑封装起来 ---

    /**
     * 【异步方法】
     * 异步处理队列中所有已存在的段，更新其位置和累积数据。
     */
    @Async("taskExecutor") // 指定使用名为 "taskExecutor" 的线程池
    public void treatSegAsync(AppMeasureEntryMessage entry,
                              AppMeasureFurnaceMessage furnace,
                              AppMeasureCoatMessage coat,
                              AppMeasureExitMessage exit,
                              BigDecimal weldDev) {
        // 调用原有的同步方法
        this.treatSeg(entry, furnace, coat, exit, weldDev);
    }


    /**
     * 创建并初始化一个新的 SegmentDTO。
     */
    private SegmentDTO createNewSegment(String coilId, BigDecimal entryLengthAtWelder) {
        SegmentDTO seg = new SegmentDTO();
        seg.setSegNo(coilSegNum);
        seg.setEnCoilID(coilId);

        if (coilSegNum == 1) {
            // 第一个段的特殊处理
            seg.setStartPos(BigDecimal.ZERO);
            seg.setEndPos(entryLengthAtWelder);
            seg.setSegLen(entryLengthAtWelder);
            seg.setHeadPos(entryLengthAtWelder);
            seg.setTailPos(BigDecimal.ZERO);
        } else {
            // 后续段的计算
            SegmentDTO prev = listSegment.peekLast();
            seg.setStartPos(prev.getEndPos());
            seg.setEndPos(entryLengthAtWelder);
            seg.setSegLen(seg.getEndPos().subtract(seg.getStartPos()));
            // 这两个位置是全线绝对位置，后续会通过 weldDev 动态更新
            seg.setHeadPos(entryLengthAtWelder);
            seg.setTailPos(seg.getEndPos().subtract(seg.getSegLen()));
        }
        return seg;
    }


    private Object getMessageForDevice(DeviceEnum device,
                                       AppMeasureEntryMessage entry,
                                       AppMeasureFurnaceMessage furnace,
                                       AppMeasureCoatMessage coat,
                                       AppMeasureExitMessage exit) {
        switch (device.getSourceType()) {
            case ENTRY:
                return entry;
            case FURNACE:
                return furnace;
            case COAT:
                return coat;
            case EXIT:
                return exit;
            default:
                return null;
        }
    }


    private double getSpeedForDevice(DeviceEnum device,
                                     AppMeasureEntryMessage entry,
                                     AppMeasureCoatMessage coat,
                                     AppMeasureExitMessage exit) {
        switch (device.getSectionType()) {
            case ENTRY:
                // 入口段设备：速度来自 entry
                return entry != null && entry.getStripSpeed() != null
                        ? entry.getStripSpeed().doubleValue()
                        : 0.0;

            case PROCESS:

                return coat != null && coat.getStripSpeedTmExit() != null
                        ? coat.getStripSpeedTmExit().doubleValue()
                        : 0.0;

            case EXIT:
                // 出口段设备：速度来自 exit
                return exit != null && exit.getSpeedExitSection() != null
                        ? exit.getSpeedExitSection().doubleValue()
                        : 0.0;
            default:
                return 0.0;
        }
    }


    private double procStripSpeed(AppMeasureEntryMessage entry, AppMeasureFurnaceMessage furnace, AppMeasureExitMessage exit) {
        if (entry != null && entry.getStripSpeed() != null) return entry.getStripSpeed().doubleValue();
//        if (furnace != null && furnace.getStripSpeed() != null) return furnace.getStripSpeed().doubleValue();
        if (exit != null && exit.getSpeedExitSection() != null) return exit.getSpeedExitSection().doubleValue();
        return 0.0;
    }

    private BigDecimal getFieldFromMessage(Object message, String fieldName) {
        if (message == null || fieldName == null) return null;
        try {
            String getterName = "get" + fieldName.substring(0, 1).toUpperCase() + fieldName.substring(1);
            Method getter = message.getClass().getMethod(getterName);
            Object value = getter.invoke(message);

            if (value instanceof BigDecimal) {
                return (BigDecimal) value;
            } else if (value instanceof Number) {
                return BigDecimal.valueOf(((Number) value).doubleValue());
            } else {
                return null;
            }
        } catch (Exception e) {
            System.err.println("Error getting field " + fieldName + " from message: " + e.getMessage());
            return null;
        }
    }

    private void setSegValue(Map<String, SegValue> segValues, String key, BigDecimal value) {
        if (value != null) {
            segValues.computeIfAbsent(key, k -> new SegValue()).add(value);
        }
    }

    /**
     * 将测量对象中的数值字段累积到段的 totalValues 中
     */
    private void accumulateMessageFields(SegmentDTO segment, Object message) {
        if (segment == null || message == null) {
            return;
        }
        for (Field field : message.getClass().getDeclaredFields()) {
            try {
                field.setAccessible(true);
                Object val = field.get(message);
                if (val instanceof BigDecimal) {
                    setSegValue(segment.getTotalValues(), field.getName(), (BigDecimal) val);
                } else if (val instanceof Number) {
                    setSegValue(segment.getTotalValues(), field.getName(), BigDecimal.valueOf(((Number) val).doubleValue()));
                }
            } catch (IllegalAccessException ignored) {
            }
        }
    }

    // 本地缓存，每个 coilId 对应已到达设备
    public void trackCoilHeadPosition(String coilId, BigDecimal headPos,
                                      AppMeasureEntryMessage entry, AppMeasureExitMessage exit) {

        log.warn(">>> trackCoilHeadPosition 当前线程：{}", Thread.currentThread().getName());

        if (LogRateLimiter.shouldLog("TRACK:" + coilId, 5000)) {
            log.info("焊缝位置匹配逻辑，当前焊缝长度{}，", headPos);
            logDataService.logInfo("MATMAP-TRACK", "CoilId=" + coilId + " ...");
        }

        Set<DeviceEnum> prevReached = coilReachedDevices.computeIfAbsent(coilId,
                k -> Collections.newSetFromMap(new ConcurrentHashMap<>()));
        if (entry.getPayOffReelNumber()-1 == DeviceEnum.POR1.getIdx()) {
            prevReached.remove(DeviceEnum.POR2);
        } else {
            prevReached.remove(DeviceEnum.POR1);
        }

        if (LogRateLimiter.shouldLog("TRACK:" + coilId, 5000)) {
            logDataService.logInfo("MATMAP-TRACK", "构建的本地matmap缓存为=" + JSONUtil.toJsonStr(prevReached) + " ...");
        }

        BigDecimal celLength = entry != null ? entry.getCelLength() : BigDecimal.ZERO;
        BigDecimal cxlLength = exit != null ? exit.getCxlLength() : BigDecimal.ZERO;

        for (DeviceEnum d : DeviceEnum.values()) {
            double dynPos = stripPositionService.calculate(d, celLength, cxlLength);
            log.info("焊缝位置匹配逻辑，当前焊缝长度{}，当前计算的设备长度：{}", headPos, dynPos);

            if (headPos.compareTo(BigDecimal.valueOf(dynPos)) >= 0 && !prevReached.contains(d)) {

                MatmapDTO matmap = MatmapUtil.getMatmap(entry.getPayOffReelNumber()-1);
                MatmapUtil.setMatId(d.getIdx(), coilId,matmap.getPlanId());

                if (d == DeviceEnum.WELDER) {
                    logDataService.logInfo("MATMAP-TRACK", "钢卷到达焊机，更新钢卷计划状态coil=" + coilId + "当前长度为：" + headPos);
                    log.info("matmap.planid={},coilid={}", matmap.getPlanId(), coilId);
                    trackService.l1OperateMat(L1OperateMatForm.builder()
                            .entryMatId(coilId)
                            .planId(matmap.getPlanId())
                            .porIdx(entry.getPayOffReelNumber())
                            .operation(L1OperateMatEnum.PRODUCING)
                            .build());
                } else {
                    logDataService.logInfo("MATMAP-TRACK", "钢卷:" + coilId + "到达设备:" + d.getDesc() + "当前长度为：" + headPos);
                    proMatmapService.flushMatmap();
                    WebSocketUtil.sendMatmapMsg();
                }

                prevReached.add(d); // 标记为已到达
            }
        }
    }




}
