"""规则配置页聚合服务实现。"""

from __future__ import annotations

import asyncio
from collections import defaultdict
import re
import time
from typing import Any

from fastapi_common.fastapi_common_sqlalchemy.database import GetAsyncSession
from fastapi_common.fastapi_common_web.domain.responses import StatusCodeEnum
from fastapi_common.fastapi_common_web.exception.LeauditException import LeauditException
from sqlalchemy import text

from fastapi_modules.fastapi_leaudit.domian.vo.ruleConfigVo import (
    RuleConfigPackListVO,
    RuleConfigPackRuleSummaryVO,
    RuleConfigPackVO,
)
from fastapi_modules.fastapi_leaudit.leaudit_bridge.ruleValidator import RuleValidator
from fastapi_modules.fastapi_leaudit.services import IOssService
from fastapi_modules.fastapi_leaudit.services.impl.ossServiceImpl import OssServiceImpl
from fastapi_modules.fastapi_leaudit.services.impl.ruleServiceImpl import GetRuleServiceSingleton
from fastapi_modules.fastapi_leaudit.services.ruleConfigService import IRuleConfigService


class RuleConfigServiceImpl(IRuleConfigService):
    """规则配置页聚合服务实现。"""

    _GLOBAL_YAML_SUMMARY_CACHE: dict[int, tuple[float, dict[str, Any]]] = {}
    _GLOBAL_PACK_SUMMARY_CACHE: tuple[float, list[RuleConfigPackListVO]] | None = None
    _GLOBAL_WARM_LOCK: asyncio.Lock | None = None

    def __init__(self, OssService: IOssService | None = None) -> None:
        self.OssService = OssService or OssServiceImpl()
        self.RuleService = GetRuleServiceSingleton()
        self.Validator = RuleValidator()
        self._yaml_summary_cache = self.__class__._GLOBAL_YAML_SUMMARY_CACHE
        self._pack_summary_cache = self.__class__._GLOBAL_PACK_SUMMARY_CACHE

    @classmethod
    def _set_pack_summary_cache(cls, value: tuple[float, list[RuleConfigPackListVO]] | None) -> None:
        cls._GLOBAL_PACK_SUMMARY_CACHE = value

    @classmethod
    def _get_warm_lock(cls) -> asyncio.Lock:
        if cls._GLOBAL_WARM_LOCK is None:
            cls._GLOBAL_WARM_LOCK = asyncio.Lock()
        return cls._GLOBAL_WARM_LOCK

    def InvalidateSummaryCaches(self, version_ids: list[int] | None = None) -> None:
        """清理规则摘要缓存；version_ids 为空时清空全部。"""
        self.__class__._set_pack_summary_cache(None)
        if version_ids is None:
            self._yaml_summary_cache.clear()
            return
        for version_id in {int(item) for item in version_ids if item is not None}:
            self._yaml_summary_cache.pop(version_id, None)

    async def WarmPackSummaries(self, force: bool = False) -> list[RuleConfigPackListVO]:
        """预热规则列表摘要缓存。"""
        async with self.__class__._get_warm_lock():
            if force:
                self.InvalidateSummaryCaches()
            return await self.ListPackSummaries()

    async def ListPacks(self) -> list[RuleConfigPackVO]:
        """列出规则配置页所需的全部 pack。"""
        rows = await self._load_pack_rows()
        rule_set_map = await self._load_rule_set_meta_map()
        return [await self._build_pack_vo(row, rule_set_map) for row in rows]

    async def ListPackSummaries(self) -> list[RuleConfigPackListVO]:
        """列出规则列表页所需的轻量 pack。"""
        cached = self.__class__._GLOBAL_PACK_SUMMARY_CACHE
        now = time.monotonic()
        if cached and now - cached[0] <= 60:
            return cached[1]

        rows = await self._load_pack_rows()
        if not rows:
            return []

        group_ids = [int(row["group_id"]) for row in rows]
        binding_map = await self._load_effective_binding_map(group_ids)
        rule_set_ids = sorted({int(item["rule_set_id"]) for item in binding_map.values() if item.get("rule_set_id") is not None})
        rule_set_map = await self._load_rule_set_meta_map_by_ids(rule_set_ids)
        latest_version_map = await self._load_latest_version_map(rule_set_ids)

        base_items: list[dict[str, Any]] = []
        resolved_version_ids: set[int] = set()
        for row in rows:
            group_id = int(row["group_id"])
            binding = binding_map.get(group_id)
            document_type = str(row["document_type_name"] or "").strip()
            main_type = str(row["main_type"] or "").strip()
            subtype = str(row["subtype"] or "").strip() or "通用"
            module_type = str(row["entry_module_name"] or "").strip() or (f"{document_type}评查" if document_type else "规则配置")

            binding_id: int | None = None
            rule_set_id: int | None = None
            rule_type: str | None = None
            rule_name: str | None = None
            current_version_id: int | None = None
            fallback_version_id: int | None = None
            resolved_version_id: int | None = None
            has_usable_version = False
            usable_rule_count = 0

            if binding:
                binding_id = int(binding["id"])
                rule_set_id = int(binding["rule_set_id"])
                rule_set_meta = rule_set_map.get(rule_set_id, {})
                rule_type = str(rule_set_meta.get("rule_type") or "") or None
                rule_name = str(rule_set_meta.get("rule_name") or "") or None
                current_version_id = self._to_int(rule_set_meta.get("current_version_id"))
                fallback_version_id = self._to_int(rule_set_meta.get("fallback_version_id"))
                has_usable_version = bool(rule_set_meta.get("has_usable_version"))
                resolved_version_id = current_version_id or fallback_version_id or latest_version_map.get(rule_set_id)
                if resolved_version_id is not None:
                    resolved_version_ids.add(resolved_version_id)

            base_items.append({
                "packId": group_id,
                "groupId": group_id,
                "rootGroupId": self._to_int(row.get("root_group_id")),
                "bindingId": binding_id,
                "ruleSetId": rule_set_id,
                "ruleType": rule_type,
                "ruleName": rule_name,
                "currentVersionId": current_version_id,
                "fallbackVersionId": fallback_version_id,
                "resolvedVersionId": resolved_version_id,
                "hasUsableVersion": has_usable_version,
                "usableRuleCount": usable_rule_count,
                "documentTypeId": self._to_int(row.get("document_type_id")),
                "documentType": document_type,
                "moduleType": module_type,
                "mainType": main_type or document_type,
                "subtype": subtype,
            })

        version_oss_map = await self._load_version_oss_map(sorted(resolved_version_ids))
        yaml_summary_map = await self._load_yaml_summaries(version_oss_map)

        packs: list[RuleConfigPackListVO] = []
        for item in base_items:
            summary = yaml_summary_map.get(item["resolvedVersionId"]) if item.get("resolvedVersionId") else None
            rules = summary["rules"] if summary else []
            source_status = "empty"
            yaml_name = item.get("ruleName") or ""
            if item.get("resolvedVersionId") is not None:
                source_status = "ready" if summary and summary.get("loaded") else "missing"
                yaml_name = str(summary.get("yaml_name") or yaml_name or "")
            item["sourceStatus"] = source_status
            item["yamlName"] = yaml_name
            item["rules"] = [RuleConfigPackRuleSummaryVO(**rule) for rule in rules]
            item["usableRuleCount"] = len(rules)
            packs.append(RuleConfigPackListVO(**item))

        cache_value = (time.monotonic(), packs)
        self.__class__._set_pack_summary_cache(cache_value)
        self._pack_summary_cache = cache_value
        return packs

    async def GetPack(self, PackId: int) -> RuleConfigPackVO:
        """获取单个规则配置 pack。"""
        async with GetAsyncSession() as session:
            row = (
                await session.execute(
                    text(
                        """
                        SELECT
                            child.id AS group_id,
                            child.name AS subtype,
                            COALESCE(child.document_type_id, root.document_type_id) AS document_type_id,
                            dt.name AS document_type_name,
                            root.id AS root_group_id,
                            COALESCE(root.name, child.name) AS main_type,
                            em.name AS entry_module_name
                        FROM leaudit_evaluation_point_groups child
                        LEFT JOIN leaudit_evaluation_point_groups root
                          ON root.id = child.pid
                         AND root.deleted_at IS NULL
                        LEFT JOIN leaudit_document_types dt
                          ON dt.id = COALESCE(child.document_type_id, root.document_type_id)
                        LEFT JOIN leaudit_entry_modules em
                          ON em.id = COALESCE(child.entry_module_id, root.entry_module_id, dt.entry_module_id)
                        WHERE child.id = :group_id
                          AND child.deleted_at IS NULL
                          AND COALESCE(child.pid, 0) <> 0
                        LIMIT 1
                        """
                    ),
                    {"group_id": PackId},
                )
            ).mappings().first()

        if not row:
            raise LeauditException(StatusCodeEnum.HTTP_404_NOT_FOUND, "规则配置 pack 不存在")

        rule_set_map = await self._load_rule_set_meta_map()
        return await self._build_pack_vo(row, rule_set_map)

    async def _build_pack_vo(self, row, rule_set_map: dict[int, dict[str, object]]) -> RuleConfigPackVO:
        """构建单个 pack 聚合对象。"""
        group_id = int(row["group_id"])
        binding = await self._load_effective_binding(group_id)

        document_type = str(row["document_type_name"] or "").strip()
        main_type = str(row["main_type"] or "").strip()
        subtype = str(row["subtype"] or "").strip() or "通用"
        module_type = str(row["entry_module_name"] or "").strip() or (f"{document_type}评查" if document_type else "规则配置")

        source_status = "empty"
        yaml_text = ""
        rule_set_id: int | None = None
        rule_type: str | None = None
        rule_name: str | None = None
        current_version_id: int | None = None
        fallback_version_id: int | None = None
        resolved_version_id: int | None = None
        has_usable_version = False
        usable_rule_count = 0
        binding_id: int | None = None

        if binding:
            binding_id = int(binding["id"])
            rule_set_id = int(binding["rule_set_id"])
            rule_set_meta = rule_set_map.get(rule_set_id, {})
            rule_type = str(rule_set_meta.get("rule_type") or "") or None
            rule_name = str(rule_set_meta.get("rule_name") or "") or None
            current_version_id = self._to_int(rule_set_meta.get("current_version_id"))
            fallback_version_id = self._to_int(rule_set_meta.get("fallback_version_id"))
            resolved_version_id = current_version_id or fallback_version_id
            has_usable_version = bool(rule_set_meta.get("has_usable_version"))
            usable_rule_count = int(rule_set_meta.get("usable_rule_count") or 0)
            if resolved_version_id is None:
                resolved_version_id = await self._load_latest_version_id(rule_set_id)
            if resolved_version_id is not None:
                yaml_text = await self._load_yaml_text_by_version_id(resolved_version_id)
                source_status = "ready" if yaml_text.strip() else "missing"

        return RuleConfigPackVO(
            packId=group_id,
            groupId=group_id,
            rootGroupId=self._to_int(row.get("root_group_id")),
            bindingId=binding_id,
            ruleSetId=rule_set_id,
            ruleType=rule_type,
            ruleName=rule_name,
            currentVersionId=current_version_id,
            fallbackVersionId=fallback_version_id,
            resolvedVersionId=resolved_version_id,
            hasUsableVersion=has_usable_version,
            usableRuleCount=usable_rule_count,
            documentTypeId=self._to_int(row.get("document_type_id")),
            documentType=document_type,
            moduleType=module_type,
            mainType=main_type or document_type,
            subtype=subtype,
            yamlText=yaml_text,
            sourceStatus=source_status,
        )

    async def _load_pack_rows(self):
        async with GetAsyncSession() as session:
            return (
                await session.execute(
                    text(
                        """
                        SELECT
                            child.id AS group_id,
                            child.name AS subtype,
                            COALESCE(child.document_type_id, root.document_type_id) AS document_type_id,
                            dt.name AS document_type_name,
                            root.id AS root_group_id,
                            COALESCE(root.name, child.name) AS main_type,
                            em.name AS entry_module_name
                        FROM leaudit_evaluation_point_groups child
                        LEFT JOIN leaudit_evaluation_point_groups root
                          ON root.id = child.pid
                         AND root.deleted_at IS NULL
                        LEFT JOIN leaudit_document_types dt
                          ON dt.id = COALESCE(child.document_type_id, root.document_type_id)
                        LEFT JOIN leaudit_entry_modules em
                          ON em.id = COALESCE(child.entry_module_id, root.entry_module_id, dt.entry_module_id)
                        WHERE child.deleted_at IS NULL
                          AND COALESCE(child.pid, 0) <> 0
                        ORDER BY COALESCE(root.sort_order, 0) ASC, root.id ASC, child.sort_order ASC, child.id ASC
                        """
                    )
                )
            ).mappings().all()

    async def _load_effective_binding(self, group_id: int):
        """读取当前二级分组实际生效的规则集绑定。"""
        async with GetAsyncSession() as session:
            row = (
                await session.execute(
                    text(
                        """
                        SELECT id, rule_set_id
                        FROM leaudit_rule_group_bindings
                        WHERE group_id = :group_id
                          AND deleted_at IS NULL
                          AND is_active = TRUE
                        ORDER BY priority DESC, id ASC
                        LIMIT 1
                        """
                    ),
                    {"group_id": group_id},
                )
            ).mappings().first()
        return row

    async def _load_effective_binding_map(self, group_ids: list[int]) -> dict[int, dict[str, Any]]:
        if not group_ids:
            return {}
        async with GetAsyncSession() as session:
            rows = (
                await session.execute(
                    text(
                        """
                        SELECT id, group_id, rule_set_id
                        FROM (
                            SELECT
                                id,
                                group_id,
                                rule_set_id,
                                ROW_NUMBER() OVER (PARTITION BY group_id ORDER BY priority DESC, id ASC) AS rn
                            FROM leaudit_rule_group_bindings
                            WHERE deleted_at IS NULL
                              AND is_active = TRUE
                              AND group_id = ANY(:group_ids)
                        ) t
                        WHERE rn = 1
                        """
                    ),
                    {"group_ids": group_ids},
                )
            ).mappings().all()
        return {int(row["group_id"]): dict(row) for row in rows}

    async def _load_rule_set_meta_map(self) -> dict[int, dict[str, object]]:
        items = await self.RuleService.ListSets()
        return {
            item.id: {
                "rule_type": item.ruleType,
                "rule_name": item.ruleName,
                "current_version_id": item.currentVersionId,
                "fallback_version_id": item.fallbackVersionId,
                "has_usable_version": item.hasUsableVersion,
                "usable_rule_count": item.usableRuleCount,
            }
            for item in items
        }

    async def _load_rule_set_meta_map_by_ids(self, rule_set_ids: list[int]) -> dict[int, dict[str, object]]:
        if not rule_set_ids:
            return {}
        async with GetAsyncSession() as session:
            rows = (
                await session.execute(
                    text(
                        """
                        SELECT
                            rs.id,
                            rs.rule_type,
                            rs.rule_name,
                            rs.current_version_id,
                            current_rv.id AS usable_current_version_id,
                            fallback_rv.id AS fallback_version_id,
                            CASE
                                WHEN current_rv.id IS NOT NULL OR fallback_rv.id IS NOT NULL THEN TRUE
                                ELSE FALSE
                            END AS has_usable_version
                        FROM leaudit_rule_sets rs
                        LEFT JOIN leaudit_rule_versions current_rv
                          ON current_rv.id = rs.current_version_id
                         AND current_rv.status = 'published'
                        LEFT JOIN LATERAL (
                            SELECT rv.id
                            FROM leaudit_rule_versions rv
                            WHERE rv.rule_set_id = rs.id
                              AND rv.status = 'published'
                              AND (rs.current_version_id IS NULL OR rv.id <> rs.current_version_id)
                            ORDER BY rv.version_seq DESC, rv.id DESC
                            LIMIT 1
                        ) fallback_rv ON TRUE
                        WHERE rs.deleted_at IS NULL
                          AND rs.id = ANY(:rule_set_ids)
                        """
                    ),
                    {"rule_set_ids": rule_set_ids},
                )
            ).mappings().all()
        return {
            int(row["id"]): {
                "rule_type": row["rule_type"],
                "rule_name": row["rule_name"],
                "current_version_id": row["current_version_id"],
                "fallback_version_id": row["fallback_version_id"],
                "has_usable_version": row["has_usable_version"],
            }
            for row in rows
        }

    async def _load_version_oss_map(self, version_ids: list[int]) -> dict[int, str]:
        if not version_ids:
            return {}
        async with GetAsyncSession() as session:
            rows = (
                await session.execute(
                    text(
                        """
                        SELECT id, oss_url
                        FROM leaudit_rule_versions
                        WHERE id = ANY(:version_ids)
                        """
                    ),
                    {"version_ids": version_ids},
                )
            ).mappings().all()
        return {int(row["id"]): str(row["oss_url"] or "") for row in rows if row.get("oss_url")}

    async def _load_latest_version_map(self, rule_set_ids: list[int]) -> dict[int, int]:
        if not rule_set_ids:
            return {}
        async with GetAsyncSession() as session:
            rows = (
                await session.execute(
                    text(
                        """
                        SELECT DISTINCT ON (rule_set_id) rule_set_id, id
                        FROM leaudit_rule_versions
                        WHERE rule_set_id = ANY(:rule_set_ids)
                        ORDER BY rule_set_id, version_seq DESC, id DESC
                        """
                    ),
                    {"rule_set_ids": rule_set_ids},
                )
            ).mappings().all()
        return {int(row["rule_set_id"]): int(row["id"]) for row in rows}

    async def _load_yaml_text_by_version_id(self, version_id: int) -> str:
        async with GetAsyncSession() as session:
            row = (
                await session.execute(
                    text(
                        """
                        SELECT oss_url
                        FROM leaudit_rule_versions
                        WHERE id = :version_id
                        LIMIT 1
                        """
                    ),
                    {"version_id": version_id},
                )
            ).mappings().first()

        if not row or not row["oss_url"]:
            return ""

        try:
            return (await self.OssService.DownloadBytes(row["oss_url"])).decode("utf-8")
        except Exception:
            return ""

    async def _load_yaml_summaries(self, version_oss_map: dict[int, str]) -> dict[int, dict[str, Any]]:
        def _extract_rule_dependencies(rule: Any) -> list[str]:
            dependencies: list[str] = []

            for item in list(getattr(rule, "dependencies", []) or []):
                value = str(item or "").strip()
                if value:
                    dependencies.append(value)

            for stage in list(getattr(rule, "stages", []) or []):
                field = str(getattr(stage, "field", "") or "").strip()
                if field:
                    dependencies.append(field)

                for attr_name in ("seal_id", "signature_id", "element"):
                    attr_value = str(getattr(stage, attr_name, "") or "").strip()
                    if attr_value:
                        dependencies.append(attr_value)

                for item in list(getattr(stage, "fields", []) or []):
                    value = str(item or "").strip()
                    if value:
                        dependencies.append(value)

                prompt = str(getattr(stage, "prompt", "") or "")
                if prompt:
                    dependencies.extend(
                        match.strip()
                        for match in re.findall(r"\{\{\s*([^}]+?)\s*\}\}", prompt)
                        if match.strip()
                    )

            deduped: list[str] = []
            seen: set[str] = set()
            for item in dependencies:
                if item in seen:
                    continue
                seen.add(item)
                deduped.append(item)
            return deduped

        async def _load_one(version_id: int, oss_url: str):
            if not oss_url:
                return version_id, {"loaded": False, "yaml_name": "", "rules": []}
            cached = self._yaml_summary_cache.get(version_id)
            now = time.monotonic()
            if cached and now - cached[0] <= 120:
                return version_id, cached[1]
            try:
                yaml_text = (await self.OssService.DownloadBytes(oss_url)).decode("utf-8")
                if not yaml_text.strip():
                    summary = {"loaded": False, "yaml_name": "", "rules": []}
                    self._yaml_summary_cache[version_id] = (time.monotonic(), summary)
                    return version_id, summary
                rules_file = self.Validator.ParseValidated(yaml_text)
                groups: dict[str, str] = {}
                try:
                    for group in getattr(rules_file, "rules", []) or []:
                        group_name = getattr(group, "group", "") or ""
                        for rule in getattr(group, "rules", []) or []:
                            groups[getattr(rule, "rule_id", "") or ""] = group_name
                except Exception:
                    groups = {}
                summaries = []
                for rule in getattr(rules_file, "flat_rules", []) or []:
                    stage_items = []
                    check_types = []
                    for idx, stage in enumerate(getattr(rule, "stages", []) or [], start=1):
                        check = str(getattr(stage, "check", "") or getattr(stage, "type", "") or "")
                        check_types.append(check)
                        content = check
                        if check == "ai":
                            content = str(getattr(stage, "prompt", "") or "")
                        elif hasattr(stage, "fields") and getattr(stage, "fields"):
                            content = "、".join(str(item) for item in getattr(stage, "fields")[:3])
                        stage_items.append({"id": str(idx), "check": check, "content": content})
                    summaries.append({
                        "id": getattr(rule, "rule_id", "") or getattr(rule, "name", "") or "-",
                        "ruleId": getattr(rule, "rule_id", "") or "-",
                        "name": getattr(rule, "name", "") or getattr(rule, "rule_id", "") or "未命名规则",
                        "group": groups.get(getattr(rule, "rule_id", "") or "", getattr(rule, "group", "") or "未分组"),
                        "risk": str(getattr(rule, "risk", "medium") or "medium"),
                        "score": str(getattr(rule, "score", "0") or "0"),
                        "type": str(getattr(rule, "type", "deterministic") or "deterministic"),
                        "checkTypes": [item for item in check_types if item],
                        "logic": str(getattr(rule, "logic", "") or ""),
                        "subRules": stage_items,
                        "subRuleIds": list(getattr(rule, "rules", []) or []),
                        "scope": list(getattr(rule, "scope", []) or []),
                        "dependencies": _extract_rule_dependencies(rule),
                        "stageCount": len(stage_items),
                        "appliesIn": list(getattr(rule, "applies_in", []) or []),
                        "prompt": stage_items[0]["content"] if len(stage_items) == 1 and stage_items[0]["check"] == "ai" else "",
                        "description": str(getattr(rule, "desc", "") or ""),
                    })
                summary_map = {
                    str(item.get("ruleId") or item.get("id") or ""): item
                    for item in summaries
                    if str(item.get("ruleId") or item.get("id") or "")
                }
                for item in summaries:
                    if item.get("dependencies"):
                        continue
                    sub_rule_ids = [str(sub_rule_id or "").strip() for sub_rule_id in list(item.get("subRuleIds") or []) if str(sub_rule_id or "").strip()]
                    if not sub_rule_ids:
                        continue
                    merged_dependencies: list[str] = []
                    seen_dependencies: set[str] = set()
                    for sub_rule_id in sub_rule_ids:
                        child_summary = summary_map.get(sub_rule_id)
                        if not child_summary:
                            continue
                        for dependency in list(child_summary.get("dependencies") or []):
                            normalized_dependency = str(dependency or "").strip()
                            if not normalized_dependency or normalized_dependency in seen_dependencies:
                                continue
                            seen_dependencies.add(normalized_dependency)
                            merged_dependencies.append(normalized_dependency)
                    item["dependencies"] = merged_dependencies
                summary = {
                    "loaded": True,
                    "yaml_name": str(getattr(getattr(rules_file, "metadata", None), "name", "") or ""),
                    "rules": summaries,
                }
                self._yaml_summary_cache[version_id] = (time.monotonic(), summary)
                return version_id, summary
            except Exception:
                summary = {"loaded": False, "yaml_name": "", "rules": []}
                self._yaml_summary_cache[version_id] = (time.monotonic(), summary)
                return version_id, summary

        results = await asyncio.gather(*[_load_one(version_id, oss_url) for version_id, oss_url in version_oss_map.items()])
        return dict(results)

    async def _load_latest_version_id(self, rule_set_id: int) -> int | None:
        async with GetAsyncSession() as session:
            row = (
                await session.execute(
                    text(
                        """
                        SELECT id
                        FROM leaudit_rule_versions
                        WHERE rule_set_id = :rule_set_id
                        ORDER BY version_seq DESC, id DESC
                        LIMIT 1
                        """
                    ),
                    {"rule_set_id": rule_set_id},
                )
            ).mappings().first()
        return self._to_int(row.get("id")) if row else None

    async def _load_version_seq_by_id(self, version_id: int | None) -> int:
        if version_id is None:
            return -1
        async with GetAsyncSession() as session:
            row = (
                await session.execute(
                    text(
                        """
                        SELECT version_seq
                        FROM leaudit_rule_versions
                        WHERE id = :version_id
                        LIMIT 1
                        """
                    ),
                    {"version_id": version_id},
                )
            ).mappings().first()
        return int(row.get("version_seq") or -1) if row else -1

    def _to_int(self, value) -> int | None:
        if value is None:
            return None
        return int(value)


_RULE_CONFIG_SERVICE_SINGLETON: RuleConfigServiceImpl | None = None


def GetRuleConfigServiceSingleton() -> RuleConfigServiceImpl:
    """返回共享的规则配置服务实例，供控制器和预热任务共用缓存。"""
    global _RULE_CONFIG_SERVICE_SINGLETON
    if _RULE_CONFIG_SERVICE_SINGLETON is None:
        _RULE_CONFIG_SERVICE_SINGLETON = RuleConfigServiceImpl()
    return _RULE_CONFIG_SERVICE_SINGLETON
