"""Reviewed UI entries; stable calculator IDs remain private persistence keys."""
from .program_registry import list_programs


# Classify by the engineering task, never by substrings in a program title.
CATEGORIES = (
    ("pipe", "压力管流", ("2-05", "6-03", "6-04", "6-05", "6-06", "6-07", "7-08")),
    ("channel", "明渠水力学", ("3-02", "3-03", "3-04", "3-05", "3-06", "3-07", "3-19", "3-20", "3-09", "4-02")),
    ("varied-flow", "渐变流与水工过流", ("4-01", "4-03", "3-14", "3-16", "7-01", "7-02", "7-03", "7-04", "7-05", "7-06", "7-07", "7-10", "7-11", "7-12")),
    ("pump", "泵站计算", ("2-06", "2-07", "2-08", "2-09")),
    ("hydrology", "水文与设计洪水", ("1-01", "1-02", "1-03", "1-04", "1-05", "1-06", "1-08", "1-09", "1-10", "1-11", "1-12", "1-13")),
    ("seepage", "渗流与防渗", ("3-08", "5-07")),
    ("sediment", "泥沙与河床演变", ("3-10", "3-23", "3-13", "3-24", "5-02")),
    ("reservoir", "水库调节", ("5-01", "5-03", "5-08")),
    ("irrigation", "灌溉工程", ("2-01", "2-02", "2-03", "2-04", "3-01", "3-22")),
    ("structure", "水工结构与稳定", ("3-11", "3-12", "3-18", "3-21", "5-04", "5-05", "5-06", "5-09", "6-08")),
    ("bridge", "桥孔与净空", ("3-15", "3-17", "3-25", "7-09")),
    ("wave", "波浪与风壅", ("4-04", "4-05")),
    ("power", "水电与机电", ("6-01", "6-02", "6-09", "6-10", "6-11")),
)

CATEGORY_DETAILS = {
    "pipe": ("pipette", "管径、损失与水锤"),
    "channel": ("waves", "均匀流、断面与流态"),
    "varied-flow": ("chart-spline", "水面线、壅水与消能"),
    "pump": ("gauge", "扬程、安装与动力"),
    "hydrology": ("cloud-rain", "设计暴雨、洪水与频率"),
    "seepage": ("droplets", "渠道渗漏与坝基防渗"),
    "sediment": ("shovel", "河床演变、冲刷与淤积"),
    "reservoir": ("dam", "调洪、库容与年调节"),
    "irrigation": ("sprout", "需水、引水与渠道高程"),
    "structure": ("brick-wall", "结构尺寸、配筋与安全"),
    "bridge": ("landmark", "桥孔、阻水与净空"),
    "wave": ("wind", "风浪参数与增水"),
    "power": ("zap", "水能、输电与容量"),
}

SECTIONS = (
    ("断面均匀流", ("3-02", "3-19", "3-20", "3-09", "4-02")),
    ("断面设计", ("3-06", "3-07")),
    ("流态与输沙条件", ("3-03", "3-04", "3-05")),
    ("水面线与壅水", ("4-01", "4-03", "3-14", "3-16")),
    ("水工建筑物过流", ("7-01", "7-02", "7-03", "7-05", "7-07", "7-10", "7-11", "7-12")),
    ("水跃与消能", ("7-04", "7-06")),
    ("设计暴雨与频率", ("1-01", "1-13")),
    ("设计洪水推求", ("1-02", "1-03", "1-04", "1-05", "1-08", "1-09", "1-11", "1-12")),
    ("过程线与成果复核", ("1-06", "1-10")),
    ("河床演变", ("3-10", "3-23")),
    ("冲刷计算", ("3-13", "3-24")),
    ("泥沙淤积", ("5-02",)),
)

GROUPS = {
    "3-24": {
        "name": "冲刷计算", "member_ids": ("3-24", "3-13"),
        "modes": (("3-24", "单项规范公式"), ("3-13", "一般冲刷与局部冲刷综合")),
        "outputs": ("护岸与丁坝冲刷", "桥下一般冲刷", "桥墩局部冲刷", "综合冲刷线"),
        "reason": "同一 scour.calculate_scour 公式库的单项与综合应用；一个入口内选计算方式，不删公式。",
    },
    "4-03": {
        "name": "多断面水面线计算（能量方程标准步法）", "member_ids": ("4-03", "4-01"),
        "legacy_labels": {"4-01": "既有水面线参数草稿"},
        "reason": "_calc_hec_standard_step 直接调用 calculate_wsl_01，仅更换标题与来源文字，属于重复入口。",
    },
    "7-12": {
        "name": "侧堰分水与泄流计算", "member_ids": ("7-12", "7-02"),
        "legacy_labels": {"7-02": "既有堰流参数草稿"},
        "reason": "旧溢流堰入口实际调用 calculate_side_weir，并非独立溢流堰模型；保留完整侧堰字段与历史记录。",
    },
}

DISPLAY_NAMES = {
    "3-09": "河道矩形概化断面流量估算",
    "4-02": "实测断面曼宁水位与流量关系",
    "3-12": "基础埋深安全校核",
    "3-14": "上游回水曲线计算",
    "3-16": "桥前壅水高度计算",
    "2-05": "提灌站压力管道尺寸计算",
    "6-03": "水电站压力管道直径与壁厚",
    "6-10": "水电站主变压器容量校核",
}


def library_catalog():
    programs = list_programs()
    by_id = {p["program_id"]: p for p in programs}
    memberships = [(pid, key, label) for key, label, ids in CATEGORIES for pid in ids]
    assigned = [pid for pid, _key, _label in memberships]
    if len(assigned) != len(set(assigned)) or set(assigned) != set(by_id):
        raise ValueError("Every calculator must have exactly one reviewed category")
    categories = {pid: (key, label) for pid, key, label in memberships}
    section_pairs = [(pid, label) for label, ids in SECTIONS for pid in ids]
    if len({pid for pid, _label in section_pairs}) != len(section_pairs):
        raise ValueError("A calculator cannot appear in multiple presentation sections")
    sections = dict(section_pairs)
    if set(sections) - set(by_id):
        raise ValueError("Presentation sections must reference existing calculators")
    if any(len({categories[pid][0] for pid in ids}) != 1 for _label, ids in SECTIONS):
        raise ValueError("Presentation sections must stay within one profession")
    owners = {pid: owner for owner, group in GROUPS.items() for pid in group["member_ids"]}
    entries = []
    for raw in programs:
        pid = raw["program_id"]
        if owners.get(pid, pid) != pid:
            continue
        group = GROUPS.get(pid, {})
        members = group.get("member_ids", (pid,))
        category, label = categories[pid]
        if any(categories[member][0] != category for member in members):
            raise ValueError("Merged entry cannot span unrelated categories")
        entries.append({
            **raw, "name": group.get("name", DISPLAY_NAMES.get(pid, raw["name"])),
            "category_key": category, "category": label, "member_ids": list(members),
            "section": sections.get(pid, ""),
            "modes": [{"program_id": mid, "name": name} for mid, name in group.get("modes", ())],
            "legacy_labels": group.get("legacy_labels", {}),
            "inputs": list(dict.fromkeys(x for member in members for x in by_id[member]["inputs"])),
            "outputs": list(group.get("outputs", raw["outputs"])),
            "search_names": [by_id[member]["name"] for member in members],
        })
    return {"entries": entries, "categories": [{"key": key, "name": label,
            "icon": CATEGORY_DETAILS[key][0], "description": CATEGORY_DETAILS[key][1]}
            for key, label, _ids in CATEGORIES]}
