"""节点 2/6：过滤（filter）。

三级过滤，全部带兜底、单级失败不影响其它级：
1) 合规黑名单（全局 + 国家 extra）
2) 真实人物（名单 + Firstname Lastname 模式，仅对 gt_trending 源，避免误删风格词）
3) 设计相关性（剔除泛新闻/科技/赛事词）
"""
from typing import Any, Dict, List

from graph.scoring import apply_blacklist, filter_design_relevance, filter_person_names, filter_query_noise
from graph.validate import validate_rows, with_fallback


@with_fallback("filter")
def filter_node(state: Dict[str, Any]) -> Dict[str, Any]:
    rows: List[Dict[str, Any]] = state.get("raw_rows") or []
    config = state["config"]
    cc = state["country_config"]
    country = state.get("country", "")

    # 黑名单：全局 + 国家专属
    bl = [str(b).lower() for b in (config.get("blacklist") or [])]
    bl += [str(b).lower() for b in (cc.get("extra_blacklist") or [])]
    bl = list(set(bl))

    name_cfg = config.get("name_filter") or {}
    rel_cfg = config.get("relevance") or {}

    dropped_total = 0

    kept, dropped = apply_blacklist(rows, bl)
    dropped_total += len(dropped)

    if name_cfg.get("enabled", True):
        kept, dropped = filter_person_names(
            kept,
            extra_names=name_cfg.get("extra_names"),
            patterns=name_cfg.get("patterns"),
            exemptions=name_cfg.get("exemptions"),
            pattern_sources=set(name_cfg.get("pattern_sources") or ["gt_trending"]),
        )
        dropped_total += len(dropped)

    if rel_cfg.get("enabled", True):
        kept, dropped = filter_design_relevance(
            kept,
            drop_patterns=rel_cfg.get("drop_patterns"),
            keep_patterns=rel_cfg.get("keep_patterns"),
        )
        dropped_total += len(dropped)

    # ③ 新闻类热点（天气/灾害/政治/事故等突发新闻，非印花主题；按国家语言过滤）
    if kept:
        from graph.scoring import filter_news
        kept, dropped = filter_news(kept, country)
        dropped_total += len(dropped)

    # ④ 查询噪声（问句/命名清单/损坏碎片/模糊名词）—— 防止被 Mock 误标 safe
    qn_cfg = config.get("query_noise") or {}
    if qn_cfg.get("enabled", True):
        kept, dropped = filter_query_noise(kept, enabled=True)
        dropped_total += len(dropped)

    kept = validate_rows(kept, "filter")
    stats = dict(state.get("stats") or {})
    stats["filter"] = {"kept": len(kept), "dropped": dropped_total}

    # 把过滤后的热点写入 collected_keywords.json（前台显示完整热点池用）：
    # 完整流水线（run_country）也会写，保证运行后前台能看到所有未用热点，
    # 而不是只显示简报（design_briefs 仅含本次限量生成的热点）。
    if kept:
        try:
            import json as _json
            from pathlib import Path as _Path
            p = _Path(state.get("cache_dir") or state.get("output_dir", "")) / "collected_keywords.json"
            p.parent.mkdir(parents=True, exist_ok=True)
            kws = [{"topic": r.get("topic", ""), "source": r.get("source", ""),
                    "kind": r.get("kind", ""), "raw_score": r.get("raw_score")} for r in kept]
            p.write_text(_json.dumps({"country": country,
                                      "collected_at": _datetime_now(),
                                      "keywords": kws}, ensure_ascii=False, indent=2),
                         encoding="utf-8")
            print(f"[filter] 已写入采集缓存 {len(kws)} 条（collected_keywords.json）")
        except Exception as e:  # noqa: BLE001
            print(f"[filter] 写入采集缓存失败: {e}")

    return {"filtered_rows": kept, "stats": stats}


def _datetime_now() -> str:
    import time
    return time.strftime("%Y-%m-%dT%H:%M:%S")
