package com.aps.service.plan;




import com.aps.entity.basic.*;

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

public class MachineSchedulerService {

    // 全局缓存（线程安全）
    private static final ConcurrentHashMap<Integer, MachineTimeline> timelineCache = new ConcurrentHashMap<>();
    private LocalDateTime currentTime;
    private List<Holiday> holidays;

    public MachineSchedulerService(List<Holiday> holidays, LocalDateTime currentTime) {
        this.holidays = holidays != null ? holidays : new ArrayList<>();
        this.currentTime = currentTime;
    }

    public MachineTimeline getOrCreateTimeline(Machine machine) {
        int machineId = machine.getId();

        // 尝试从缓存获取
        MachineTimeline timeline = timelineCache.get(machineId);
        if (timeline != null) {
            // 检查有效期（4小时刷新）
            if (Duration.between(timeline.getLastUpdated(), LocalDateTime.now()).toHours() < 4) {
                return timeline;
            }
        }

        // 创建新时间线（60天范围）
        MachineTimeline newTimeline = generateTimeline(machine);
        timelineCache.put(machineId, newTimeline);
        return newTimeline;
    }

    private MachineTimeline generateTimeline(Machine machine) {
        MachineTimeline timeline = new MachineTimeline();
        timeline.setMachineId(machine.getId());
        timeline.setValidFrom(currentTime);
        timeline.setValidTo(currentTime.plusDays(60));
        timeline.setLastUpdated(LocalDateTime.now());

        LocalDate currentDate = currentTime.toLocalDate();
        LocalDate endDate = currentTime.plusDays(60).toLocalDate();

        List<TimeSegment> allSegments = new ArrayList<>();

        while (!currentDate.isAfter(endDate)) {
            // 检查是否在假期内
            boolean isHolidayPeriod = isHoliday(currentDate);

            // 生成当日时间段
            List<TimeSegment> daySegments = calculateDaySegments(machine, currentDate, isHolidayPeriod);
            allSegments.addAll(daySegments);

            // 跳到下一天
            currentDate = currentDate.plusDays(1);
        }

        // 合并连续时间段
        timeline.setSegments(mergeSegments(allSegments));

        // 处理维护窗口
        if (machine.getMaintenanceWindows() != null && !timeline.getSegments().isEmpty()) {
            LocalDateTime maxEnd = timeline.getSegments().stream()
                    .map(TimeSegment::getEnd)
                    .max(LocalDateTime::compareTo)
                    .orElse(LocalDateTime.MIN);

            for (MaintenanceWindow maintenanceWindow : machine.getMaintenanceWindows()) {
                if (maintenanceWindow.getStartTime().isAfter(maxEnd)) {
                    continue;
                }
                updateSegmentsForMaintenance(timeline.getSegments(),
                        maintenanceWindow.getStartTime(), maintenanceWindow.getEndTime());
            }
        }

        return timeline;
    }

    public List<TimeSegment> generateTimeSegment(Machine machine, LocalDateTime currentTime) {
        int machineId = machine.getId();
        MachineTimeline timeline = timelineCache.get(machineId);

        if (timeline == null) {
            timeline = new MachineTimeline();
            timeline.setMachineId(machine.getId());
            timeline.setValidFrom(currentTime);
            timeline.setValidTo(currentTime.plusDays(60));
            timeline.setLastUpdated(LocalDateTime.now());
            timeline.setSegments(new ArrayList<>());
        } else {
            timeline.setValidTo(currentTime.plusDays(60));
            timeline.setLastUpdated(LocalDateTime.now());
        }

        LocalDate currentDate = currentTime.toLocalDate();
        LocalDate endDate = currentTime.plusDays(60).toLocalDate();

        List<TimeSegment> segments = new ArrayList<>();

        while (!currentDate.isAfter(endDate)) {
            // 检查是否在假期内
            boolean isHolidayPeriod = isHoliday(currentDate);

            // 生成当日时间段
            List<TimeSegment> daySegments = calculateDaySegments(machine, currentDate, isHolidayPeriod);
            segments.addAll(daySegments);

            // 跳到下一天
            currentDate = currentDate.plusDays(1);
        }

        // 处理维护窗口
        if (machine.getMaintenanceWindows() != null && !segments.isEmpty()) {
            LocalDateTime maxEnd = segments.stream()
                    .map(TimeSegment::getEnd)
                    .max(LocalDateTime::compareTo)
                    .orElse(LocalDateTime.MIN);

            for (MaintenanceWindow maintenanceWindow : machine.getMaintenanceWindows()) {
                if (maintenanceWindow.getStartTime().isAfter(maxEnd) ||
                        maintenanceWindow.getStartTime().isBefore(currentTime)) {
                    continue;
                }
                updateSegmentsForMaintenance(segments, maintenanceWindow.getStartTime(), maintenanceWindow.getEndTime());
            }
        }

        // 添加新生成的段到时间线
        timeline.getSegments().addAll(copyTimeSegments(segments));

        // 合并连续时间段
        timeline.setSegments(mergeSegments(timeline.getSegments()));
        segments = mergeSegments(segments);

        return segments;
    }

    private boolean isHoliday(LocalDate currentDate) {
        for (Holiday holiday : holidays) {
            LocalDateTime holidayStart = holiday.getStart();
            LocalDateTime holidayEnd = holiday.getEnd();

            if (holidayStart != null && holidayEnd != null) {
                LocalDate holidayStartDate = holidayStart.toLocalDate();
                LocalDate holidayEndDate = holidayEnd.toLocalDate();

                if ((currentDate.isEqual(holidayStartDate) || currentDate.isAfter(holidayStartDate)) &&
                        (currentDate.isEqual(holidayEndDate) || currentDate.isBefore(holidayEndDate))) {
                    return true;
                }
            }
        }
        return false;
    }

    private List<TimeSegment> calculateDaySegments(Machine machine, LocalDate date, boolean isHoliday) {
        List<TimeSegment> segments = new ArrayList<>();

        if (isHoliday) {
            // 假期：只处理特定日期的班次
            List<Shift> shifts = machine.getShifts().stream()
                    .filter(s -> s.getDays() == null &&
                            s.getShiftDate() != null &&
                            s.getShiftDate().toLocalDate().equals(date))
                    .collect(Collectors.toList());

            for (Shift shift : shifts) {
                LocalDateTime shiftStart = date.atTime(shift.getStartTime());
                LocalDateTime shiftEnd = shift.getEndTime().isBefore(shift.getStartTime()) ?
                        date.plusDays(1).atTime(shift.getEndTime()) :
                        date.atTime(shift.getEndTime());

                segments.add(new TimeSegment(
                        shiftStart,
                        shiftEnd,
                        shift.isTemporaryShift() ? SegmentType.TEMP : SegmentType.REGULAR,
                        false
                ));
            }
        } else {
            // 非假期：处理常规班次
            List<Shift> shifts = machine.getShifts().stream()
                    .filter(s -> s.getDays() != null && containsDay(s.getDays(), date.getDayOfWeek()))
                    .collect(Collectors.toList());

            for (Shift shift : shifts) {
                LocalDateTime shiftStart = date.atTime(shift.getStartTime());
                LocalDateTime shiftEnd = shift.getEndTime().isBefore(shift.getStartTime()) ?
                        date.plusDays(1).atTime(shift.getEndTime()) :
                        date.atTime(shift.getEndTime());

                segments.add(new TimeSegment(
                        shiftStart,
                        shiftEnd,
                        shift.isTemporaryShift() ? SegmentType.TEMP : SegmentType.REGULAR,
                        false
                ));
            }
        }

        return segments;
    }

    /**
     * 检查班次是否包含指定的星期几
     * JSON中的Day映射：0=周日, 1=周一, 2=周二, ..., 6=周六
     * Java DayOfWeek: MONDAY=1, TUESDAY=2, ..., SUNDAY=7
     */
    private boolean containsDay(Set<Integer> shiftDays, DayOfWeek targetDay) {
        if (shiftDays == null || shiftDays.isEmpty()) {
            return false;
        }

        // 将Java的DayOfWeek转换为JSON格式的数字
        int targetDayValue = targetDay.getValue();

        // 特殊处理周日：JSON中0表示周日，Java中Sunday是7
        if (targetDay == DayOfWeek.SUNDAY) {
            return shiftDays.contains(0) || shiftDays.contains(7);
        }

        return shiftDays.contains(targetDayValue);
    }

    private List<TimeSegment> mergeSegments(List<TimeSegment> segments) {
        List<TimeSegment> maintenanceSegments = segments.stream()
                .filter(t -> t.getType() == SegmentType.MAINTENANCE)
                .collect(Collectors.toList());

        List<TimeSegment> otherSegments = segments.stream()
                .filter(t -> t.getType() != SegmentType.MAINTENANCE)
                .collect(Collectors.toList());

        // 按开始时间排序
        otherSegments.sort(Comparator.comparing(TimeSegment::getStart));

        Stack<TimeSegment> merged = new Stack<>();

        for (TimeSegment seg : otherSegments) {
            if (!merged.isEmpty() && !merged.peek().getEnd().isBefore(seg.getStart())) {
                TimeSegment top = merged.peek();
                if (top.getEnd().isBefore(seg.getEnd())) {
                    top.setEnd(seg.getEnd());
                }
            } else {
                seg.setKey(UUID.randomUUID().toString());
                merged.push(seg);
            }
        }

        List<TimeSegment> result = new ArrayList<>(merged);
        result.addAll(maintenanceSegments);

        // 按开始时间排序
        result.sort(Comparator.comparing(TimeSegment::getStart));
        return result;
    }

    private void updateSegmentsForMaintenance(List<TimeSegment> segments, LocalDateTime maintenanceStart, LocalDateTime maintenanceEnd) {
        if (segments.isEmpty()) return;

        // 1. 二分查找
        int firstIndex = binarySearchSegment(segments, maintenanceStart);
        if (firstIndex == segments.size()) return;

        // 2. 收集有交集的段
        List<Integer> indicesToProcess = new ArrayList<>();
        int i = firstIndex;
        while (i < segments.size() && segments.get(i).getStart().isBefore(maintenanceEnd)) {
            if (segments.get(i).getEnd().isAfter(maintenanceStart)) {
                indicesToProcess.add(i);
            }
            i++;
        }

        // 3. 倒序处理
        for (int j = indicesToProcess.size() - 1; j >= 0; j--) {
            int index = indicesToProcess.get(j);
            TimeSegment seg = segments.get(index);
            LocalDateTime segStart = seg.getStart();
            LocalDateTime segEnd = seg.getEnd();

            // 完全覆盖
            if ((maintenanceStart.isBefore(segStart) || maintenanceStart.equals(segStart)) &&
                    (maintenanceEnd.isAfter(segEnd) || maintenanceEnd.equals(segEnd))) {
                segments.remove(index);
            }
            // 维护时间段在段内部
            else if (maintenanceStart.isAfter(segStart) && maintenanceEnd.isBefore(segEnd)) {
                // 分割：原段变成前半段 [segStart, maintenanceStart)
                seg.setEnd(maintenanceStart);

                // 添加后半段 [maintenanceEnd, segEnd)
                segments.add(index + 1, new TimeSegment(
                        maintenanceEnd,
                        segEnd,
                        seg.getType(),
                        seg.isHoliday()
                ));
            }
            // 维护时间段覆盖开始部分
            else if (maintenanceStart.isBefore(segStart) || maintenanceStart.equals(segStart)) {
                // 保留：维护时间段之后的部分 [maintenanceEnd, segEnd)
                seg.setStart(maintenanceEnd);
            }
            // 维护时间段覆盖结束部分
            else if (maintenanceEnd.isAfter(segEnd) || maintenanceEnd.equals(segEnd)) {
                // 保留：维护时间段之前的部分 [segStart, maintenanceStart)
                seg.setEnd(maintenanceStart);
            }
        }

        segments.add(new TimeSegment(
                maintenanceStart,
                maintenanceEnd,
                SegmentType.MAINTENANCE,
                false
        ));
    }

    private int binarySearchSegment(List<TimeSegment> segments, LocalDateTime maintenanceStart) {
        int left = 0;
        int right = segments.size() - 1;
        int result = segments.size();

        while (left <= right) {
            int mid = left + (right - left) / 2;
            TimeSegment seg = segments.get(mid);

            if ((seg.getStart().isBefore(maintenanceStart) || seg.getStart().equals(maintenanceStart)) &&
                    maintenanceStart.isBefore(seg.getEnd())) {
                return mid;
            }

            if (seg.getEnd().isBefore(maintenanceStart) || seg.getEnd().equals(maintenanceStart)) {
                left = mid + 1;
            } else {
                result = mid;
                right = mid - 1;
            }
        }

        return result;
    }

    private List<TimeSegment> copyTimeSegments(List<TimeSegment> originals) {
        return originals.stream()
                .map(seg -> {
                    TimeSegment copy = new TimeSegment();
                    copy.setStart(seg.getStart());
                    copy.setEnd(seg.getEnd());
                    copy.setType(seg.getType());
                    copy.setHoliday(seg.isHoliday());
                    copy.setUsed(seg.isUsed());
                    copy.setKey(seg.getKey());
                    return copy;
                })
                .collect(Collectors.toList());
    }

    // 清理缓存的方法
    public static void clearCache() {
        timelineCache.clear();
    }

    public static int getCacheSize() {
        return timelineCache.size();
    }
}