package com.fizz.business.service.strip;

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.TrackService;
import com.fizz.business.service.impl.SegmentService;
import com.fizz.business.utils.MatmapUtil;
import com.fizz.business.utils.WebSocketUtil;
import com.fizz.business.vo.SegmentParamVO;
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.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.WELDER;

@Service
@Slf4j
@RequiredArgsConstructor
public class SegmentTrackerService {

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

    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;

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

        // 1. 首次测量初始化或新卷判定
        BigDecimal weldDev;
        if (firstMeasure || entryLengthAtWelder.compareTo(weldLength) < 0) {
            weldDev = entryLengthAtWelder; // 新卷偏移量等于当前长度
            coilSegNum = 1;
            weldLength = entryLengthAtWelder;
            firstMeasure = false;
            listSegment.clear(); // 清空旧段
            coilReachedDevices.remove(coilId); // 清空旧钢卷的快照记录
            log.info("New coil detected or initialized:{} " ,coilId);
        } else {
            weldDev = entryLengthAtWelder.subtract(weldLength);
        }

        // 更新焊机长度
        weldLength = entryLengthAtWelder;

        // trace 控制
        traceCount++;
        if (traceCount > 5) {
            log.info(String.format("Trace: weldLen=%.2f, speed=%s",
                    entryLengthAtWelder, procStripSpeed(entry, furnace, exit)));
            traceCount = 0;
        }


        // 2. 检查是否达到生成新段的条件
        BigDecimal segThreshold = BigDecimal.valueOf(coilSegNum * SEGSTRIPLEN);
        if (entryLengthAtWelder.compareTo(segThreshold) >= 0) {

            // 2.1. 创建新的 SegmentDTO 并初始化基本位置
            SegmentDTO newSeg = createNewSegment(coilId, entryLengthAtWelder);

            // 2.3. 将新段添加到队列中
            listSegment.addLast(newSeg);
            if (listSegment.size() > MAX_SEG_COUNT) {
                listSegment.removeFirst();
            }
            coilSegNum++;
        }

        // 3. 处理队列中所有已存在的段
        treatSegAsync(entry, furnace, coat, exit, weldDev);

    }

    /**
     * 【核心功能】
     * 遍历所有已存在的段，更新其位置，并累积其在设备区域内的实时数据。
     * 这部分逻辑只负责“移动”和“累积”。
     */
    public void treatSeg(AppMeasureEntryMessage entry,
                         AppMeasureFurnaceMessage furnace,
                         AppMeasureCoatMessage coat,
                         AppMeasureExitMessage exit,
                         BigDecimal weldDev) {
        if (listSegment.isEmpty()) {
            return;
        }

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

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

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

            // 遍历所有设备，累积段在设备区域内的数据
            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) {

                    double currentSpeed = getSpeedForDevice(device, entry, coat, exit);
                    if (currentSpeed > LOWSPEEDLIMIT) {
                        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) {
                System.out.println("钢卷 " + 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) {
        if (device.getDesc().contains("开卷机") || device.getDesc().contains("活套") || device.getDesc().contains("焊机")) {
            return entry;
        } else if (device.getDesc().contains("清洗段") || device.getDesc().contains("退火炉")) {
            return furnace;
        } else if (device.getDesc().contains("涂机")) {
            return coat;
        } else {
            return exit;
        }
    }

    private double getSpeedForDevice(DeviceEnum device, AppMeasureEntryMessage entry, AppMeasureCoatMessage coat, AppMeasureExitMessage exit) {
        if (device.getDesc().contains("开卷机") || device.getDesc().contains("活套") || device.getDesc().contains("焊机")) {
            return entry != null && entry.getStripSpeed() != null ? entry.getStripSpeed().doubleValue() : 0.0;
        } else if (device.getDesc().contains("清洗段") || device.getDesc().contains("退火炉")) {
            return coat != null && coat.getStripSpeedTmExit() != null ? coat.getStripSpeedTmExit().doubleValue() : 0.0;
        } else {
            return exit != null && exit.getSpeedExitSection() != null ? exit.getSpeedExitSection().doubleValue() : 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);
        }
    }

    /**
     * 【新方法】
     * 专门用于处理钢卷头部在设备间的移动和物料跟踪。
     * 这部分逻辑包含了对 Redis/DB 的写入，最适合异步化。
     */
    @Async("taskExecutor")
    public void trackCoilHeadPosition(String coilId, BigDecimal headPos,
                                      AppMeasureEntryMessage entry, AppMeasureExitMessage exit) {

        Set<DeviceEnum> prevReached = coilReachedDevices.computeIfAbsent(coilId, k -> Collections.newSetFromMap(new ConcurrentHashMap<>()));

        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);

            // 判断钢卷的头部是否首次到达该设备
            if (headPos.compareTo(BigDecimal.valueOf(dynPos)) >= 0) {
                if (!prevReached.contains(d)) {

                    // 1. 如果是焊机，则调用 CRM 更新计划状态
                    if (d == WELDER) {

                        MatmapDTO matmap = MatmapUtil.getMatmap(WELDER.getIdx());
                        trackService.l1OperateMat(L1OperateMatForm.builder()
                                .entryMatId(coilId)
                                .planId(matmap.getPlanId())
                                .porIdx(entry.getPayOffReelNumber())
                                .operation(L1OperateMatEnum.PRODUCING)
                                .build());
                    }

                    // 2. 更新 Matmap
                    MatmapUtil.setMatId(d.getIdx(), coilId);
                    WebSocketUtil.sendMatmapMsg();
                    // 3. 标记为已到达，防止重复操作
                    prevReached.add(d);
                }
            }
        }
    }


}