from __future__ import annotations

from typing import Any, Literal, Optional

from pydantic import BaseModel, Field

Scene = Literal["feed", "push", "widget"]

EmptyReason = Literal["hard_filter_all", "freqcap_all", "pool_empty", "unknown"]


class MissingFields(BaseModel):
    """
    画像字段缺失情况（布尔结构）。
    """

    need: bool = False
    context: bool = False
    emotion: bool = False


class RecoMeta(BaseModel):
    """
    推荐模块统一可观测载荷（返回给调用方；调用方负责上报/落库/打点）。
    """

    scene: Scene

    candidate_pool_size_raw: int = 0
    candidate_pool_size_after_hard_filter: int = 0
    candidate_pool_size_after_dedup: int = 0
    candidate_pool_size_after_freqcap: int = 0

    fallback_level_final: int = 0
    served_k: int = 0

    # served_k=0 时必填；served_k>0 时建议为 None
    empty_reason: Optional[EmptyReason] = None

    conf_U: float = 1.0
    missing_fields: MissingFields = Field(default_factory=MissingFields)

    # 可选：Hard Filter 风险命中统计（按 flag 聚合）
    risk_filtered_count_by_flag: dict[str, int] = Field(default_factory=dict)

    # 可选：Freqcap 过滤统计（sentence/author/template）
    freqcap_filtered_counts: dict[str, int] = Field(default_factory=dict)

    # 可选：调参快照（V1 可先只在内部事件使用）
    config_snapshot: dict[str, Any] = Field(default_factory=dict)

