package com.aps.service.impl;

import com.aps.entity.*;
import com.aps.service.ScheduleService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalDateTime;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;

@Service
public class ScheduleServiceImpl implements ScheduleService {
    private static final Logger logger = LoggerFactory.getLogger(ScheduleServiceImpl.class);

    // 缓存设备速度计算结果
    private final Map<RoutingDetailEquip, BigDecimal> equipmentSpeedCache = new ConcurrentHashMap<>();

    @Override
    public List<ScheduledTask> scheduleOrders(
            List<Mpsplannedorder> orders,
            List<RoutingHeader> processes,
            List<RoutingDetail> operations,
            List<RoutingDetailEquip> operationEquipments,
            List<Equipinfo> equipments) {

        long startTime = System.currentTimeMillis();
        logger.info("开始调度，订单数量: {}", orders.size());

        // 1. 初始化设备状态（只使用可用设备）
        Map<Integer, EquipmentScheduleState> equipmentStates = equipments.stream()
                .filter(e -> e.getStatus() != null && e.getStatus() == 0) // 只使用状态为0（可用）的设备
                .collect(Collectors.toMap(
                        Equipinfo::getId,
                        e -> new EquipmentScheduleState(e, LocalDateTime.now()),
                        (existing, replacement) -> existing
                ));

        // 2. 构建工序-设备关系映射
        Map<Integer, List<RoutingDetailEquip>> operationToEquipments = operationEquipments.stream()
                .collect(Collectors.groupingBy(RoutingDetailEquip::getRoutingDetailId));

        // 3. 按优先级和截止时间排序工单
        orders.sort(Comparator.comparing(Mpsplannedorder::getPriority)
                .thenComparing(Mpsplannedorder::getDuadate));

        List<ScheduledTask> scheduledTasks = new ArrayList<>();

        // 4. 处理每个工单
        for (Mpsplannedorder order : orders) {
            try {
                processOrder(order, processes, operations, operationToEquipments, equipmentStates, scheduledTasks);
            } catch (Exception e) {
                logger.error("工单 {} 调度失败: {}", order.getOrderid(), e.getMessage(), e);
                throw new RuntimeException("工单调度失败: " + order.getOrderid(), e);
            }
        }

        long endTime = System.currentTimeMillis();
        logger.info("调度完成，生成任务数量: {}, 耗时: {}ms", scheduledTasks.size(), (endTime - startTime));

        return scheduledTasks;
    }

    private void processOrder(Mpsplannedorder order,
                              List<RoutingHeader> processes,
                              List<RoutingDetail> operations,
                              Map<Integer, List<RoutingDetailEquip>> operationToEquipments,
                              Map<Integer, EquipmentScheduleState> equipmentStates,
                              List<ScheduledTask> scheduledTasks) {

        // 1. 匹配工艺
        RoutingHeader process = findProcess(order.getRoutingid(), processes);

        // 2. 获取该工艺的所有工序（严格按taskSeq排序）
        List<RoutingDetail> processOperations = operations.stream()
                .filter(op -> op.getRoutingHeaderId() != null &&
                        op.getRoutingHeaderId().equals(process.getId()))
                .sorted(Comparator.comparing(RoutingDetail::getTaskSeq))
                .collect(Collectors.toList());

        // 3. 记录上一道工序的结束时间（初始为工单计划开始时间）
        LocalDateTime lastOperationEndTime = order.getStartdate();

        // 4. 按顺序处理每道工序
        for (RoutingDetail operation : processOperations) {
            ScheduledTask task = scheduleOperation(
                    order,
                    operation,
                    operationToEquipments,
                    equipmentStates,
                    lastOperationEndTime
            );

            scheduledTasks.add(task);
            lastOperationEndTime = task.getEndTime(); // 更新上一道工序的结束时间
        }
    }

    private ScheduledTask scheduleOperation(Mpsplannedorder order,
                                            RoutingDetail operation,
                                            Map<Integer, List<RoutingDetailEquip>> operationToEquipments,
                                            Map<Integer, EquipmentScheduleState> equipmentStates,
                                            LocalDateTime lastOperationEndTime) {

        // 1. 获取该工序可用的设备
        List<RoutingDetailEquip> availableEquipments = operationToEquipments.getOrDefault(
                operation.getId(), Collections.emptyList());

        if (availableEquipments.isEmpty()) {
            throw new RuntimeException("工序 " + operation.getId() + " 没有配置可用设备");
        }

        // 2. 选择最优设备（按产出速度降序）
        RoutingDetailEquip selectedEquipment = selectFastestEquipment(availableEquipments);
        EquipmentScheduleState equipmentState = equipmentStates.get(selectedEquipment.getEquipId());

        if (equipmentState == null) {
            throw new RuntimeException("设备 " + selectedEquipment.getEquipId() + " 状态异常");
        }

        // 3. 计算时间范围（关键修改：考虑工序顺序约束）
        LocalDateTime startTime = calculateOperationStartTime(
                lastOperationEndTime,
                order.getStartdate(),
                equipmentState.getNextAvailableTime()
        );

        LocalDateTime endTime = calculateOperationEndTime(
                startTime,
                order.getQuantity(),
                selectedEquipment,
                operation.getSetupTime()
        );

        // 4. 更新设备状态
        equipmentState.getTaskQueue().add(new ScheduledTask()); // 只用于记录设备占用
        equipmentState.setNextAvailableTime(endTime);

        // 4. 创建任务
        ScheduledTask task = new ScheduledTask();
        task.setOrderId(order.getOrderid() != null ? order.getOrderid().toString() : "");
        task.setOperationId(operation.getId());
        task.setEquipId(selectedEquipment.getEquipId());
        task.setEquipName(equipmentState.getEquipName()); // 设置设备名称
        task.setStartTime(startTime);
        task.setEndTime(endTime);
        task.setQuantity(order.getQuantity() != null ? BigDecimal.valueOf(order.getQuantity()) : BigDecimal.ZERO);
        task.setOperationName(operation.getName());
        task.setRouteId(order.getRoutingid());
        return task;
    }

    // 关键方法：计算工序的实际开始时间
    private LocalDateTime calculateOperationStartTime(
            LocalDateTime lastOperationEndTime,      // 上一道工序的结束时间
            LocalDateTime orderPlannedStartTime,    // 工单计划开始时间
            LocalDateTime equipmentAvailableTime     // 设备可用时间
    ) {
        // 取最晚的时间：上一道工序结束时间、工单计划开始时间、设备可用时间
        return Collections.max(Arrays.asList(
                lastOperationEndTime,
                orderPlannedStartTime,
                equipmentAvailableTime
        ));
    }

    // 计算工序结束时间
    private LocalDateTime calculateOperationEndTime(
            LocalDateTime startTime,
            Integer quantity,
            RoutingDetailEquip equipment,
            BigDecimal setupTime
    ) {
        // 总时间= (数量 × 单件时间) + 准备时间
        BigDecimal totalHours = calculateTotalProcessingTime(quantity, equipment, setupTime);
        return startTime.plusMinutes(totalHours.multiply(BigDecimal.valueOf(60)).longValue());
    }

    private BigDecimal calculateTotalProcessingTime(
            Integer quantity,
            RoutingDetailEquip equipment,
            BigDecimal setupTime
    ) {
        // 参数校验
        if (equipment.getOutputQuantity() == null || equipment.getOutputQuantity().compareTo(BigDecimal.ZERO) <= 0) {
            throw new IllegalArgumentException("设备产出数量必须大于0");
        }
        if (equipment.getDuration() == null || equipment.getDuration().compareTo(BigDecimal.ZERO) <= 0) {
            throw new IllegalArgumentException("设备持续时间必须大于0");
        }
        if (quantity == null || quantity <= 0) {
            throw new IllegalArgumentException("工单数量必须大于0");
        }

        // 单件时间（小时）= (持续时间(秒)/产出数量)/3600
        BigDecimal singleItemTimeHours = equipment.getDuration()
                .divide(equipment.getOutputQuantity(), 10, RoundingMode.HALF_UP)
                .divide(BigDecimal.valueOf(3600), 10, RoundingMode.HALF_UP);

        // 总加工时间= 数量 × 单件时间
        BigDecimal processingTime = BigDecimal.valueOf(quantity)
                .multiply(singleItemTimeHours);

        // 加上准备时间（如果存在）
        if (setupTime != null && setupTime.compareTo(BigDecimal.ZERO) > 0) {
            return processingTime.add(setupTime);
        }
        return processingTime;
    }

    // 其他辅助方法保持不变
    private RoutingDetailEquip selectFastestEquipment(List<RoutingDetailEquip> equipments) {
        return equipments.stream()
                .max(Comparator.comparing(this::calculateEquipmentSpeed))
                .orElseThrow(() -> new RuntimeException("没有可用的设备"));
    }

    private BigDecimal calculateEquipmentSpeed(RoutingDetailEquip equipment) {
        return equipmentSpeedCache.computeIfAbsent(equipment, eq -> {
            if (eq.getOutputQuantity() == null || eq.getDuration() == null ||
                    eq.getOutputQuantity().compareTo(BigDecimal.ZERO) <= 0 ||
                    eq.getDuration().compareTo(BigDecimal.ZERO) <= 0) {
                return BigDecimal.ZERO;
            }

            return eq.getOutputQuantity()
                    .divide(eq.getDuration(), 10, RoundingMode.HALF_UP)
                    .multiply(BigDecimal.valueOf(3600));
        });
    }

    private RoutingHeader findProcess(String routingCode, List<RoutingHeader> processes) {
        return processes.stream()
                .filter(p -> p.getCode() != null && p.getCode().equals(routingCode))
                .min(Comparator.comparing(RoutingHeader::getCreationTime))
                .orElseThrow(() -> new RuntimeException("工艺不存在: " + routingCode));
    }
}