"""查询分类树、需求自身与后验的独立分布，供批量分级统一口径。"""
from __future__ import annotations

import logging
from typing import Optional

from agents.demand_grade_agent.tools.demand_priority import (
    DEMAND_PRIORITY_SCORE_METHOD,
    build_demand_priority_index,
)
from agents.demand_grade_agent.tools.shared import distribution_summary, normalize_biz_dt, to_float
from supply_infra.scoring.posterior import POSTERIOR_EFFECT_ACCEPTABLE_FLOOR
from supply_agent.tools import tool
from supply_infra.db.repositories.category_tree_weight_repo import CategoryTreeWeightRepository
from supply_infra.db.repositories.multi_demand_pool_di_repo import MultiDemandPoolDiRepository
from supply_infra.db.session import get_session

logger = logging.getLogger(__name__)

_DIM_FIELDS: tuple[tuple[str, str], ...] = (
    ("total_score", "全局热度total_score"),
)


def _format_dist(label: str, dist: dict) -> str:
    if dist["count"] == 0:
        return f"{label}: 无数据"
    return (
        f"{label} (n={dist['count']}): "
        f"min={dist['min']:.4f} p25={dist['p25']:.4f} p50={dist['p50']:.4f} "
        f"p75={dist['p75']:.4f} p90={dist['p90']:.4f} max={dist['max']:.4f}"
    )


@tool
def query_score_distribution(biz_dt: Optional[str] = None) -> str:
    """
    查询指定业务日分类树全局热度、需求自身来源归一分与后验分布。

    建议在批量分级任务开始时调用一次，分别参考各自分位数制定本批次统一的分档阈值
    （例如 total_score 前 10% 视为全局热度很高），避免同一批次内多次判断标准漂移。
    需求自身分按来源内 rank 归一后对已有来源取均值，不直接合并跨来源 raw weight；
    后验 real_rov_7d_avg / real_vov_7d_avg 存的是相对全局的 rov_diff / vov_diff；
    分布只统计 real_rov_7d_count>0（有真实验证数据）的子集，因为无验证数据的行 avg 无意义。
    判级绝对阈值：>0 效果非常好，[-0.2,0) 可接受，<-0.2 效果不佳。

    Args:
        biz_dt: 业务日期 YYYYMMDD，可选；不传则使用 category_tree_weight 最新业务日。

    Returns:
        每个维度一行分布摘要，例如：
        全局热度total_score (n=1500): min=0.0000 p25=0.8500 ...
        后验real_rov_7d_avg(仅count>0子集) (n=210): ...
    """
    normalized_dt, err = normalize_biz_dt(biz_dt)
    if err:
        return err

    try:
        with get_session() as session:
            repo = CategoryTreeWeightRepository(session)
            resolved_dt = normalized_dt or repo.get_latest_biz_dt()
            if not resolved_dt:
                return "category_tree_weight 暂无数据"

            weights = repo.list_by_biz_dt(resolved_dt)
            if not weights:
                return f"biz_dt={resolved_dt} category_tree_weight 无数据"

            node_count = len(weights)
            dim_values: dict[str, list[float]] = {}
            for field, _ in _DIM_FIELDS:
                if field == "total_score":
                    dim_values[field] = [
                        v
                        for v in (to_float(getattr(w, field)) for w in weights)
                        if v is not None and v > 0
                    ]
                else:
                    count_field = field.replace("_avg", "_count")
                    dim_values[field] = [
                        v
                        for w in weights
                        if int(getattr(w, count_field, 0) or 0) > 0
                        for v in [to_float(getattr(w, field))]
                        if v is not None
                    ]
            posterior_values = [
                v
                for v in (to_float(w.real_rov_7d_avg) for w in weights if w.real_rov_7d_count > 0)
                if v is not None
            ]
            demand_priority_index = build_demand_priority_index(
                MultiDemandPoolDiRepository(session).list_by_biz_dt(resolved_dt)
            )
            demand_priority_values = [
                float(item["source_rank_score"])
                for item in demand_priority_index.values()
                if item["source_rank_score"] is not None
            ]

        lines = [f"biz_dt={resolved_dt} 共 {node_count} 个树节点"]
        for field, label in _DIM_FIELDS:
            lines.append(_format_dist(label, distribution_summary(dim_values[field])))

        lines.append(
            _format_dist(
                f"后验rov_diff_avg(仅count>0子集，共{len(posterior_values)}个节点有验证数据)",
                distribution_summary(posterior_values),
            )
        )
        lines.append(
            "后验效果档位："
            f">0 效果非常好；[{POSTERIOR_EFFECT_ACCEPTABLE_FLOOR}, 0) 可接受；"
            f"<{POSTERIOR_EFFECT_ACCEPTABLE_FLOOR} 效果不佳（ROV/VOV 各自独立判断）。"
        )
        lines.append(
            _format_dist(
                "需求自身来源归一分(0-100，来源内排名后对已有来源取均值)",
                distribution_summary(demand_priority_values),
            )
        )
        lines.append(f"需求自身分口径={DEMAND_PRIORITY_SCORE_METHOD['name']}；不同来源原始 weight 禁止直接相加。")
        lines.append("注意：需求自身来源归一分与分类树 total_score 是两类独立证据，不得直接相加或共用阈值。")

        message = "\n".join(lines)
        logger.info("query_score_distribution completed: biz_dt=%s nodes=%d", resolved_dt, node_count)
        return message

    except Exception as e:
        logger.error("query_score_distribution failed: %s", e, exc_info=True)
        return f"查询分数分布失败: {e}"


def main() -> None:
    print(query_score_distribution())


if __name__ == "__main__":
    main()
