"""
按热度维度查询有数据的全局分类树。

联查 global_tree_category + category_tree_weight，仅保留指定维度有样本的节点
（及其祖先，以保持树结构），格式化为 [id]名称（score）。
若节点下存在有数据的叶子节点，末尾标记 +。
"""
from __future__ import annotations

import logging

from agents.generate_demand_agent.tools.dim_constants import (
    DIM_KEYS,
    DIM_LABEL,
    HAS_DATA_LEAF_MARK,
    build_children_map,
    build_score_by_id,
    collect_descendant_leaves,
    format_score,
    resolve_biz_dt,
)
from supply_agent.tools import tool
from supply_infra.db.models.global_tree_category import GlobalTreeCategory
from supply_infra.db.repositories.category_tree_weight_repo import (
    CategoryTreeWeightRepository,
)
from supply_infra.db.repositories.global_tree_category_repo import (
    GlobalTreeCategoryRepository,
)
from supply_infra.db.session import get_session

logger = logging.getLogger(__name__)


def _has_data_in_subtree(
    category_id: int,
    children_map: dict[int | None, list[GlobalTreeCategory]],
    score_by_id: dict[int, tuple[float | None, int]],
    cache: dict[int, bool],
) -> bool:
    if category_id in cache:
        return cache[category_id]
    _, count = score_by_id.get(category_id, (None, 0))
    if count > 0:
        cache[category_id] = True
        return True
    for child in children_map.get(category_id, []):
        if _has_data_in_subtree(int(child.id), children_map, score_by_id, cache):
            cache[category_id] = True
            return True
    cache[category_id] = False
    return False


def _has_data_leaf_under(
    category_id: int,
    children_map: dict[int | None, list[GlobalTreeCategory]],
    score_by_id: dict[int, tuple[float | None, int]],
    cache: dict[int, bool],
) -> bool:
    """当前节点下（不含自身）是否存在 count>0 的叶子节点。"""
    if category_id in cache:
        return cache[category_id]
    for leaf_id in collect_descendant_leaves(category_id, children_map):
        if leaf_id == category_id:
            continue
        _, count = score_by_id.get(leaf_id, (None, 0))
        if count > 0:
            cache[category_id] = True
            return True
    cache[category_id] = False
    return False


def _format_weighted_tree(
    categories: list[GlobalTreeCategory],
    score_by_id: dict[int, tuple[float | None, int]],
) -> str:
    """格式化为有数据节点的完整树形文本。"""
    if not categories:
        return "（暂无分类数据）"

    children_map = build_children_map(categories)
    data_cache: dict[int, bool] = {}
    leaf_mark_cache: dict[int, bool] = {}
    lines: list[str] = []

    def visible_children(category_id: int) -> list[GlobalTreeCategory]:
        return [
            child
            for child in children_map.get(category_id, [])
            if _has_data_in_subtree(int(child.id), children_map, score_by_id, data_cache)
        ]

    def render(category: GlobalTreeCategory, depth: int) -> None:
        cid = int(category.id)
        if not _has_data_in_subtree(cid, children_map, score_by_id, data_cache):
            return

        avg, _count = score_by_id.get(cid, (None, 0))
        score_text = format_score(avg)
        mark = ""
        if _has_data_leaf_under(cid, children_map, score_by_id, leaf_mark_cache):
            mark = f" {HAS_DATA_LEAF_MARK}"
        indent = "  " * depth
        name = category.name or ""
        lines.append(f"{indent}[{cid}]{name}（{score_text}）{mark}")

        for child in visible_children(cid):
            render(child, depth + 1)

    for root in children_map.get(None, []):
        render(root, 0)

    if not lines:
        return "（该维度暂无有数据的分类）"
    return "\n".join(lines)


@tool
def query_category_tree_by_dim(dimension: str, biz_dt: str | None = None) -> str:
    """
    按热度维度查询有数据的全局分类树。

    联查 global_tree_category 与 category_tree_weight，仅返回指定维度 count>0
    的节点及其祖先。每行格式为 [id]名称（score）；若该节点下存在有数据的叶子节点，
    末尾带 + 标记。

    Args:
        dimension: 必选热度维度，只能是其一：
            - ext_pop：外部热度
            - plat_sust_pop：平台持续热度
            - plat_ly_pop：平台去年同期热度
            - recent_pop：近期热度
        biz_dt: 业务日 YYYYMMDD；省略则使用 category_tree_weight 最新业务日。

    Returns:
        层级分明的完整有数据分类树，例如：
        [1]表象（12.3） +
          [3]行为（8.5） +
            [19]表演（3.2）
          [4]内容形态（1.1）
    """
    dim = str(dimension or "").strip()
    if dim not in DIM_KEYS:
        allowed = "、".join(f"{k}（{DIM_LABEL[k]}）" for k in DIM_KEYS)
        return f"dimension 无效，只能是：{allowed}"

    try:
        with get_session() as session:
            categories = GlobalTreeCategoryRepository(session).list_active_categories()
            weight_repo = CategoryTreeWeightRepository(session)
            resolved_biz_dt, err = resolve_biz_dt(
                biz_dt,
                get_latest=weight_repo.get_latest_biz_dt,
                has_data=weight_repo.has_biz_dt,
                table_label="category_tree_weight 数据",
            )
            if err:
                return err

            weights = weight_repo.list_by_biz_dt(resolved_biz_dt)
            score_by_id = build_score_by_id(weights, dim)
            tree_text = _format_weighted_tree(categories, score_by_id)

        header = (
            f"维度={dim}（{DIM_LABEL[dim]}）| biz_dt={resolved_biz_dt} | "
            f"「{HAS_DATA_LEAF_MARK}」表示其下有带数据的叶子节点"
        )
        logger.info(
            "query_category_tree_by_dim completed: dim=%s biz_dt=%s",
            dim,
            resolved_biz_dt,
        )
        return f"{header}\n{tree_text}"

    except Exception as e:
        logger.error("query_category_tree_by_dim failed: %s", e, exc_info=True)
        return f"查询分类树权重失败: {e}"


def main() -> None:
    """本地手动验证：查 recent_pop 全树。"""
    print(query_category_tree_by_dim(dimension="recent_pop"))


if __name__ == "__main__":
    main()
