from __future__ import annotations

from dataclasses import dataclass, field
from typing import Any, Callable

from app.services.scenes.gate_rules import GateRule, SceneRoute


@dataclass(frozen=True)
class SceneDescriptor:
    """单个业务场景的声明式描述符。

    一个场景的"如何识别""走哪条路""做什么""要什么槽位""能否恢复上下文"
    全部在这里声明,实现接入成本 O(1)。
    """

    scene_id: str
    """唯一标识,等同 task_type(如 expense_application / query_travel_standard)。"""

    label: str
    """中文标签,用于 system prompt、前端展示、日志。"""

    signal_keywords: tuple[str, ...] = ()
    """规则识别的关键词;聚合进 off_topic 信号池,也用于 CHOICE 门控规则匹配。"""

    ontology_fields: tuple[str, ...] = ()
    """该场景允许的 canonical 槽位;为空表示沿用全局 BUSINESS_CANONICAL_FIELDS。"""

    gate: GateRule = GateRule.MODEL_ONLY
    """门控规则,决定场景如何参与 gate_classify 裁决。"""

    route: SceneRoute = SceneRoute.MODEL_INTENT
    """路由策略,gate_classify 命中后决定走图的哪条边。"""

    handler: Callable[..., Any] | None = None
    """执行函数;HANDLER_ONLY 路由必填,其他路由可选。"""

    action_steps_builder: Callable[[Any], list[Any]] | None = None
    """动作步骤生成函数;把 StewardTask 转换为白名单 action steps。"""

    can_resume: bool = False
    """是否参与"再提交"上下文恢复。"""

    flow_id: str | None = None
    """候选流程确认使用的 flow_id;查询/命令类为 None。"""

    prompt_fragment: str = ""
    """注入 steward_intent_agent system prompt 的识别指引片段。"""

    priority: int = 100
    """gate_classify 的匹配优先级;数字小的优先。"""

    side_effect_actions: tuple[str, ...] = ()
    """该场景产生副作用的 action_type 集合。"""

    noop_actions: tuple[str, ...] = ()
    """该场景的无副作用 action_type 集合(填充/预览/校验等)。"""

    assigned_agent: str = ""
    """该场景对应的执行 agent 标识。"""

    executor: Callable[..., Any] | None = None
    """副作用/查询动作的执行器;供 action_executor 通过 registry 分发。"""
