from __future__ import annotations

from typing import Any, Iterable, Optional


def normalize_int_id_list(mixed_ids: Iterable[Any]) -> list[int]:
    """
    将混合类型的 id 列表归一化为 int 列表。

    规则：
    - int/可转 int 的 str -> int
    - 其他（None/空字符串/不可解析）忽略
    """

    out: list[int] = []
    for x in mixed_ids or []:
        if x is None:
            continue
        if isinstance(x, bool):
            # 避免 True/False 被当作 1/0
            continue
        try:
            s = str(x).strip()
            if s == "":
                continue
            out.append(int(s))
        except Exception:
            continue
    return out


def merge_exclude_ids(
    *,
    already_recommended_ids: Iterable[Any],
    touched_or_viewed_ids: Iterable[Any],
    extra_exclude_content_ids: Optional[Iterable[int]] = None,
) -> list[int]:
    """
    合并并去重排除 id（保持首次出现顺序）。
    """

    merged = list(normalize_int_id_list(list(already_recommended_ids or []) + list(touched_or_viewed_ids or [])))
    if extra_exclude_content_ids:
        merged += [int(x) for x in extra_exclude_content_ids if x is not None]

    seen: set[int] = set()
    out: list[int] = []
    for cid in merged:
        if cid in seen:
            continue
        seen.add(cid)
        out.append(cid)
    return out


def normalize_or_default_locale(locale: Optional[str]) -> str:
    """
    locale 防御式归一化：
    - 未传/空 -> 默认 "en"
    - 其他 -> 原样返回，由下游 normalize_locale 做严格校验
    """

    if locale is None:
        return "en"
    raw = str(locale).strip()
    return raw or "en"


def clamp_personalization_power(power: Any, *, fallback_level: int) -> float:
    """
    按回退层级对 personalization_power 做防御式约束。

    - L0：不改
    - L1：<= 0.5
    - L2/L3：= 0
    """

    try:
        p = float(power)
    except Exception:
        p = 0.0
    if p != p:
        p = 0.0

    if int(fallback_level) >= 2:
        return 0.0
    if int(fallback_level) >= 1:
        return min(p, 0.5)
    return p

