# 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 logging
from abc import ABC, abstractmethod
from typing import List, Optional

from olc.bean.match_wrapper import MatchWrapper
from olc.bean.olc_config_rule import BasePolicy
from olc.bean.olc_control_request import OlcControlRequest
from olc.bean.tag_group import TagGroup, SubGroup
from olc.cache.safe__ttl_cache import SafeTTLCache, key_of_list
from olc.event.sny_event import Event
from olc.rule.olc_rule_manager import OlcRuleManager

logger = logging.getLogger(__name__)


class PolicyMatcher(ABC):
    @abstractmethod
    def match(
        self, req: OlcControlRequest, group: List[TagGroup]
    ) -> List[MatchWrapper]:
        pass


class AbstractPolicyMatcher(PolicyMatcher):
    def __init__(self):
        self._cache: SafeTTLCache[List[MatchWrapper[BasePolicy]]] = SafeTTLCache(
            maxsize=100000, ttl=60
        )

    @abstractmethod
    def get_policy(self, name: str) -> Optional[BasePolicy]:
        pass

    def match(
        self, req: OlcControlRequest, groups: List[TagGroup]
    ) -> List[MatchWrapper]:
        if not groups:
            return []

        result = self._cache.get(key_of_list(groups))
        # 如果是空列表说明之前匹配过了返回空列表
        if (result is not None) and (len(result) == 0):
            return result
        result: List[MatchWrapper[BasePolicy]] = []
        for group in groups:
            if group.create_sub_group():
                continue
            group_name = group.name
            if isinstance(group, SubGroup):
                group_name = group.parent.name
            policy: BasePolicy = self.get_policy(group_name)
            if policy is None:
                continue
            result.append(MatchWrapper(group, policy))

        # 仅保留优先级数字最小的 MatchWrapper
        if result:
            min_priority = min(wrapper.tag_group.priority for wrapper in result)
            result = [
                wrapper
                for wrapper in result
                if wrapper.tag_group.priority == min_priority
            ]

        self._cache.put(key_of_list(groups), result)
        return result


class FlowMatcher(AbstractPolicyMatcher):

    def __init__(self):
        super().__init__()
        Event.get_instance().olc_clean_rule.connect(self.subscribe_clean)

    def get_policy(self, name: str) -> Optional[BasePolicy]:
        return OlcRuleManager.get_instance().get_flow_policy_by_group_name(name)

    def clean_cache(self):
        self._cache.clear()

    def subscribe_clean(self, sender, **kwargs):
        logger.info("clean flow matcher cache")
        self.clean_cache()


class AdmissionMatcher(AbstractPolicyMatcher):
    def __init__(self):
        super().__init__()
        Event.get_instance().olc_clean_rule.connect(self.subscribe_clean)

    def get_policy(self, name: str) -> Optional[BasePolicy]:
        return OlcRuleManager.get_instance().get_admission_policy_by_group_name(name)

    def clean_cache(self):
        self._cache.clear()

    def subscribe_clean(self, sender, **kwargs):
        logger.info("clean admission matcher cache")
        self.clean_cache()