package com.aps.service.Algorithm;

import com.aps.common.util.ProductionDeepCopyUtil;
import com.aps.entity.Algorithm.Chromosome;
import com.aps.entity.Algorithm.GAScheduleResult;
import com.aps.entity.Algorithm.KpiMetrics;
import com.aps.entity.basic.Machine;
import com.aps.entity.basic.Order;
import com.aps.entity.basic.TimeSegment;

import java.time.Duration;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 作者：佟礼
 * 时间：2025-12-10
 */
public class KpiCalculator {
    private Chromosome chromosome;
    private LocalDateTime baseTime;
    public KpiCalculator(Chromosome _chromosome)
    {
        chromosome=_chromosome;
        baseTime=_chromosome.getBaseTime();
    }
    public void calculatekpi() {
        calculateMachine();
        calculateOrder();
        List<KpiMetrics> kpiMetrics = new ArrayList<>();
        //最大设备利用率
        double machineMaxRate = chromosome.getMachines().stream()
                .mapToDouble(Machine::getRate)
                .max()
                .orElse(0);

        addKpi(kpiMetrics,"最大设备利用率", Math.round(machineMaxRate * 100.0) / 100.0, "实际工作时间/计划工作时间 最大", 0, 1);
        //最小设备利用率
        double machineMinRate = chromosome.getMachines().stream()
                .mapToDouble(Machine::getRate)
                .min()
                .orElse(0);
        addKpi(kpiMetrics,"最小设备利用率", Math.round(machineMinRate * 100.0) / 100.0, "实际工作时间/计划工作时间 最小", 0, 1);

        double machineActualWorkTime = chromosome.getMachines().stream()
                .mapToDouble(Machine::getActualWorkTime)
                .sum();
        double machineallWorkTime= calculateAllTimeSegmentsTotalDuration();
        addKpi(kpiMetrics,"设备等待时间", (machineallWorkTime-machineActualWorkTime)/60/60, "生产周期内，设备时长 与 （设备所有工序时长+设备停机时长）之间的差值", 2, 1);

        // 订单按时完成的数量
        double orderNoDelayCount = chromosome.getOrders().stream()
                .filter(t -> t.getDelayHours() <= 0)
                .count();

        double orderOnTime = Math.round(orderNoDelayCount * 10000.0 / chromosome.getOrders().size()) / 100.0;
        addKpi(kpiMetrics,"订单按时完成率", orderOnTime, "规定时间内交付的订单数量/所有订单数量", 0, 1);
        //最大延迟
        double OrderMaxDelay = chromosome.getOrders().stream()
                .mapToDouble(Order::getDelayHours)
                .max()
                .orElse(0);
        addKpi(kpiMetrics,"最大延迟", OrderMaxDelay, "所有订单的延迟时间最大值", 2, 1);
        //最小延迟
        double orderMinDelay = chromosome.getOrders().stream()
                .mapToDouble(Order::getDelayHours)
                .min()
                .orElse(0);
        addKpi(kpiMetrics,"最小延迟", orderMinDelay, "所有订单的延迟时间最小值", 2, 1);

        double orderSumDelay = chromosome.getOrders().stream()
                .mapToDouble(Order::getDelayHours)
                .sum();
        //平均延迟
        double OrderDelay = Math.round(orderSumDelay / chromosome.getOrders().size() * 100.0) / 100.0;
        addKpi(kpiMetrics,"平均延迟", OrderDelay, "所有订单的延迟时间平均值", 2, 1);
        //最长生产周期
        double orderMaxProductTime = chromosome.getOrders().stream()
                .mapToDouble(Order::getOrderProductTime)
                .max()
                .orElse(0);
        addKpi(kpiMetrics,"最长生产周期", orderMaxProductTime, "订单在计划中的实际存续时长", 2, 1);
        //最小生产周期
        double orderMinProductTime = chromosome.getOrders().stream()
                .mapToDouble(Order::getOrderProductTime)
                .min()
                .orElse(0);
        addKpi(kpiMetrics,"最短生产周期", orderMinProductTime, "订单在计划中的实际存续时长", 2, 1);

        double orderSumProductTime = chromosome.getOrders().stream()
                .mapToDouble(Order::getOrderProductTime)
                .sum();
        //平均生产周期
        double OrderProductTime= Math.round(orderSumProductTime / chromosome.getOrders().size() * 100.0) / 100.0;
        addKpi(kpiMetrics,"平均生产周期", OrderProductTime, "订单在计划中的实际存续时长", 2, 1);

        double wip = calculateAverageDailyEndWipQuantity();
        addKpi(kpiMetrics, "平均WIP", wip, "每天23:59已开始未完成工序的产品数量平均值，单位为数量", 1, 1);

        //最大流量
        double orderMaxFlow = chromosome.getOrders().stream()
                .mapToDouble(Order::getOrderFlow)
                .max()
                .orElse(0);
        addKpi(kpiMetrics,"最大流量", orderMaxFlow, "订单的最短理论耗时与其在计划中的实际耗时的比值最大值", 0, 1);

        //最小流量
        double orderMinFlow = chromosome.getOrders().stream()
                .mapToDouble(Order::getOrderFlow)
                .min()
                .orElse(0);
        addKpi(kpiMetrics,"最小流量", orderMinFlow, "订单的最短理论耗时与其在计划中的实际耗时的比值最小值", 0, 1);


        double orderSumFlow = chromosome.getOrders().stream()
                .mapToDouble(Order::getOrderFlow)
                .sum();
        //平均流量
        double OrderFlow= Math.round(orderSumFlow / chromosome.getOrders().size() * 100.0) / 100.0;
        addKpi(kpiMetrics,"平均流量", OrderFlow, "订单的最短理论耗时与其在计划中的实际耗时的比值", 0, 1);


        //总换型
        double totalChangeOverTime = chromosome.getResult().stream()
                .mapToDouble(GAScheduleResult::getChangeOverTime)
                .sum();
        addKpi(kpiMetrics,"换型时间", totalChangeOverTime, "总的换型时间", 2, 1);
        //总换型次数
        long changeOverTimeCount = chromosome.getResult().stream()
                .mapToLong(GAScheduleResult::getChangeOverTime)
                .filter(changeOverTime -> changeOverTime > 0)
                .count();
        addKpi(kpiMetrics,"换型次数", changeOverTimeCount, "总的换型次数", 1, 1);
        chromosome.setKpiMetrics(kpiMetrics);

    }


    /**
     * 计算每台设备的利用率（实际工作时间/计划工作时间）
     *
     * @return 设备ID与利用率的Map
     */
    private void calculateMachine() {

        // 按设备ID分组工序列表（对应C# GroupBy + ToList）- Java 8兼容方式，处理null键
        List<GAScheduleResult> list= chromosome.getResult() ;
        Map<Long, List<GAScheduleResult>> machineTasks = new HashMap<>();
        list.forEach(result -> {
            Long machineId = result.getMachineId();
            machineTasks.computeIfAbsent(machineId, k -> new ArrayList<>()).add(result);
        });

        // 计划工作时间：最后一个任务结束时间 - 第一个任务开始时间
        int firstTaskStart = list.stream()
                .mapToInt(GAScheduleResult::getStartTime)
                .min()
                .orElse(0);
        int lastTaskEnd =  list.stream()
                .mapToInt(GAScheduleResult::getEndTime)
                .max()
                .orElse(0);
        double plannedWorkTime = lastTaskEnd - firstTaskStart;

        // 遍历每个设备的任务组计算利用率
        for (Map.Entry<Long, List<GAScheduleResult>> entry : machineTasks.entrySet()) {
            Long machineId = entry.getKey();
            List<GAScheduleResult> tasks = entry.getValue();

            // 按开始时间排序（对应C# OrderBy + ToList）
            List<GAScheduleResult> sortedTasks = tasks.stream()
                    .sorted(Comparator.comparingInt(GAScheduleResult::getStartTime))
                    .collect(Collectors.toList());

            // 计算实际工作时间：所有任务加工时间总和
            double actualWorkTime = sortedTasks.stream()
                    .mapToDouble(GAScheduleResult::getProcessingTime)
                    .sum();

            Machine machine=  chromosome.getMachines().stream()
                    .filter(t->t.getId()==machineId)
                    .findFirst()
                    .orElse(null);
            if(machine==null)
            {
                continue;

            }
            machine.setActualWorkTime(actualWorkTime);

//            // 计划工作时间：最后一个任务结束时间 - 第一个任务开始时间
//            int firstTaskStart = sortedTasks.get(0).getStartTime();
//            int lastTaskEnd = sortedTasks.get(sortedTasks.size() - 1).getEndTime();
//            double plannedWorkTime = lastTaskEnd - firstTaskStart;

            double rate =  Math.round((actualWorkTime / plannedWorkTime) * 10000) / 10000.0*100;
            machine.setRate(rate);
        }


    }

    /**
     * 计算每个订单
     *
     * @return 订单ID与延迟时间（小时）的Map
     */
    private void calculateOrder() {
        List<GAScheduleResult> list= chromosome.getResult();
        // 按GroupId（订单ID）分组 - Java 8兼容方式，处理null键
        Map<String, List<GAScheduleResult>> orderGroups = new HashMap<>();
        list.forEach(result -> {
            String orderId = result.getOrderId();
            orderGroups.computeIfAbsent(orderId, k -> new ArrayList<>()).add(result);
        });

        for (Map.Entry<String, List<GAScheduleResult>> entry : orderGroups.entrySet()) {
            String orderId = entry.getKey();
            List<GAScheduleResult> processList = entry.getValue();

            // 订单实际完工时间（该订单所有工序的最大结束时间）
            int orderCompletion = processList.stream()
                    .mapToInt(GAScheduleResult::getEndTime)
                    .max()
                    .orElse(0);
            // 订单实际开工时间（该订单所有工序的最早开工时间）
            int orderStart = processList.stream()
                    .mapToInt(GAScheduleResult::getStartTime)
                    .min()
                    .orElse(0);

            LocalDateTime actualCompletionTime = baseTime.plusSeconds(orderCompletion);
            LocalDateTime actualStartTime = baseTime.plusSeconds(orderStart);

            double orderProcessingTime = processList.stream()
                    .mapToDouble(GAScheduleResult::getProcessingTime)
                    .sum();

            Order order=  chromosome.getOrders().stream()
                    .filter(t->t.getOrderId().equals(orderId))
                    .findFirst()
                    .orElse(null);
            if(order==null)
            {
                continue;

            }


            order.setOrderCompletion(actualCompletionTime);
            order.setOrderStart(actualStartTime);
            order.setMachineProcessingTime(orderProcessingTime);

            Duration Orderduration = Duration.between(actualStartTime,actualCompletionTime);
            double orderProductTime  =Orderduration.getSeconds();
            order.setOrderProductTime(orderProductTime);
            double flow=Math.round(orderProcessingTime/orderProductTime *10000.0)/10000.0*100.0;

            order.setOrderFlow(flow);
            // 计算延迟时间（若提前完成则延迟为0）
            if (actualCompletionTime.isAfter(order.getDueDate())) {
                Duration duration = Duration.between(order.getDueDate(), actualCompletionTime);
                double delayHours = duration.getSeconds(); // 转换为秒
                order.setDelayHours(delayHours);
            } else {
                Duration duration = Duration.between(actualCompletionTime,order.getDueDate());
                double delayHours = duration.getSeconds(); // 转换为秒
                order.setDelayHours(0-delayHours);
            }
        }




    }

    /**
     * 计算所有机器的所有TimeSegment时间段总时长（返回总秒数，便于转换单位）
     *
     * @return 所有时间段的总时长（秒）
     */
    public long calculateAllTimeSegmentsTotalDuration() {
        // 初始化总时长（秒）
        long totalSeconds = 0;



        // 遍历每台机器
        for (Machine machine :chromosome.getMachines()) {
            // 空值保护：若当前机器的时间段列表为空/Null，跳过
            if (machine.getAvailability() == null || machine.getAvailability().isEmpty()) {
                continue;
            }

            // 遍历当前机器的所有时间段，累加时长
            for (TimeSegment segment : machine.getAvailability()) {
                // 空值保护：跳过无效的时间段（开始/结束时间为Null）
                if (segment.getStart() == null || segment.getEnd() == null) {
                    continue;
                }

                // 计算单个时间段的时长（秒），确保结束时间≥开始时间
                LocalDateTime start = segment.getStart();
                LocalDateTime end = segment.getEnd();
                long segmentSeconds = Math.max(0, Duration.between(start, end).getSeconds());
                totalSeconds += segmentSeconds;
            }
        }

        return totalSeconds;
    }

    /**
     * 计算日末快照平均WIP：每天23:59已开始且未完成工序的产品数量。
     */
    private double calculateAverageDailyEndWipQuantity() {
        List<GAScheduleResult> results = chromosome.getResult();
        if (results == null || results.isEmpty()) {
            return 0;
        }

        int firstTaskStart = results.stream()
                .mapToInt(GAScheduleResult::getStartTime)
                .min()
                .orElse(0);
        int lastTaskEnd = results.stream()
                .mapToInt(GAScheduleResult::getEndTime)
                .max()
                .orElse(0);
        if (lastTaskEnd <= firstTaskStart) {
            return 0;
        }

        if (baseTime == null) {
            return calculateAverageDailyEndWipQuantityByRelativeDay(results, firstTaskStart, lastTaskEnd);
        }

        LocalDate firstDate = baseTime.plusSeconds(firstTaskStart).toLocalDate();
        LocalDate lastDate = baseTime.plusSeconds(lastTaskEnd).toLocalDate();
        double totalSnapshotQuantity = 0;
        int dayCount = 0;

        for (LocalDate date = firstDate; !date.isAfter(lastDate); date = date.plusDays(1)) {
            long snapshotSecond = Duration.between(baseTime, date.atTime(23, 59)).getSeconds();
            double snapshotQuantity = results.stream()
                    .filter(result -> result.getEndTime() > result.getStartTime())
                    .filter(result -> result.getStartTime() <= snapshotSecond && snapshotSecond < result.getEndTime())
                    .mapToDouble(GAScheduleResult::getQuantity)
                    .sum();
            totalSnapshotQuantity += snapshotQuantity;
            dayCount++;
        }

        if (dayCount == 0) {
            return 0;
        }
        return Math.round(totalSnapshotQuantity / dayCount * 100.0) / 100.0;
    }

    private double calculateAverageDailyEndWipQuantityByRelativeDay(List<GAScheduleResult> results, int firstTaskStart, int lastTaskEnd) {
        final int secondsPerDay = 24 * 60 * 60;
        final int snapshotSecondOfDay = 23 * 60 * 60 + 59 * 60;
        int firstDayStart = Math.floorDiv(firstTaskStart, secondsPerDay) * secondsPerDay;
        int lastDayStart = Math.floorDiv(lastTaskEnd, secondsPerDay) * secondsPerDay;
        double totalSnapshotQuantity = 0;
        int dayCount = 0;

        for (int dayStart = firstDayStart; dayStart <= lastDayStart; dayStart += secondsPerDay) {
            int snapshotSecond = dayStart + snapshotSecondOfDay;
            double snapshotQuantity = results.stream()
                    .filter(result -> result.getEndTime() > result.getStartTime())
                    .filter(result -> result.getStartTime() <= snapshotSecond && snapshotSecond < result.getEndTime())
                    .mapToDouble(GAScheduleResult::getQuantity)
                    .sum();
            totalSnapshotQuantity += snapshotQuantity;
            dayCount++;
        }

        if (dayCount == 0) {
            return 0;
        }
        return Math.round(totalSnapshotQuantity / dayCount * 100.0) / 100.0;
    }



    private void addKpi(List<KpiMetrics> kpiList, String name, double value, String tip, int type, int status) {
        KpiMetrics kpi = new KpiMetrics();
        kpi.setId(UUID.randomUUID().toString());
        kpi.setName(name);
        kpi.setTip(tip);
        kpi.setType(type);
        kpi.setValue(value);
        kpi.setStatus(status);
        kpiList.add(kpi);
    }
}
