from __future__ import annotations

from datetime import datetime
from typing import Any, Literal, Optional

from pydantic import BaseModel, Field

from app.features.personalized_reco.observability.types import RecoMeta

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


class RecoConstraints(BaseModel):
    """
    推荐请求的可选约束（调用方可按需传入）。
    """

    exclude_content_ids: list[int] = Field(default_factory=list)
    exclude_author_ids: list[str] = Field(default_factory=list)
    exclude_template_ids: list[str] = Field(default_factory=list)

    # 候选池上限（用于资源保护）
    max_candidates_limit: Optional[int] = None

    # Push/Widget 作者/模板冷却窗口内的历史集合（增强频控输入）
    # 说明：若不提供（None），rerank_freqcap 会记录缺失并跳过该维度过滤
    recent_author_ids: Optional[list[str]] = None
    recent_template_ids: Optional[list[str]] = None


class RecoEngineConfig(BaseModel):
    """
    引擎级配置（V1 可调参项）。
    """

    # Feed 是否允许 served_k < k（允许不足）
    feed_allow_partial: bool = True
    # Feed 是否在不足时继续回退补齐
    feed_fill_with_fallback: bool = True

    # 候选拉取倍率（limit = min(max_candidates_limit, k * multiplier)）
    candidate_multiplier_feed: int = 10
    candidate_multiplier_push_widget: int = 30

    # 每层回退的最大候选数量下限（避免 k=1 但候选过少）
    min_candidates_per_level: int = 30

    # explanations 默认开启（但应保持轻量）
    enable_explanations: bool = True


class RecommendedItem(BaseModel):
    """
    引擎最终下发的推荐项。
    """

    content_id: int
    text: str
    final_score: float
    fallback_level_final: int

    # 解释信息：默认开启，但建议保持轻量（避免 payload 过大）
    explanations: Optional[dict[str, Any]] = None


class RecoEngineResult(BaseModel):
    """
    引擎输出容器：items + meta。
    """

    items: list[RecommendedItem] = Field(default_factory=list)
    meta: RecoMeta


class HardFilterResult(BaseModel):
    """
    Hard Filter 输出。
    """

    kept_items: list[Any] = Field(default_factory=list)
    removed_count: int = 0
    risk_filtered_count_by_flag: dict[str, int] = Field(default_factory=dict)
    # 每条内容的命中信息（仅用于 explanations；默认可为空）
    hits_by_content_id: dict[int, list[str]] = Field(default_factory=dict)


class RecommendRequest(BaseModel):
    """
    内部便捷结构（单测/集成时可用）。
    """

    scene: Scene
    user_profile: Any
    already_recommended_ids: list[Any] = Field(default_factory=list)
    touched_or_viewed_ids: list[Any] = Field(default_factory=list)
    k: int = 1
    now: datetime
    locale: Optional[str] = None
    constraints: Optional[RecoConstraints] = None
    config: Optional[RecoEngineConfig] = None

