# Copyright (c) Huawei Technologies Co., Ltd. 2026-2026. All rights reserved.
# OpenOLC 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.

import json
import logging
from abc import ABC, abstractmethod
from typing import Optional

from olc.alg.alg_context import AlgContextBuilder
from olc.alg.alg_manager import OlcAlgManager
from olc.alg.flow.flow_alg import FlowAbstractAlgorithm
from olc.bean.match_wrapper import MatchWrapper
from olc.bean.olc_config_rule import FlowPolicy, AlgInstance
from olc.bean.tag_group import TagGroup
from olc.collector.collector_manager import OlcCollectorManager
from olc.limit.limiter import Limit

logger = logging.getLogger(__name__)


class AbsDynamicLimiter(Limit, ABC):
    def __init__(self, wrapper: MatchWrapper[FlowPolicy]):
        self._wrapper: MatchWrapper[FlowPolicy] = wrapper
        self.flow_policy: FlowPolicy = wrapper.policy
        self._cal_ins: Optional[AlgInstance] = wrapper.policy.calculate_alg
        self._cal_alg: Optional[FlowAbstractAlgorithm] = (
            OlcAlgManager.get_instance().get_flow_alg(self._cal_ins.alg_name)
        )
        self.group: TagGroup = wrapper.tag_group
        self._rate: int = self.flow_policy.rate_limit
        self._init_burst: int = self.flow_policy.burst_limit
        self._pre_rate: int = self._rate

    def refresh_rate_limiter(self):
        """
            动态算法,刷新限速器
        :return:
        """
        try:
            if self._cal_alg is None:
                logger.warning(f"not found flow algorithm {self._cal_ins.alg_name}")
                return

            instance_paras = self._cal_ins.alg_param.copy()
            # 把算法入参中的动态参数,转换为真实数据
            for key, value in instance_paras.items():
                alg_param = self._cal_alg.para_names.get(key)
                if alg_param is not None:
                    if alg_param.dynamic:
                        true_value = OlcCollectorManager.get_instance().get_dyn_key_true_value(value)
                        if true_value is None:
                            logger.warning(f"get dynamic value err, dynamic key {value} is null")
                            return
                        instance_paras[key] = true_value

            builder = AlgContextBuilder[float]().new_builder()
            builder.set_group(self.group)
            builder.set_policy(self.flow_policy)
            builder.set_parameter(instance_paras)
            builder.set_previous(self._rate)
            builder.set_init_result(self._init_burst)
            new_rate = self._cal_alg.make_decision(builder.build())
        except Exception as e:
            logger.warning(f"limit dynamic algorithm {self._cal_ins.alg_name} error: {e}")
            self.update_rate(self._init_burst)
            self._rate = self._init_burst
        else:
            logger.info(f"{self.group.name}, limit update:{new_rate}, paras: {json.dumps(instance_paras, indent=4)}")
            self.update_rate(new_rate)
            self._rate = new_rate

    @abstractmethod
    def update_rate(self, rate_limit: int) -> None:
        """
            更新速率
        :param rate_limit:
        :return:
        """