# Copyright (c) Huawei Technologies Co., Ltd. 2026-2026. All rights reserved.
# MindIE is licensed under Mulan PSL v2.
# You can use this software according to the terms and conditions of the Mulan PSL v2.
# You may obtain a copy of Mulan PSL v2 at:
#         `http://license.coscl.org.cn/MulanPSL2`
# THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
# EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
# MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
# See the Mulan PSL v2 for more details.

from olc.statistic.sliding_window import SlidingWindow
from olc.utils.atomic_num import AtomicNum


class _RequestBucket:
    """
        请求统计桶
        承载通过/拒绝请求计数、通过请求响应时间相关聚合值
        所有计数字段使用 AtomicNum 保证多线程下累加不丢失;
        min/max 响应时间使用普通可空字段,并发下接受少量竞争误差。
    """

    def __init__(self):
        self.pass_num: AtomicNum = AtomicNum(0)
        self.block_num: AtomicNum = AtomicNum(0)
        self.pass_rt: AtomicNum = AtomicNum(0.0)
        self.min_pass_rt: float | None = None
        self.max_pass_rt: float | None = None

    def add_pass_num(self, count: int = 1):
        """
            原子地累加通过请求计数
        :param count: 累加值,默认为 1
        :return:
        """
        self.pass_num.add(count)

    def add_block_num(self, count: int = 1):
        """
            原子地累加拒绝请求计数
        :param count: 累加值,默认为 1
        :return:
        """
        self.block_num.add(count)

    def add_pass_rt(self, rt: float):
        """
            记录单次通过请求响应时间,同步更新累计响应时间、记录次数、最小值与最大值
        :param rt: 单次响应时间(毫秒)
        :return:
        """
        self.pass_rt.add(rt)
        if self.min_pass_rt is None or rt < self.min_pass_rt:
            self.min_pass_rt = rt
        if self.max_pass_rt is None or rt > self.max_pass_rt:
            self.max_pass_rt = rt

    def get_pass_num(self) -> int:
        """
            获取当前桶的通过请求次数
        :return: 通过请求次数
        """
        return int(self.pass_num.get())

    def get_block_num(self) -> int:
        """
            获取当前桶的拒绝请求次数
        :return: 拒绝请求次数
        """
        return int(self.block_num.get())

    def get_pass_rt(self) -> float:
        """
            获取当前桶的累计通过响应时间
        :return: 累计响应时间
        """
        return float(self.pass_rt.get())

    def get_min_pass_rt(self) -> float | None:
        """
            获取当前桶的最小响应时间
        :return: 最小响应时间,无记录时返回 None
        """
        return self.min_pass_rt

    def get_max_pass_rt(self) -> float | None:
        """
            获取当前桶的最大响应时间
        :return: 最大响应时间,无记录时返回 None
        """
        return self.max_pass_rt

    def reset(self):
        """
            将桶内所有统计字段复位到初始值
        :return:
        """
        self.pass_num.set(0)
        self.block_num.set(0)
        self.pass_rt.set(0)
        self.min_pass_rt = None
        self.max_pass_rt = None


class RequestStatSlidingWindow(SlidingWindow[_RequestBucket]):
    """
        请求统计滑动窗口
        在通用滑动窗口骨架上提供"通过/拒绝请求数 + 通过请求响应时间"业务接口
    """

    def _create_bucket(self) -> _RequestBucket:
        return _RequestBucket()

    def _reset_bucket(self, bucket: _RequestBucket) -> None:
        """
            请求统计桶复位:把通过/拒绝计数与响应时间统计字段重置为初始值
        :param bucket: 待复位的桶
        :return:
        """
        bucket.reset()

    def record_pass_num(self, count: int = 1) -> None:
        """
            记录通过请求次数到当前时间所在桶
        :param count: 通过请求次数,默认为 1
        :return:
        """
        bucket = self.current_bucket()
        bucket.value.add_pass_num(count)

    def get_pass_num_sum(self) -> int:
        """
            获取所有活跃桶的通过请求次数总和
        :return: 通过请求次数总和
        """
        buckets = self.get_all_buckets()
        pass_num_sum: int = 0
        for bucket in buckets:
            pass_num_sum += bucket.get_pass_num()
        return pass_num_sum

    def record_block_num(self, count: int = 1) -> None:
        """
            记录拒绝请求次数到当前时间所在桶
        :param count: 拒绝请求次数,默认为 1
        :return:
        """
        bucket = self.current_bucket()
        bucket.value.add_block_num(count)

    def get_block_num_sum(self) -> int:
        """
            获取所有活跃桶的拒绝请求次数总和
        :return: 拒绝请求次数总和
        """
        buckets = self.get_all_buckets()
        block_num_sum: int = 0
        for bucket in buckets:
            block_num_sum += bucket.get_block_num()
        return block_num_sum

    def record_pass_rt(self, rt: float) -> None:
        """
            记录单次通过请求响应时间到当前时间所在桶
            同时更新桶内的响应时间总和、响应时间计数、最小响应时间和最大响应时间
        :param rt: 单次请求响应时间(毫秒)
        :return:
        """
        bucket = self.current_bucket()
        bucket.value.add_pass_rt(rt)

    def get_pass_avg_rt(self) -> float:
        """
            获取通过平均时延
        """
        buckets = self.get_all_buckets()
        pass_rt_sum: float = 0.0
        pass_num: float = 0.0

        for bucket in buckets:
            pass_rt_sum += bucket.get_pass_rt()
            pass_num += bucket.get_pass_num()

        if pass_rt_sum == 0 or pass_num == 0:
            return 0.0

        return pass_rt_sum / pass_num

    def get_pass_rt_sum(self) -> float:
        """
            获取所有活跃桶的响应时间总和
        :return: 响应时间总和
        """
        buckets = self.get_all_buckets()
        pass_rt_sum: float = 0.0
        for bucket in buckets:
            pass_rt_sum += bucket.get_pass_rt()
        return pass_rt_sum

    def get_pass_rt_min(self) -> float | None:
        """
            获取所有活跃桶的最小响应时间
        :return: 最小响应时间,无记录时返回 None
        """
        buckets = self.get_all_buckets()
        min_rt: float | None = None
        for bucket in buckets:
            bucket_min = bucket.get_min_pass_rt()
            if bucket_min is None:
                continue
            if min_rt is None or bucket_min < min_rt:
                min_rt = bucket_min
        return min_rt

    def get_pass_rt_max(self) -> float | None:
        """
            获取所有活跃桶的最大响应时间
        :return: 最大响应时间,无记录时返回 None
        """
        buckets = self.get_all_buckets()
        max_rt: float | None = None
        for bucket in buckets:
            bucket_max = bucket.get_max_pass_rt()
            if bucket_max is None:
                continue
            if max_rt is None or bucket_max > max_rt:
                max_rt = bucket_max
        return max_rt