# src/common_utils.py

from typing import List, Union

BEIJING_TZ = "Asia/Shanghai"


def generate_parameter_range(start: Union[int, float], end: Union[int, float], step: Union[int, float]) -> List[Union[int, float]]:
    """
    根据开始、结束和步长生成一个参数值的列表。
    支持整数和浮点数。

    Args:
        start (Union[int, float]): 参数范围的起始值。
        end (Union[int, float]): 参数范围的结束值。
        step (Union[int, float]): 参数的步长。

    Returns:
        List[Union[int, float]]: 生成的参数值列表。
    """
    if step == 0:
        raise ValueError("步长不能为0。")
    if (end - start) * step < 0: # 步长方向与范围不符
        raise ValueError("步长方向与起始/结束值不一致，请检查步长正负。")

    param_range = []
    current_value = start
    while (step > 0 and current_value <= end) or (step < 0 and current_value >= end):
        param_range.append(current_value)
        current_value += step
        # 针对浮点数精度问题进行小幅调整，避免无限循环或过早停止
        if isinstance(step, float) or isinstance(start, float) or isinstance(end, float):
            current_value = round(current_value, 10) # 四舍五入到一定小数位

    return param_range

from datetime import datetime, time, timedelta
from typing import Optional

def is_futures_trading_time(current_dt: Optional[datetime] = None) -> bool:
    """
    判断当前时间（或指定时间）是否是中国期货的开盘时间。

    Args:
        current_dt (Optional[datetime]): 要检查的 datetime 对象。如果为 None，
                                         则默认使用当前系统时间（datetime.now()）。

    Returns:
        bool: 如果是期货开盘时间则返回 True，否则返回 False。
    """
    if current_dt is None:
        current_dt = datetime.now()

    current_time = current_dt.time() # 获取时间部分 (小时、分钟、秒)

    # 定义白盘交易时段 (使用 time 对象进行精确比较)
    day_sessions = [
        (time(9, 0), time(10, 15)),   # 09:00 - 10:15
        (time(10, 30), time(11, 30)), # 10:30 - 11:30
        (time(13, 30), time(15, 0))   # 13:30 - 15:00
    ]

    # 定义夜盘交易时段 (简化为 21:00 - 23:00)
    # 注意：这里的 23:00 是结束时刻的开始，意味着实际交易到 22:59:59。
    night_session = (time(21, 0), time(23, 0)) # 21:00 - 23:00 (不包含 23:00)

    # 检查当前时间是否在任何一个日盘交易时段内
    for start_t, end_t in day_sessions:
        if start_t <= current_time < end_t:
            return True

    # 检查当前时间是否在夜盘交易时段内
    if night_session[0] <= current_time < night_session[1]:
        return True

    # 如果不在任何交易时段内，则不是开盘时间
    return False


def is_bar_pre_close_period(
    bar_start_time: datetime,
    bar_duration_seconds: int,
    pre_close_minutes: int,
    current_system_time: Optional[datetime] = None
) -> bool:
    """
    判断当前系统时间是否在一根K线的即将结束时间段内。

    Args:
        bar_start_time (datetime): 当前K线的开始时间（例如：bar.datetime）。
                                   可以是 datetime.datetime 对象或 pandas.Timestamp 对象。
        bar_duration_seconds (int): 该K线的持续时间，以秒为单位。
                                    对于1小时K线，传入 3600。
        pre_close_minutes (int): K线结束前多少分钟被认为是“即将结束”状态。例如，传入 3 表示结束前3分钟。
        current_system_time (Optional[datetime]): 用于判断的当前时间。
                                                  如果为 None，则默认使用 `datetime.now()`。

    Returns:
        bool: 如果当前时间在K线结束前 `pre_close_minutes` 的窗口内，则返回 True，否则返回 False。
    """
    if current_system_time is None:
        current_system_time = datetime.now(BEIJING_TZ)

    # 1. 计算K线的精确结束时间
    # K线结束时间 = K线开始时间 + K线持续时间
    bar_end_time = bar_start_time + timedelta(seconds=bar_duration_seconds)

    # 2. 计算“即将结束”窗口的开始时间
    # 这个窗口从 (K线结束时间 - pre_close_minutes) 开始，到 K线结束时间 结束
    pre_close_window_start_time = bar_end_time - timedelta(minutes=pre_close_minutes)

    # 3. 判断当前系统时间是否在这个窗口内
    # 窗口定义为 [pre_close_window_start_time, bar_end_time)，即包含开始时间，不包含结束时间
    print(pre_close_window_start_time, current_system_time, bar_end_time)
    return pre_close_window_start_time <= current_system_time < bar_end_time