#!/usr/bin/env python3
"""
fix_crawler_naming.py — 修复爬虫配置的名称列和分组列

功能：
1. 修复名称列：清理常见前缀后缀（"www." → 中文站点名、" [orphan]" 等）
2. 自动推断分组：从名称中检测省份/企业/环评等类别
3. 备份保护：--apply 前先自动备份

用法:
  python3 fix_crawler_naming.py --preview      # 预览修改（推荐先看）
  python3 fix_crawler_naming.py --preview 20    # 只看前20条
  python3 fix_crawler_naming.py --backup        # 只备份配置
  python3 fix_crawler_naming.py --apply         # 正式应用修改
"""

import json
import re
import sys
import os
from collections import Counter
from datetime import datetime

CONFIG_PATH = "/root/gov_crawler/daily_crawl_config.json"
BACKUP_DIR = "/root/gov_crawler/backups"

# ── 省份推断规则 ──
PROVINCE_MAP = {
    # 直接匹配
    "安徽": "安徽", "安徽省": "安徽",
    "江西": "江西", "江西省": "江西",
    "江苏": "江苏", "江苏省": "江苏",
    "山东": "山东", "山东省": "山东",
    "广东": "广东", "广东省": "广东",
    "河南": "河南", "河南省": "河南",
    "四川": "四川", "四川省": "四川",
    "湖北": "湖北", "湖北省": "湖北",
    "湖南": "湖南", "湖南省": "湖南",
    "河北": "河北", "河北省": "河北",
    "浙江": "浙江", "浙江省": "浙江",
    "云南": "云南", "云南省": "云南",
    "贵州": "贵州", "贵州省": "贵州",
    "山西": "山西", "山西省": "山西",
    "陕西": "陕西", "陕西省": "陕西",
    "辽宁": "辽宁", "辽宁省": "辽宁",
    "广西": "广西", "广西壮族自治区": "广西", "广西省": "广西",
    "福建": "福建", "福建省": "福建",
    "甘肃": "甘肃", "甘肃省": "甘肃",
    "黑龙江": "黑龙江", "黑龙江省": "黑龙江",
    "内蒙古": "内蒙古", "内蒙古自治区": "内蒙古",
    "新疆": "新疆", "新疆维吾尔自治区": "新疆",
    "重庆": "重庆", "重庆市": "重庆",
    "吉林": "吉林", "吉林省": "吉林",
    "宁夏": "宁夏", "宁夏回族自治区": "宁夏",
    "青海": "青海", "青海省": "青海",
    "天津": "天津",
    "上海": "上海",
    "北京": "北京",
    "西藏": "西藏", "西藏自治区": "西藏",
    "海南": "海南", "海南省": "海南",
    # 含地市名的变体（映射到省份）
    "苏州": "江苏",
    "南京": "江苏",
    "无锡": "江苏",
    "徐州": "江苏",
    "常州": "江苏",
    "南通": "江苏",
    "淮安": "江苏",
    "盐城": "江苏",
    "扬州": "江苏",
    "镇江": "江苏",
    "泰州": "江苏",
    "宿迁": "江苏",
    "连云港": "江苏",
    "杭州": "浙江",
    "宁波": "浙江",
    "温州": "浙江",
    "嘉兴": "浙江",
    "绍兴": "浙江",
    "金华": "浙江",
    "台州": "浙江",
    "湖州": "浙江",
    "衢州": "浙江",
    "丽水": "浙江",
    "济南": "山东",
    "青岛": "山东",
    "淄博": "山东",
    "烟台": "山东",
    "潍坊": "山东",
    "济宁": "山东",
    "临沂": "山东",
    "威海": "山东",
    "日照": "山东",
    "东营": "山东",
    "滨州": "山东",
    "垦利": "山东",
    "滕州": "山东",
    "邹城": "山东",
    "曲阜": "山东",
    "合肥": "安徽",
    "芜湖": "安徽",
    "蚌埠": "安徽",
    "阜阳": "安徽",
    "淮南": "安徽",
    "马鞍山": "安徽",
    "铜陵": "安徽",
    "池州": "安徽",
    "安庆": "安徽",
    "宣城": "安徽",
    "滁州": "安徽",
    "宿州": "安徽",
    "亳州": "安徽",
    "六安": "安徽",
    "常熟": "江苏",
    "昆山": "江苏",
    "张家港": "江苏",
    "太仓": "江苏",
    "江阴": "江苏",
    "宜兴": "江苏",
    "溧阳": "江苏",
    "如皋": "江苏",
    "启东": "江苏",
    "海安": "江苏",
    "沭阳": "江苏",
    "灌南": "江苏",
    "长兴": "浙江",
    "德清": "浙江",
    "安吉": "浙江",
    "海宁": "浙江",
    "桐乡": "浙江",
    "慈溪": "浙江",
    "余姚": "浙江",
    "乐清": "浙江",
    "瑞安": "浙江",
    "福清": "福建",
    "晋江": "福建",
    "南安": "福建",
    "揭阳": "广东",
    "潮州": "广东",
    "汕头": "广东",
    "汕尾": "广东",
    "梅州": "广东",
    "河源": "广东",
    "韶关": "广东",
    "清远": "广东",
    "肇庆": "广东",
    "广州": "广东",
    "佛山": "广东",
    "中山": "广东",
    "东莞": "广东",
    "珠海": "广东",
    "江门": "广东",
    "阳江": "广东",
    "茂名": "广东",
    "湛江": "广东",
    "惠州": "广东",
    "茂名": "广东",
    "云浮": "广东",
    "稷山": "山西",
    "闻喜": "山西",
    "夏县": "山西",
    "绛县": "山西",
    "永济": "山西",
    "河津": "山西",
    "万荣": "山西",
    "新绛": "山西",
    "临猗": "山西",
    "垣曲": "山西",
    "平陆": "山西",
    "芮城": "山西",
# ========== 补齐：所有缺失的省份地级市 ==========
    # 河南
    "郑州": "河南", "开封": "河南", "洛阳": "河南", "平顶山": "河南",
    "安阳": "河南", "鹤壁": "河南", "焦作": "河南", "濮阳": "河南",
    "许昌": "河南", "漯河": "河南", "三门峡": "河南", "南阳": "河南",
    "商丘": "河南", "信阳": "河南", "周口": "河南", "驻马店": "河南",
    "新乡": "河南", "济源": "河南", "孟津": "河南",
    # 河北
    "石家庄": "河北", "唐山": "河北", "秦皇岛": "河北", "邯郸": "河北",
    "邢台": "河北", "保定": "河北", "张家口": "河北", "承德": "河北",
    "沧州": "河北", "廊坊": "河北", "衡水": "河北", "南大港": "河北",
    "南浦": "河北", "温水": "河北",
    # 湖北
    "武汉": "湖北", "黄石": "湖北", "十堰": "湖北", "宜昌": "湖北",
    "襄阳": "湖北", "鄂州": "湖北", "荆门": "湖北", "孝感": "湖北",
    "荆州": "湖北", "黄冈": "湖北", "咸宁": "湖北", "随州": "湖北",
    "恩施": "湖北", "仙桃": "湖北", "潜江": "湖北", "天门": "湖北",
    "神农架": "湖北", "宜都": "湖北",
    # 湖南
    "长沙": "湖南", "株洲": "湖南", "衡阳": "湖南", "邵阳": "湖南",
    "岳阳": "湖南", "常德": "湖南", "张家界": "湖南", "益阳": "湖南",
    "郴州": "湖南", "永州": "湖南", "怀化": "湖南", "娄底": "湖南",
    "湘西": "湖南", "湘潭": "湖南", "岳麓": "湖南",
    # 江西
    "南昌": "江西", "景德镇": "江西", "萍乡": "江西", "九江": "江西",
    "新余": "江西", "鹰潭": "江西", "赣州": "江西", "吉安": "江西",
    "宜春": "江西", "抚州": "江西", "上饶": "江西", "上高": "江西",
    "万载": "江西",
    "修水": "江西",
    # 四川
    "成都": "四川", "自贡": "四川", "攀枝花": "四川", "泸州": "四川",
    "德阳": "四川", "绵阳": "四川", "广元": "四川", "遂宁": "四川",
    "内江": "四川", "乐山": "四川", "南充": "四川", "眉山": "四川",
    "宜宾": "四川", "广安": "四川", "达州": "四川", "雅安": "四川",
    "巴中": "四川", "资阳": "四川", "西昌": "四川", "阿坝": "四川",
    "甘孜": "四川", "凉山": "四川", "江油": "四川",
    "jiangyou": "四川", "射洪": "四川",
    "宝兴": "四川",
    # 云南
    "昆明": "云南", "曲靖": "云南", "玉溪": "云南", "保山": "云南",
    "昭通": "云南", "丽江": "云南", "普洱": "云南", "临沧": "云南",
    "楚雄": "云南", "红河": "云南", "文山": "云南", "西双版纳": "云南",
    "大理": "云南", "德宏": "云南", "怒江": "云南", "迪庆": "云南",
    "沧源": "云南",
    # 贵州
    "贵阳": "贵州", "六盘水": "贵州", "遵义": "贵州", "安顺": "贵州",
    "毕节": "贵州", "铜仁": "贵州", "黔西南": "贵州", "黔东南": "贵州",
    "黔南": "贵州", "凯里": "贵州", "都匀": "贵州", "思南": "贵州",
    "大龙": "贵州",
    # 陕西
    "西安": "陕西", "铜川": "陕西", "宝鸡": "陕西", "咸阳": "陕西",
    "渭南": "陕西", "延安": "陕西", "汉中": "陕西", "榆林": "陕西",
    "安康": "陕西", "商洛": "陕西",
    # 辽宁
    "沈阳": "辽宁", "大连": "辽宁", "鞍山": "辽宁", "抚顺": "辽宁",
    "本溪": "辽宁", "丹东": "辽宁", "锦州": "辽宁", "营口": "辽宁",
    "阜新": "辽宁", "辽阳": "辽宁", "盘锦": "辽宁", "铁岭": "辽宁",
    "朝阳": "辽宁", "葫芦岛": "辽宁", "沈抚": "辽宁", "辽滨": "辽宁",
    # 黑龙江
    "哈尔滨": "黑龙江", "齐齐哈尔": "黑龙江", "鸡西": "黑龙江",
    "鹤岗": "黑龙江", "双鸭山": "黑龙江", "大庆": "黑龙江",
    "伊春": "黑龙江", "佳木斯": "黑龙江", "七台河": "黑龙江",
    "牡丹江": "黑龙江", "黑河": "黑龙江", "绥化": "黑龙江",
    "大兴安岭": "黑龙江",
    # 广西
    "南宁": "广西", "柳州": "广西", "桂林": "广西", "梧州": "广西",
    "北海": "广西", "防城港": "广西", "钦州": "广西", "贵港": "广西",
    "玉林": "广西", "百色": "广西", "贺州": "广西", "河池": "广西",
    "来宾": "广西", "崇左": "广西",
    # 福建
    "福州": "福建", "厦门": "福建", "莆田": "福建", "三明": "福建",
    "泉州": "福建", "漳州": "福建", "龙岩": "福建", "宁德": "福建",
    "仙游": "福建",
    "舟山": "浙江",
    # 山西（补充）
    "太原": "山西", "大同": "山西", "阳泉": "山西", "长治": "山西",
    "晋城": "山西", "朔州": "山西", "晋中": "山西", "忻州": "山西",
    "临汾": "山西", "吕梁": "山西", "陵川": "山西", "平定": "山西",
    "清徐": "山西",
    "孝义": "山西",
    "风陵渡": "山西",
    # 甘肃
    "兰州": "甘肃", "嘉峪关": "甘肃", "金昌": "甘肃", "白银": "甘肃",
    "天水": "甘肃", "武威": "甘肃", "张掖": "甘肃", "平凉": "甘肃",
    "酒泉": "甘肃", "庆阳": "甘肃", "定西": "甘肃", "陇南": "甘肃",
    "临夏": "甘肃", "甘南": "甘肃",
    # 内蒙古
    "呼和浩特": "内蒙古", "包头": "内蒙古", "乌海": "内蒙古",
    "赤峰": "内蒙古", "通辽": "内蒙古", "鄂尔多斯": "内蒙古",
    "呼伦贝尔": "内蒙古", "巴彦淖尔": "内蒙古", "乌兰察布": "内蒙古",
    "兴安盟": "内蒙古", "锡林郭勒": "内蒙古", "阿拉善": "内蒙古",
    "察右": "内蒙古", "科左": "内蒙古", "科左中旗": "内蒙古",
    # 吉林
    "长春": "吉林", "吉林市": "吉林", "四平": "吉林", "辽源": "吉林",
    "通化": "吉林", "白山": "吉林", "松原": "吉林", "白城": "吉林",
    "延边": "吉林",
    # 新疆
    "乌鲁木齐": "新疆", "克拉玛依": "新疆", "吐鲁番": "新疆",
    "哈密": "新疆", "昌吉": "新疆", "博尔塔拉": "新疆",
    "巴音郭楞": "新疆", "阿克苏": "新疆", "克孜勒苏": "新疆",
    "喀什": "新疆", "和田": "新疆", "伊犁": "新疆", "塔城": "新疆",
    "阿勒泰": "新疆", "兵团": "新疆",
    # 宁夏
    "银川": "宁夏", "石嘴山": "宁夏", "吴忠": "宁夏", "固原": "宁夏",
    "中卫": "宁夏", "苏银": "宁夏",
    # 青海
    "西宁": "青海", "海东": "青海", "海北": "青海", "黄南": "青海",
    "海南州": "青海", "果洛": "青海", "玉树": "青海", "海西": "青海",
    # 海南
    "海口": "海南", "三亚": "海南", "儋州": "海南",
    # 西藏
    "拉萨": "西藏", "日喀则": "西藏", "昌都": "西藏",
    "林芝": "西藏", "山南": "西藏", "那曲": "西藏", "阿里": "西藏",
    # 区县级（剩余条目中包含的）
    "如东": "江苏", "徐圩": "江苏", "宿城": "江苏",
    "怀宁": "安徽", "皖江": "安徽",
    "寿光": "山东", "莱阳": "山东",
    "金乡": "山东", "东营港": "山东",
    "万州": "重庆", "涪陵": "重庆",
}

# 企业关键词（名称中包含这些词的归为"企业"）
# 注意：会被 province 检测覆盖，所以企业名带地名的优先归省份
ENTERPRISE_KEYWORDS = [
    "公司", "集团", "石化", "制药", "药业", "药厂", "医药",
    "能源", "钢铁", "矿业", "煤业", "焦化",
    "环保科技", "环保技术", "环境科技", "环保服务", "环保公司",
    "机械", "制造", "食品", "纺织", "建材", "水泥",
    "新材料", "新能源", "电子", "科技公司",
]

# 企业排除模式（包含这些关键词但不是企业）
ENTERPRISE_EXCLUDE_PATTERNS = [
    "化工产业园", "化工园区", "化工区", "化工产业园区",
    "工业园", "经开区", "开发区", "高新区",
    "产业园", "产业区", "集聚区",
    "石化工业区", "石化产业园区", "石化园区", "石化区",
    "化工集中区",
]

# 域名到中文名的映射
DOMAIN_TO_NAME = {
    "www.xam.gov.cn": "兴安盟",
    "www.fjql.gov.cn": "福建清流",
    "www.cnhu.com": "中国化工",
    "www.euclidchemical.com": "优凯奇化学",
}

# 特殊名称 → 分组映射（必须精确匹配这些名称时不按规则推断）
SPECIAL_GROUP_OVERRIDE = {
    "大西北网-新疆/宁夏/青海/陕西/甘肃": "全国",
}


def load_config():
    with open(CONFIG_PATH) as f:
        cfg = json.load(f)
    return cfg if isinstance(cfg, list) else cfg.get('crawlers', cfg)


def save_config(config, path=None):
    path = path or CONFIG_PATH
    os.makedirs(os.path.dirname(path), exist_ok=True)
    with open(path, 'w') as f:
        json.dump(config, f, ensure_ascii=False, indent=2)


def backup_config():
    """备份配置文件"""
    os.makedirs(BACKUP_DIR, exist_ok=True)
    ts = datetime.now().strftime("%Y%m%d_%H%M%S")
    backup_path = os.path.join(BACKUP_DIR, f"daily_crawl_config_{ts}.json")
    config = load_config()
    save_config(config, backup_path)
    print(f"✅ 已备份到: {backup_path}")
    return backup_path


def fix_name(name, script=None):
    """修复名称列"""
    if not name:
        # 从脚本名生成默认名称
        if script:
            gen = generate_name_from_script(script)
            if gen:
                return gen
        return name
    original = name
    
    # 1. 移除 " [orphan]" 后缀
    name = re.sub(r'\s*\[orphan\]\s*$', '', name)
    
    # 2. 域名 → 中文名
    for domain, cn_name in DOMAIN_TO_NAME.items():
        if name.startswith(domain):
            name = cn_name + name[len(domain):]
    
    # 3. 清理 www. 开头的域名模式
    m = re.match(r'^(www\.)?([a-z]+)\.gov\.cn[-—](.+)$', name)
    if m:
        # 尝试用域名前缀推断站点名
        pass
    
    # 4. 清理纯英文/拼音缩写名（如 "yayc", "shouguang"）
    # 这些没有中文，保留原样
    
    result = name if name.strip() else original
    return result


def generate_name_from_script(script_name):
    """从脚本名生成默认名称"""
    if not script_name:
        return ""
    base = script_name.replace('.py', '')
    if base.startswith('crawl_'):
        base = base[6:]
    # Convert snake_case to Chinese-readable form
    # Just use the cleaned script name as fallback
    if base:
        return base
    return ""


def detect_province(name):
    """从名称推断省份"""
    if not name:
        return None
    
    # 按长度降序匹配（最长词优先）
    for prov_key in sorted(PROVINCE_MAP.keys(), key=len, reverse=True):
        if prov_key in name:
            return PROVINCE_MAP[prov_key]
    
    return None


def is_enterprise(name):
    """判断是否为企业的名称"""
    if not name:
        return False
    
    # 排除模式检查
    for pat in ENTERPRISE_EXCLUDE_PATTERNS:
        if pat in name:
            return False
    
    name_lower = name.lower()
    for kw in ENTERPRISE_KEYWORDS:
        if kw in name:
            return True
    # 特定企业域名模式
    if re.search(r'\.(com|cn|net|org)(/|$|-|—)', name):
        # 检查不是知名政府域名
        if not re.search(r'\.gov\.cn', name):
            return True
    return False


def auto_classify_group(name, current_group):
    """根据名称自动推断分组"""
    
    # 0. 特殊名称覆盖
    if name in SPECIAL_GROUP_OVERRIDE:
        return SPECIAL_GROUP_OVERRIDE[name]
    
    # 1. 尝试匹配省份
    province = detect_province(name)
    if province:
        return province
    
    # 2. 企业检测
    if is_enterprise(name):
        return "企业"
    
    # 3. 根据当前分组映射
    group_map = {
        "环保公示": "环评",
        "环评": "环评",
        "环评公示": "环评",
        "环评平台": "环评",
        "环评平台组": "环评",
        "环评审批": "环评",
        "环评单位": "环评",
        "公示公告": "政府公告",
        "通知公告": "政府公告",
        "政府公告": "政府公告",
        "政府通知公告": "政府公告",
        "县政府通知公告": "政府公告",
        "公告/通知公告": "政府公告",
        "政府文件": "政府公告",
        "政策文件": "政府公告",
        "县区": "区县",
        "区县政府": "区县",
        "区县政务公开": "区县",
        "县区级政府": "区县",
        "开发区": "区县",
        "高新区": "区县",
        "政府网站": "区县",
        "乡镇": "区县",
        "乡镇公告公示": "区县",
        "乡镇公示": "区县",
        "乡镇动态": "区县",
        "生态环境": "生态环境",
        "生态环境局": "生态环境",
        "生态环境厅": "生态环境",
        "区县生态环境": "生态环境",
        "区县生态环境分局": "生态环境",
        "省级生态环境厅": "生态环境",
        "行政审批": "行政审批",
        "全国": "全国",
    "西北": "西北",
        "规划公示": "规划公示",
        "企业环保": "企业",
        "企业环评": "企业",
        "环评协会": "环评",
        "新闻门户": "新闻资讯",
        "新闻媒体": "新闻资讯",
        "新闻资讯": "新闻资讯",
        "其他": "其他",
        "部门工作": "其他",
    }
    
    if current_group in group_map:
        return group_map[current_group]
    
    # 4. 特殊处理
    for old, new in [
        ("新疆", "新疆"), ("新疆兵团", "新疆"), ("新疆-阿克苏", "新疆"),
        ("山东", "山东"), ("山东烟台", "山东"), ("山东滨州", "山东"),
        ("河南省", "河南"), ("安徽省", "安徽"), ("湖南省", "湖南"),
        ("四川省", "四川"), ("湖北省", "湖北"),
        ("贵州省", "贵州"), ("浙江省", "浙江"),
        ("山西省", "山西"), ("山东省", "山东"),
        ("广东组", "广东"),
        ("青海-海东", "青海"),
        ("湖北-荆门", "湖北"),
        ("企业-宁夏", "宁夏"),
        ("安徽颍上环保", "安徽"),
        ("qiye-jiangsu", "江苏"),
        ("ningxia", "宁夏"),
        ("新星经开区行政执法", "区县"),
        ("阜新细河便民通知", "区县"),
        ("山西环评平台", "山西"),
        ("新疆阿克苏", "新疆"),
        ("池州市贵池区", "安徽"),
        ("宁波", "浙江"),
    ]:
        if current_group == old:
            return new
    
    # 5. 保留原分组
    return current_group


def analyze(config):
    """分析配置"""
    changes = []
    for i, item in enumerate(config):
        old_name = item.get("name", "") or ""
        old_group = item.get("group", "") or ""
        
        new_name = fix_name(old_name, item.get("script", ""))
        new_group = auto_classify_group(new_name or old_name, old_group)
        
        if new_name != old_name or new_group != old_group:
            changes.append((i, old_name, new_name, old_group, new_group))
    
    return changes


def preview(changes, limit=None):
    """预览修改"""
    if limit:
        changes = changes[:limit]
    
    print(f"共检测到 {len(changes)} 条需要修改\n")
    
    # 按分组统计
    new_groups = Counter()
    for _, _, _, _, new_g in changes:
        new_groups[new_g] += 1
    
    print("=== 分组变更概况 ===")
    for g, cnt in sorted(new_groups.items(), key=lambda x: -x[1]):
        old_groups = set()
        for _, _, _, og, _ in changes:
            if _ == -1:
                continue
        print(f"  → {g}: {cnt}条")
    
    print(f"\n{'='*100}")
    print(f"{'序号':>4} │ {'旧名称':<35} │ {'新名称':<35} │ {'旧分组':<15} → {'新分组':<15}")
    print(f"{'='*100}")
    
    for idx, (i, old_n, new_n, old_g, new_g) in enumerate(changes):
        if limit and idx >= limit:
            break
        old_n_disp = (old_n[:34] + "…") if len(old_n) > 35 else old_n
        new_n_disp = (new_n[:34] + "…") if len(new_n) > 35 else new_n
        changed_n = "✎" if old_n != new_n else " "
        changed_g = "✎" if old_g != new_g else " "
        print(f"{i:>4} │ {old_n_disp:<35} │ {new_n_disp:<35} │ {old_g:<15} → {new_g:<15}")


def apply(config):
    """应用修改"""
    changes = 0
    name_changes = 0
    group_changes = 0
    
    for item in config:
        old_name = item.get("name", "") or ""
        old_group = item.get("group", "") or ""
        
        new_name = fix_name(old_name, item.get("script", ""))
        new_group = auto_classify_group(new_name or old_name, old_group)
        
        if new_name != old_name:
            item["name"] = new_name
            name_changes += 1
        
        if new_group != old_group:
            item["group"] = new_group
            group_changes += 1
    
    if name_changes or group_changes:
        save_config(config)
        print(f"✅ 已保存，名称修改 {name_changes} 条，分组修改 {group_changes} 条")
    else:
        print("✅ 无需修改")
    
    return name_changes, group_changes


def main():
    if len(sys.argv) < 2:
        print(__doc__)
        return
    
    cmd = sys.argv[1]
    limit = None
    if cmd == '--preview' and len(sys.argv) > 2:
        try:
            limit = int(sys.argv[2])
        except ValueError:
            pass
    
    config = load_config()
    
    if cmd == '--backup':
        backup_config()
        return
    
    if cmd == '--preview':
        backup_config()  # 预览前也备份保险
        changes = analyze(config)
        preview(changes, limit)
        return
    
    if cmd == '--apply':
        backup_path = backup_config()
        changes = analyze(config)
        print(f"即将修改 {len(changes)} 条")
        if changes:
            preview(changes, min(limit or 20, 20))
            confirm = input("确认应用以上修改? (yes/no): ")
            if confirm.strip().lower() in ('yes', 'y', '是'):
                nc, gc = apply(config)
                print(f"✅ 完成！名称修改 {nc} 条，分组修改 {gc} 条")
            else:
                print("❌ 已取消")
        else:
            print("✅ 无需修改")
        return
    
    print(f"未知命令: {cmd}")
    print(__doc__)


if __name__ == '__main__':
    main()
