package com.ylx.massage.utils;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.ylx.massage.domain.TOrder;
import com.ylx.massage.enums.OrderStatusEnum;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.time.LocalDateTime;

/**
 * 订单时间范围校验工具类
 * <p>
 * 提供订单时间范围重叠校验的功能，确保技师服务时间的互斥性。
 * 通过检查订单的时间范围（开始时间到结束时间）是否有重叠来判断是否冲突。
 * </p>
 *
 * @author ylx
 * @version 1.0
 * @since 2024
 */
public class OrderTimeRangeUtils {

    private static final Logger log = LoggerFactory.getLogger(OrderTimeRangeUtils.class);

    /**
     * 添加缓冲时间（分钟）
     * <p>
     * 在两个订单之间添加缓冲时间，用于处理路途、准备等额外时间。
     * 例如：订单A 14:00结束，订单B 14:30开始，如果有30分钟缓冲时间，则不会冲突。
     * </p>
     */
    private static final int BUFFER_MINUTES = 30;

    /**
     * 检查新订单是否与现有订单时间范围重叠
     * <p>
     * 时间范围重叠判断逻辑：
     * 1. 如果现有订单或新订单的时间信息不完整，则认为有冲突（保守策略）
     * 2. 计算两个订单的时间范围，并判断是否重叠
     * 3. 考虑缓冲时间，在两个订单之间留出缓冲期
     * </p>
     *
     * @param newOrder 新订单
     * @param existingOrder 现有订单
     * @return boolean 如果时间范围重叠返回true，否则返回false
     */
    public static boolean isTimeRangeOverlapping(TOrder newOrder, TOrder existingOrder) {
        // 1. 检查现有订单的时间信息
        LocalDateTime existingStart = existingOrder.getStartTime();
        LocalDateTime existingEnd = existingOrder.getEndTime();

        // 2. 检查新订单的时间信息
        LocalDateTime newStart = newOrder.getStartTime();
        LocalDateTime newEnd = newOrder.getEndTime();

        // 3. 保守策略：如果任何订单的时间信息不完整，认为有冲突
        if (existingStart == null || existingEnd == null || newStart == null || newEnd == null) {
            log.warn("订单时间信息不完整，认为时间冲突。现有订单：{}，新订单：{}", existingOrder.getOrderNo(), newOrder.getOrderNo());
            return true;
        }

        // 4. 计算新订单的服务时长（分钟）
        Long newDurationMinutes = calculateOrderDuration(newOrder);
        if (newDurationMinutes == null || newDurationMinutes <= 0) {
            log.warn("无法计算新订单的服务时长，订单号：{}", newOrder.getOrderNo());
            return true;
        }

        // 5. 重新计算新订单的结束时间（基于当前时间 + 服务时长）
        LocalDateTime actualNewStart = newStart;
        LocalDateTime actualNewEnd = actualNewStart.plusMinutes(newDurationMinutes);

        // 6. 应用缓冲时间
        // 在现有订单结束后增加缓冲时间，新订单需要在缓冲时间之后才能开始
        //LocalDateTime existingEndWithBuffer = existingEnd.plusMinutes(BUFFER_MINUTES);
        LocalDateTime existingEndWithBuffer = existingEnd;

        // 7. 判断时间范围是否重叠
        // 重叠条件：新订单开始时间 < 现有订单结束时间（含缓冲）
        // 并且：新订单结束时间 > 现有订单开始时间
        boolean isOverlapping = !actualNewStart.isAfter(existingEndWithBuffer) && !actualNewEnd.isBefore(existingStart);

        if (isOverlapping) {
            log.info("检测到时间冲突：现有订单 {}（{} - {}）与新订单 {}（{} - {}）",
                existingOrder.getOrderNo(), existingStart, existingEnd,
                newOrder.getOrderNo(), actualNewStart, actualNewEnd);
        } else {
            log.info("无时间冲突：现有订单 {}（{} - {}）与新订单 {}（{} - {}）",
                existingOrder.getOrderNo(), existingStart, existingEnd,
                newOrder.getOrderNo(), actualNewStart, actualNewEnd);
        }
        return isOverlapping;
    }

    /**
     * 计算订单的服务时长（分钟）
     * <p>
     * 从订单明细（cGoods）中获取所有项目的服务时长，并计算总时长。
     * </p>
     *
     * @param order 订单对象
     * @return Long 服务时长（分钟），如果无法计算则返回null
     */
    public static Long calculateOrderDuration(TOrder order) {
        try {
            JSONArray goods = order.getcGoods();
            long totalMinutes = 0;

            for (int i = 0; i < goods.size(); i++) {
                JSONObject item = goods.getJSONObject(i);
                Integer minutes = item.getInteger("nMinute");
                Integer number = item.getInteger("number");

                if (minutes != null && number != null) {
                    totalMinutes += (minutes * number);
                }
            }

            if (totalMinutes <= 0) {
                log.warn("订单服务时长计算结果无效：{} 分钟，订单号：{}", totalMinutes, order.getOrderNo());
                return null;
            }

            log.debug("订单 {} 服务时长：{} 分钟", order.getOrderNo(), totalMinutes);
            return totalMinutes;
        } catch (Exception e) {
            log.error("计算订单服务时长失败，订单号：{}", order.getOrderNo(), e);
            return null;
        }
    }

    /**
     * 检查订单是否有有效的时间范围
     * <p>
     * 判断订单是否具有有效的开始时间和结束时间。
     * </p>
     *
     * @param order 订单对象
     * @return boolean 如果订单有有效的时间范围返回true，否则返回false
     */
    public static boolean hasValidTimeRange(TOrder order) {
        if (order == null) {
            return false;
        }

        LocalDateTime start = order.getStartTime();
        LocalDateTime end = order.getEndTime();

        return start != null && end != null && start.isBefore(end);
    }

    /**
     * 检查是否需要基于服务时长重新计算结束时间
     * <p>
     * 对于待接单状态的订单，结束时间可能未设置，需要基于开始时间和服务时长计算。
     * </p>
     *
     * @param order 订单对象
     * @return boolean 如果需要重新计算结束时间返回true，否则返回false
     */
    public static boolean needsEndTimeCalculation(TOrder order) {
        if (order == null) {
            return false;
        }

        // 待接单状态的订单，可能还没有设置结束时间
        boolean isWaitOrder = OrderStatusEnum.WAIT_JD.getCode().equals(order.getnStatus());

        // 结束时间为空或早于开始时间
        boolean invalidEndTime = order.getEndTime() == null
            || (order.getStartTime() != null && order.getEndTime().isBefore(order.getStartTime()));

        return isWaitOrder && invalidEndTime;
    }

    /**
     * 订单的开始时间
     * <p>
     * 根据订单的当前状态和时间字段，估算订单的开始时间。
     * - 如果已有startTime，直接使用
     * - 如果是待接单状态，使用当前时间作为开始时间
     * - 如果有reachTime，使用到达时间作为开始时间
     * - 如果有acceptanceTime，使用接单时间+路程时间作为开始时间
     * </p>
     *
     * @param order 订单对象
     * @return LocalDateTime 估算的开始时间
     */
    public static LocalDateTime estimateStartTime(TOrder order) {
        // 使用已设置的startTime
        if (order.getStartTime() != null) {
            return order.getStartTime();
        }
        return null;
    }

    /**
     * 估算订单的结束时间
     * <p>
     * 基于开始时间和订单服务时长，估算订单的结束时间。
     * </p>
     *
     * @param order 订单对象
     * @return LocalDateTime 估算的结束时间
     */
    public static LocalDateTime estimateEndTime(TOrder order) {
        // 1. 如果已有endTime，直接使用
        if (order.getEndTime() != null) {
            return order.getEndTime();
        }

        // 2. 基于开始时间和服务时长计算
        LocalDateTime startTime = estimateStartTime(order);
        if (startTime == null) {
            return null;
        }

        Long durationMinutes = calculateOrderDuration(order);
        log.info("订单 {} 服务时长：{} 分钟", order.getOrderNo(), durationMinutes);
        if (durationMinutes == null || durationMinutes <= 0) {
            // 默认60分钟
            durationMinutes = 60L;
        }
        return startTime.plusMinutes(durationMinutes);
    }

    /**
     * 检查订单是否是升级订单
     * <p>
     * 升级订单（orderType == 2）在原有订单基础上增加服务项目，
     * 不需要额外的时间互斥校验。
     * </p>
     *
     * @param order 订单对象
     * @return boolean 如果是升级订单返回true，否则返回false
     */
    public static boolean isUpgradeOrder(TOrder order) {
        return order != null
            && order.getParentNo() != null
            && !order.getParentNo().trim().isEmpty()
            && order.getOrderType() != null
            && order.getOrderType() == 2;
    }
}
