<?php

namespace Kylin\WordFilter\Console;

use Illuminate\Console\Command;
use Illuminate\Support\Facades\DB;
use Kylin\WordFilter\Enums\RiskLevel;
use Kylin\WordFilter\Enums\Severity;
use Kylin\WordFilter\Enums\WordStatus;
use Kylin\WordFilter\Models\SensitiveWord;
use Kylin\WordFilter\Models\WordCategory;
use Kylin\WordFilter\Services\WordSyncService;

/**
 * 敏感词导入命令
 *
 * 整合多个开源词库，按31个分类精准导入。
 * 数据源：
 *   1. 内置基础词库（每类25-30词）
 *   2. Sensitive-lexicon 词库（konsheng, MIT, 14万+词）
 *   3. sensitive-word 标签词库（houbb, Apache-2.0, 6.4万词+4.3万标签）
 *
 * 用法：
 *   php artisan kylin:import-sensitive-words              # 导入内置+外部词库，自动发布版本
 *   php artisan kylin:import-sensitive-words --force      # 强制覆盖已存在的敏感词
 *   php artisan kylin:import-sensitive-words --no-publish # 导入但不发布版本
 */
class ImportSensitiveWordsCommand extends Command
{
    protected $signature = 'kylin:import-sensitive-words
                            {--force : 强制覆盖已存在的敏感词}
                            {--no-publish : 导入完成后不发布新版本（默认自动发布）}';

    protected $description = '导入完整敏感词库（31分类，整合多源词库），自动发布版本';

    /**
     * houbb sensitive-word 标签到系统分类的映射
     *
     * 标签编号：0=政治相关 1=色情相关 2=暴力/辱骂 3=赌博 4=毒品/枪支/其他违法
     * 格式：[标签编号 => [[分类code, 严重程度, 最大数量], ...]]
     */
    private const TAG_MAP = [
        '0' => [ // 政治
            ['political', 'severe', 0],
            ['national_security', 'severe', 500],
            ['espionage', 'severe', 300],
            ['separatism', 'severe', 300],
            ['riot_instigation', 'severe', 300],
        ],
        '1' => [ // 色情
            ['pornography', 'severe', 0],
            ['vulgarity', 'moderate', 200],
        ],
        '2' => [ // 暴力/辱骂
            ['violence', 'severe', 0],
            ['cyberbullying', 'high', 300],
            ['ethnic_discrimination', 'severe', 200],
            ['religious_extremism', 'severe', 200],
            ['cult', 'severe', 200],
            ['terrorist_organization', 'high', 200],
            ['human_trafficking', 'severe', 300],
        ],
        '3' => [ // 赌博
            ['gambling', 'high', 0],
            ['pyramid_scheme', 'moderate', 100],
            ['illegal_fundraising', 'moderate', 100],
        ],
        '4' => [ // 毒品/枪支/其他违法
            ['drug', 'severe', 0],
            ['firearms', 'severe', 0],
            ['money_laundering', 'high', 200],
            ['prohibited_drugs', 'moderate', 200],
            ['contraband', 'moderate', 200],
            ['surrogacy', 'moderate', 100],
            ['organ_trading', 'moderate', 100],
            ['rumor', 'high', 200],
            ['false_advertising', 'moderate', 200],
            ['harmful_inducement', 'moderate', 200],
            ['ip_infringement', 'moderate', 100],
            ['privacy_leak', 'moderate', 100],
            ['clickbait', 'moderate', 100],
            ['discrimination', 'moderate', 100],
        ],
    ];

    /**
     * Sensitive-lexicon 词库文件到分类的补充映射
     *
     * 格式：[文件名, 分类code, 严重程度, 最大导入数量(0=不限)]
     */
    private const VOCABULARY_MAP = [
        ['政治类型.txt', 'political', 'severe', 0],
        ['反动词库.txt', 'political', 'severe', 0],
        ['新思想启蒙.txt', 'political', 'severe', 0],
        ['暴恐词库.txt', 'violence', 'severe', 0],
        ['色情词库.txt', 'pornography', 'severe', 0],
        ['色情类型.txt', 'pornography', 'severe', 0],
        ['涉枪涉爆.txt', 'firearms', 'severe', 0],
        ['COVID-19词库.txt', 'rumor', 'high', 0],
        ['贪腐词库.txt', 'money_laundering', 'high', 0],
        ['广告类型.txt', 'false_advertising', 'moderate', 0],
        ['民生词库.txt', 'prohibited_drugs', 'moderate', 0],
    ];

    /**
     * 内置的敏感词分类及词库数据（基础词库，每类25-30词）
     */
    private const WORD_DATA = [
        'high' => [
            'political' => ['政治敏感', '涉及政治敏感内容的词汇', 'severe', [
                '颠覆政权', '分裂国家', '推翻政府', '反党反社', '政治颠覆',
                '国家机密', '泄露机密', '间谍活动', '颠覆社会主义', '反革命',
                '邪教组织', '恐怖组织', '极端主义', '分裂主义',
                '煽动颠覆', '危害国家安全', '敌对势力', '反动言论', '政治敏感',
                '颠覆国家', '窃取机密', '叛国', '通敌', '策反',
                '反华势力', '境外渗透', '政治颠覆活动', '煽动分裂', '非法组党',
            ]],
            'violence' => ['暴力恐怖', '涉及暴力恐怖内容的词汇', 'severe', [
                '杀人放火', '恐怖袭击', '爆炸袭击', '血腥暴力', '砍人事件',
                '自杀方法', '教唆犯罪', '暴力恐怖', '残忍杀害', '人体炸弹',
                '制造炸弹', '枪支买卖', '毒品交易', '黑社会', '打砸抢烧',
                '暴力催收', '校园暴力', '家庭暴力', '恐怖炸弹', '杀人技巧',
                '斩首', '绑架', '劫持人质', '武装袭击', '自杀式袭击',
                '炸弹制作', '爆炸物', '杀伤性武器', '血腥屠杀', '暴力行凶',
            ]],
            'pornography' => ['色情低俗', '涉及色情低俗内容的词汇', 'severe', [
                '色情视频', '裸体照片', '成人电影', '淫秽内容', '色情直播',
                '卖淫嫖娼', '色情服务', '黄色网站', '情色小说', '成人用品',
                '一夜情', '约炮', '色情交易', '裸聊', '涉黄',
                '色情广告', '成人直播', '黄色视频', '淫秽物品', '色情图片',
                '招嫖', '卖淫', '嫖娼', '色情表演', '脱衣舞',
                '淫秽视频', '色情推广', '成人内容', '黄色直播', '淫秽直播',
            ]],
            'cyberbullying' => ['网络欺凌', '涉及网络欺凌的词汇', 'high', [
                '网络暴力', '人肉搜索', '恶意造谣', '网络围攻', '人身攻击',
                '恶意曝光', '隐私侵犯', '网络恐吓', '集体辱骂', '恶意举报',
                '网络霸凌', '恶意P图', '侮辱诽谤', '恶意传播', '网络骚扰',
                '威胁恐吓', '恶意评论', '网络谩骂', '群体攻击', '恶意中伤',
                '网络欺凌', '恶意跟风', '网络暴力攻击', '人身威胁', '恶意散布',
            ]],
            'drug' => ['毒品违法', '涉及毒品违法的词汇', 'severe', [
                '毒品交易', '贩卖毒品', '吸食毒品', '制毒', '贩毒',
                '冰毒', '海洛因', '大麻', '可卡因', '摇头丸',
                'K粉', '麻古', '鸦片', '吗啡', '杜冷丁',
                '安非他命', '甲基苯丙胺', '致幻剂', '迷幻药', '毒品制作',
                '吸毒工具', '注射毒品', '毒品走私', '毒品运输', '毒品销售',
                '罂粟种植', '大麻种植', '制毒原料', '毒品加工', '毒品分销',
            ]],
            'rumor' => ['谣言虚假', '涉及谣言虚假信息的词汇', 'high', [
                '虚假信息', '造谣传谣', '散布谣言', '不实消息', '虚假新闻',
                '编造事实', '恶意造谣', '谣言传播', '虚假声明', '伪造证据',
                '虚假宣传', '捏造事实', '歪曲真相', '虚假报道', '误导信息',
                '谣言制造', '虚假公告', '不实传言', '编造谣言', '传播谣言',
                '虚假疫情', '虚假灾情', '虚假警情', '造谣生事', '谣言散布',
            ]],
            'religious_extremism' => ['宗教极端', '涉及宗教极端的词汇', 'severe', [
                '宗教极端', '极端宗教', '圣战', '宗教狂热', '宗教迫害',
                '异教徒', '宗教仇恨', '极端教义', '宗教洗脑', '邪教宣传',
                '宗教暴力', '极端信仰', '宗教歧视', '宗教煽动', '极端主义',
                '宗教恐怖', '原教旨主义', '宗教排他', '宗教对抗', '极端传教',
                '宗教武装', '宗教冲突煽动', '宗教仇恨宣传', '极端宗教思想', '宗教极端组织',
            ]],
            'ethnic_discrimination' => ['民族歧视', '涉及民族歧视的词汇', 'severe', [
                '民族歧视', '种族歧视', '民族仇恨', '种族仇恨', '民族排斥',
                '种族侮辱', '民族侮辱', '种族优越', '民族优越', '种族隔离',
                '民族压迫', '种族压迫', '民族贬低', '种族贬低', '民族攻击',
                '种族攻击', '民族侮辱性称呼', '种族侮辱性称呼', '民族对立', '种族对立',
                '民族排斥言论', '种族歧视言论', '民族仇恨宣传', '种族仇恨煽动', '民族歧视行为',
            ]],
            'separatism' => ['分裂主义', '涉及分裂主义的词汇', 'severe', [
                '分裂主义', '分裂国家', '领土分裂', '民族分裂', '地区分裂',
                '分裂活动', '分裂势力', '分裂组织', '分裂宣传', '分裂煽动',
                '独立建国', '分裂国土', '割据', '分裂运动', '分裂主张',
                '领土分离', '分裂叛乱', '分裂武装', '分裂渗透', '分裂颠覆',
                '分裂策划', '分裂煽动宣传', '分裂国家活动', '分裂势力渗透', '分裂主义宣传',
            ]],
            'terrorist_organization' => ['恐怖组织', '涉及恐怖组织的词汇', 'severe', [
                '恐怖组织', '基地组织', '塔利班', '伊斯兰国', 'ISIS',
                '博科圣地', '真主党', '哈马斯', '东突', '东伊运',
                '恐怖训练营', '恐怖分子', '恐怖网络', '恐怖融资', '恐怖招募',
                '恐怖策划', '恐怖联络', '恐怖后勤', '恐怖宣传', '恐怖洗钱',
                '恐怖组织成员', '恐怖组织分支', '恐怖组织联络', '恐怖组织资金', '恐怖组织策划',
            ]],
            'cult' => ['邪教', '涉及邪教的词汇', 'severe', [
                '邪教', '法轮功', '全能神', '门徒会', '呼喊派',
                '统一教', '科学教', '邪教组织', '邪教宣传', '邪教洗脑',
                '邪教控制', '邪教诱骗', '邪教敛财', '邪教危害', '邪教活动',
                '邪教传播', '邪教聚会', '邪教书籍', '邪教视频', '邪教音频',
                '邪教诱骗手段', '邪教精神控制', '邪教非法活动', '邪教危害社会', '邪教危害家庭',
            ]],
            'firearms' => ['枪支弹药', '涉及枪支弹药的词汇', 'severe', [
                '枪支买卖', '枪支弹药', '非法持枪', '枪支制造', '枪支走私',
                '手枪', '步枪', '冲锋枪', '狙击枪', '霰弹枪',
                '弹药', '子弹', '炸药', '雷管', '导火索',
                '枪支改装', '枪支零件', '弹药制造', '弹药走私', '枪支运输',
                '非法枪支', '黑市枪支', '枪支交易', '弹药交易', '枪支黑市',
            ]],
            'riot_instigation' => ['暴乱煽动', '涉及暴乱煽动的词汇', 'severe', [
                '暴乱煽动', '煽动暴乱', '煽动闹事', '聚众闹事', '煽动骚乱',
                '暴乱策划', '暴乱组织', '暴乱指挥', '暴乱宣传', '暴乱煽动言论',
                '煽动冲击', '煽动打砸', '煽动抢烧', '煽动对抗', '煽动暴力',
                '暴乱联络', '暴乱物资', '暴乱资金', '暴乱招募', '暴乱指挥链',
                '煽动非法集会', '煽动非法游行', '煽动冲击机关', '煽动破坏秩序', '煽动对抗执法',
            ]],
            'national_security' => ['危害国家安全', '涉及危害国家安全的词汇', 'severe', [
                '危害国家安全', '颠覆国家政权', '分裂国家', '煽动颠覆', '窃取国家秘密',
                '间谍行为', '叛逃', '资敌', '通敌叛国', '泄露国家秘密',
                '破坏国家统一', '危害国家统一', '颠覆制度', '破坏社会稳定', '危害社会稳定',
                '窃取情报', '刺探情报', '非法获取国家秘密', '非法持有国家秘密', '泄露国家情报',
                '危害国家主权', '破坏国家领土', '危害国家利益', '破坏国防', '危害国防安全',
            ]],
            'espionage' => ['间谍活动', '涉及间谍活动的词汇', 'severe', [
                '间谍活动', '间谍组织', '间谍人员', '情报窃取', '情报收集',
                '窃密', '刺探', '侦察', '窃听', '监听',
                '情报传递', '情报交易', '情报网络', '间谍器材', '窃密设备',
                '间谍培训', '间谍招募', '间谍联络', '间谍资金', '间谍掩护',
                '间谍渗透', '情报刺探', '窃取机密', '非法侦察', '间谍器材制造',
            ]],
            'money_laundering' => ['洗钱', '涉及洗钱的词汇', 'severe', [
                '洗钱', '资金洗白', '非法资金转移', '地下钱庄', '资金清洗',
                '洗钱渠道', '洗钱网络', '洗钱操作', '洗钱账户', '洗钱交易',
                '虚假交易', '虚假转账', '资金掩护', '资金伪装', '资金隐匿',
                '跨境洗钱', '虚拟货币洗钱', '数字货币洗钱', '地下汇款', '非法汇兑',
                '洗钱团伙', '洗钱组织', '洗钱通道', '资金回流', '资金分散转移',
            ]],
            'human_trafficking' => ['人口贩卖', '涉及人口贩卖的词汇', 'severe', [
                '人口贩卖', '拐卖妇女', '拐卖儿童', '人口走私', '人口交易',
                '贩卖人口', '拐骗', '绑架贩卖', '强迫劳动', '强迫卖淫',
                '人口贩运', '人口拐卖', '拐卖', '收买被拐卖人口', '组织偷渡',
                '非法移民', '人口走私组织', '人口贩卖网络', '人口贩卖渠道', '人口贩卖资金',
                '拐卖组织', '贩卖组织', '强迫婚姻', '强迫乞讨', '人口贩卖团伙',
            ]],
        ],
        'medium' => [
            'vulgarity' => ['低俗媚俗', '涉及低俗媚俗内容的词汇', 'moderate', [
                '低俗内容', '媚俗表演', '低俗直播', '低俗视频', '低俗图片',
                '恶俗', '庸俗', '低级趣味', '恶趣味', '低俗段子',
                '低俗笑话', '荤段子', '低俗互动', '低俗挑战', '低俗游戏',
                '擦边球', '打擦边', '低俗暗示', '低俗表演', '低俗内容创作',
                '低俗文化传播', '低俗网络内容', '低俗短视频', '低俗直播内容', '低俗网络直播',
            ]],
            'clickbait' => ['标题党', '涉及标题党的词汇', 'moderate', [
                '标题党', '震惊体', '不转不是中国人', '看完惊呆了', '速看',
                '删前速看', '必看', '震惊', '惊呆了', '不可思议',
                '惊天秘密', '万万没想到', '太可怕了', '吓死人', '出大事了',
                '紧急通知', '赶紧看', '马上删', '央视曝光', '内部消息',
                '惊天大揭秘', '瞒不住了', '终于曝光了', '真相大白', '不敢相信',
            ]],
            'discrimination' => ['歧视', '涉及歧视的词汇', 'moderate', [
                '性别歧视', '地域歧视', '年龄歧视', '学历歧视', '外貌歧视',
                '身高歧视', '残疾歧视', '职业歧视', '户籍歧视', '语言歧视',
                '性取向歧视', '婚姻歧视', '疾病歧视', '体重歧视', '出身歧视',
                '歧视性言论', '歧视性行为', '歧视性规定', '歧视性要求', '歧视性招聘',
                '就业歧视', '教育歧视', '住房歧视', '服务歧视', '社会歧视',
            ]],
            'harmful_inducement' => ['不良诱导', '涉及不良诱导的词汇', 'moderate', [
                '不良诱导', '诱导消费', '诱导充值', '诱导打赏', '诱导转账',
                '诱导投资', '诱导借贷', '诱导赌博', '诱导吸烟', '诱导饮酒',
                '诱导自残', '诱导厌食', '诱导极端减肥', '诱导危险行为', '诱导不良习惯',
                '诱导未成年人消费', '诱导未成年人充值', '诱导分享', '诱导转发', '诱导关注',
                '诱导下载', '诱导注册', '诱导点击', '诱导消费陷阱', '诱导性广告',
            ]],
            'ip_infringement' => ['侵犯知识产权', '涉及侵犯知识产权的词汇', 'moderate', [
                '盗版', '侵权', '抄袭', '仿冒', '假冒',
                '盗版软件', '盗版影视', '盗版音乐', '盗版书籍', '盗版游戏',
                '侵权作品', '侵权产品', '假冒品牌', '仿冒商品', '山寨',
                '盗版资源', '侵权下载', '非法转载', '盗版网站', '盗版链接',
                '侵权传播', '盗版分发', '盗版制作', '盗版销售', '侵权复制',
            ]],
            'privacy_leak' => ['泄露隐私', '涉及泄露隐私的词汇', 'moderate', [
                '泄露隐私', '隐私泄露', '个人信息泄露', '数据泄露', '信息泄露',
                '人肉搜索', '非法获取信息', '非法买卖信息', '信息倒卖', '数据倒卖',
                '隐私侵犯', '偷拍', '偷录', '窃听', '跟踪',
                '骚扰电话', '骚扰短信', '垃圾短信', '诈骗电话', '推销电话',
                '个人信息买卖', '通讯录泄露', '位置泄露', '照片泄露', '隐私数据交易',
            ]],
            'false_advertising' => ['虚假广告', '涉及虚假广告的词汇', 'moderate', [
                '虚假广告', '夸大宣传', '虚假宣传', '误导性广告', '欺骗性广告',
                '包治百病', '药到病除', '无效退款', '绝对有效', '百分百有效',
                '永不复发', '根治', '祖传秘方', '神奇疗效', '特效药',
                '免费赠送', '零元购', '限时免费', '永久免费', '完全免费',
                '虚假功效', '虚假承诺', '虚假评价', '刷好评', '虚假销量',
            ]],
            'pyramid_scheme' => ['传销', '涉及传销的词汇', 'moderate', [
                '传销', '传销组织', '传销活动', '网络传销', '新型传销',
                '拉人头', '发展下线', '层级返利', '静态收益', '动态收益',
                '资本运作', '连锁销售', '阳光工程', '1040工程', '纯资本运作',
                '传销模式', '传销骗局', '传销洗脑', '传销培训', '传销话术',
                '传销窝点', '传销网络', '传销头目', '传销资金', '传销受害',
            ]],
            'gambling' => ['赌博', '涉及赌博的词汇', 'high', [
                '网络赌博', '赌场网站', '博彩平台', '赌博网站', '在线赌场',
                '百家乐', '老虎机', '德州扑克', '赌球网站', '彩票预测',
                '赌博代理', '赌资洗钱', '棋牌作弊', '出千技巧', '赌场荷官',
                '博彩代理', '赌资转账', '赌博充值', '下注平台', '赌博软件',
                '赌场代理', '赌博推广', '博彩推广', '赌资兑换', '赌博赔率',
            ]],
            'prohibited_drugs' => ['违禁药品', '涉及违禁药品的词汇', 'moderate', [
                '违禁药品', '假药', '劣药', '非法药品', '未经批准药品',
                '减肥药', '壮阳药', '催情药', '迷药', '迷魂药',
                '处方药私售', '非法售药', '药品走私', '假药销售', '劣药销售',
                '非法进口药', '未经检验药品', '违规药品', '禁药', '兴奋剂',
                '瘦肉精', '三聚氰胺', '非法添加剂', '违禁保健品', '违禁中药',
            ]],
            'contraband' => ['违禁品交易', '涉及违禁品交易的词汇', 'moderate', [
                '违禁品', '违禁品交易', '非法交易', '黑市交易', '地下交易',
                '走私', '走私货物', '走私物品', '非法进出口', '偷逃关税',
                '非法经营', '黑市', '地下市场', '非法买卖', '违规交易',
                '违禁品销售', '违禁品运输', '违禁品储存', '违禁品制造', '违禁品分销',
                '走私渠道', '走私网络', '走私团伙', '走私资金', '走私工具',
            ]],
            'illegal_fundraising' => ['非法集资', '涉及非法集资的词汇', 'moderate', [
                '非法集资', '非法吸收存款', '非法融资', '集资诈骗', '庞氏骗局',
                '高息揽储', '高额回报', '保本保息', '稳赚不赔', '零风险投资',
                '投资理财骗局', '虚拟货币骗局', '数字货币骗局', '原始股骗局', '股权骗局',
                '非法募资', '非法吸储', '资金盘', '互助盘', '拆分盘',
                '非法集资宣传', '非法集资推广', '非法集资招募', '非法集资网络', '非法集资团伙',
            ]],
            'surrogacy' => ['代孕', '涉及代孕的词汇', 'moderate', [
                '代孕', '代孕妈妈', '代孕机构', '代孕中介', '代孕服务',
                '商业代孕', '代孕合同', '代孕费用', '代孕交易', '代孕广告',
                '非法代孕', '代孕产业链', '代孕黑市', '代孕招募', '代孕咨询',
                '代孕平台', '代孕网站', '代孕信息', '代孕安排', '代孕协调',
                '代孕手术', '代孕医院', '代孕法律规避', '代孕风险隐瞒', '代孕欺诈',
            ]],
            'organ_trading' => ['器官买卖', '涉及器官买卖的词汇', 'moderate', [
                '器官买卖', '器官交易', '器官贩卖', '器官移植黑市', '非法器官移植',
                '肾脏买卖', '肝脏买卖', '角膜买卖', '器官中介', '器官贩子',
                '非法摘取器官', '器官走私', '器官黑市', '器官交易网络', '器官交易渠道',
                '活体器官交易', '器官供体买卖', '器官配型交易', '器官运输', '器官储存',
                '器官交易资金', '器官交易团伙', '器官交易广告', '器官交易信息', '器官交易招募',
            ]],
        ],
    ];

    public function handle(): int
    {
        $force = $this->option('force');
        $totalCategories = 0;
        $totalWords = 0;
        $skippedWords = 0;

        // 第一步：导入内置分类和基础词库
        $this->info('第一步：导入内置分类和基础词库...');
        $this->importBuiltInData($force, $totalCategories, $totalWords, $skippedWords);

        // 第二步：从 houbb 标签词库导入（核心数据源，4.3万条带标签词）
        $this->newLine();
        $this->info('第二步：从标签词库导入（sensitive_word_tags.txt）...');
        $this->importFromTaggedVocabulary($force, $totalWords, $skippedWords);

        // 第三步：从 Sensitive-lexicon 词库补充导入
        $this->newLine();
        $this->info('第三步：从 Sensitive-lexicon 词库补充导入...');
        $this->importFromVocabulary($force, $totalWords, $skippedWords);

        $this->newLine();
        $this->info("导入完成！分类: {$totalCategories}，敏感词: {$totalWords}，跳过: {$skippedWords}");

        if ($skippedWords > 0 && !$force) {
            $this->comment('使用 --force 选项可覆盖已存在的敏感词');
        }

        // 自动发布版本
        if (!$this->option('no-publish')) {
            $this->newLine();
            $this->info('正在发布新版本...');

            $syncService = app(WordSyncService::class);
            $version = $syncService->publishVersion("导入敏感词词库: {$totalCategories} 分类, {$totalWords} 词");

            $this->info("版本发布成功: v{$version->version}");
            $this->info("  总词数: {$version->total_words}");
            $this->info("  校验和: {$version->checksum}");
            $this->info("  发布时间: {$version->published_at->format('Y-m-d H:i:s')}");

            if ($version->category_stats) {
                $this->newLine();
                $this->info('分类统计:');
                foreach ($version->category_stats as $catCode => $count) {
                    $cat = WordCategory::where('code', $catCode)->first();
                    $catName = $cat ? $cat->name : $catCode;
                    $this->line("  {$catName}: {$count} 词");
                }
            }
        } else {
            $this->comment('已跳过版本发布（使用了 --no-publish 选项）');
        }

        return self::SUCCESS;
    }

    /**
     * 导入内置分类和基础词库
     */
    private function importBuiltInData(bool $force, int &$totalCategories, int &$totalWords, int &$skippedWords): void
    {
        DB::transaction(function () use ($force, &$totalCategories, &$totalWords, &$skippedWords) {
            foreach (self::WORD_DATA as $riskLevel => $categories) {
                $riskLabel = RiskLevel::from($riskLevel)->label();

                foreach ($categories as $code => [$name, $description, $severity, $words]) {
                    $category = WordCategory::updateOrCreate(
                        ['code' => $code],
                        [
                            'name' => $name,
                            'description' => $description,
                            'risk_level' => $riskLevel,
                        ]
                    );
                    $totalCategories++;

                    $this->output->write("  [<info>{$riskLabel}</info>] {$name} ({$code}): ");

                    $categoryWordCount = 0;
                    $categorySkipped = 0;

                    foreach ($words as $word) {
                        $word = trim($word);
                        if ($word === '') {
                            continue;
                        }

                        $existing = SensitiveWord::where('word', $word)
                            ->where('category_id', $category->id)
                            ->first();

                        if ($existing && !$force) {
                            $categorySkipped++;
                            $skippedWords++;
                            continue;
                        }

                        SensitiveWord::updateOrCreate(
                            [
                                'word' => $word,
                                'category_id' => $category->id,
                            ],
                            [
                                'severity' => $severity,
                                'is_variant' => false,
                                'status' => WordStatus::Active->value,
                            ]
                        );
                        $categoryWordCount++;
                        $totalWords++;
                    }

                    $category->refreshWordCount();

                    $msg = "{$categoryWordCount} 词已导入";
                    if ($categorySkipped > 0) {
                        $msg .= "，{$categorySkipped} 词已存在跳过";
                    }
                    $this->output->writeln("<comment>{$msg}</comment>");
                }
            }
        });
    }

    /**
     * 从 houbb 标签词库导入（核心数据源）
     *
     * 格式：词语 标签编号（0=政治 1=色情 2=暴力/辱骂 3=赌博 4=毒品/枪支/其他违法）
     * 多标签用逗号分隔，如 "1,4" 表示同时属于色情和毒品分类
     */
    private function importFromTaggedVocabulary(bool $force, int &$totalWords, int &$skippedWords): void
    {
        $vocabularyDir = dirname(__DIR__, 2) . '/data/vocabulary';
        $tagsFile = $vocabularyDir . '/sensitive_word_tags.txt';

        if (!file_exists($tagsFile)) {
            $this->warn('标签词库文件不存在: sensitive_word_tags.txt，跳过');
            return;
        }

        // 读取白名单
        $allowWords = [];
        $allowFile = $vocabularyDir . '/sensitive_word_allow.txt';
        if (file_exists($allowFile)) {
            $allowLines = file($allowFile, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
            $allowWords = array_flip(array_map('trim', $allowLines));
        }

        // 解析标签文件，按分类聚合
        $categoryWords = []; // [categoryCode => [word => severity]]
        $handle = fopen($tagsFile, 'r');
        if (!$handle) {
            $this->error('无法打开标签词库文件');
            return;
        }

        $totalLines = 0;
        while (($line = fgets($handle)) !== false) {
            $line = trim($line);
            if ($line === '') {
                continue;
            }
            $totalLines++;

            // 解析 "词语 标签" 格式
            $parts = preg_split('/\s+/', $line, 2);
            if (count($parts) < 2) {
                continue;
            }

            $word = trim($parts[0]);
            $tags = trim($parts[1]);

            // 过滤条件
            if ($word === '' || mb_strlen($word) > 50) {
                continue;
            }
            if (preg_match('/^https?:\/\//i', $word)) {
                continue;
            }
            if (isset($allowWords[$word])) {
                continue;
            }

            // 解析标签，取第一个主标签
            $tagList = explode(',', $tags);
            $primaryTag = trim($tagList[0]);

            if (!isset(self::TAG_MAP[$primaryTag])) {
                continue;
            }

            // 按标签映射分配到各分类
            foreach (self::TAG_MAP[$primaryTag] as [$categoryCode, $severity, $maxCount]) {
                if (!isset($categoryWords[$categoryCode])) {
                    $categoryWords[$categoryCode] = [];
                }

                // 达到最大数量限制则跳过
                if ($maxCount > 0 && count($categoryWords[$categoryCode]) >= $maxCount) {
                    continue;
                }

                // 同分类去重
                if (!isset($categoryWords[$categoryCode][$word])) {
                    $categoryWords[$categoryCode][$word] = $severity;
                }
            }
        }
        fclose($handle);

        $this->line("  解析标签文件: {$totalLines} 行");

        // 批量写入数据库
        DB::transaction(function () use ($force, &$totalWords, &$skippedWords, $categoryWords) {
            foreach ($categoryWords as $categoryCode => $words) {
                $category = WordCategory::where('code', $categoryCode)->first();
                if (!$category) {
                    continue;
                }

                $categoryWordCount = 0;
                $categorySkipped = 0;

                // 分批处理，每批500词
                $batches = array_chunk($words, 500, true);

                foreach ($batches as $batch) {
                    // 批量查询已存在的词
                    $existingWords = SensitiveWord::where('category_id', $category->id)
                        ->whereIn('word', array_keys($batch))
                        ->pluck('word')
                        ->flip()
                        ->toArray();

                    $insertData = [];
                    foreach ($batch as $word => $severity) {
                        if (isset($existingWords[$word])) {
                            if ($force) {
                                // 强制更新
                                SensitiveWord::where('word', $word)
                                    ->where('category_id', $category->id)
                                    ->update([
                                        'severity' => $severity,
                                        'status' => WordStatus::Active->value,
                                    ]);
                                $categoryWordCount++;
                                $totalWords++;
                            } else {
                                $categorySkipped++;
                                $skippedWords++;
                            }
                        } else {
                            $insertData[] = [
                                'word' => $word,
                                'category_id' => $category->id,
                                'severity' => $severity,
                                'is_variant' => false,
                                'status' => WordStatus::Active->value,
                                'created_at' => now(),
                                'updated_at' => now(),
                            ];
                            $categoryWordCount++;
                            $totalWords++;
                        }
                    }

                    // 批量插入
                    if (!empty($insertData)) {
                        // 再分批插入，每批100条
                        foreach (array_chunk($insertData, 100) as $chunk) {
                            DB::table('kylin_sensitive_words')->insert($chunk);
                        }
                    }
                }

                $category->refreshWordCount();

                $catName = $category->name;
                $msg = "  {$catName} ({$categoryCode}): {$categoryWordCount} 词已导入";
                if ($categorySkipped > 0) {
                    $msg .= "，{$categorySkipped} 词已存在跳过";
                }
                $this->line($msg);
            }
        });
    }

    /**
     * 从 Sensitive-lexicon 词库文件补充导入
     */
    private function importFromVocabulary(bool $force, int &$totalWords, int &$skippedWords): void
    {
        $vocabularyDir = dirname(__DIR__, 2) . '/data/vocabulary';

        if (!is_dir($vocabularyDir)) {
            return;
        }

        // 按分类聚合映射
        $categoryFiles = [];
        foreach (self::VOCABULARY_MAP as [$file, $categoryCode, $severity, $maxCount]) {
            $categoryFiles[$categoryCode][] = ['file' => $file, 'severity' => $severity, 'max' => $maxCount];
        }

        $fileCache = [];

        DB::transaction(function () use ($force, &$totalWords, &$skippedWords, $vocabularyDir, $categoryFiles, &$fileCache) {
            foreach ($categoryFiles as $categoryCode => $fileConfigs) {
                $category = WordCategory::where('code', $categoryCode)->first();
                if (!$category) {
                    continue;
                }

                $categoryTotal = 0;
                $categorySkipped = 0;
                $importedWords = [];

                foreach ($fileConfigs as $config) {
                    $filePath = $vocabularyDir . '/' . $config['file'];

                    if (!file_exists($filePath)) {
                        continue;
                    }

                    if (!isset($fileCache[$config['file']])) {
                        $content = file_get_contents($filePath);
                        $lines = explode("\n", $content);
                        $fileCache[$config['file']] = array_map('trim', $lines);
                    }

                    $words = $fileCache[$config['file']];
                    $maxCount = $config['max'];
                    $imported = 0;

                    foreach ($words as $word) {
                        if ($word === '' || mb_strlen($word) > 50) {
                            continue;
                        }
                        if (preg_match('/^https?:\/\//i', $word)) {
                            continue;
                        }
                        if (preg_match('/^[a-z0-9._-]+$/i', $word) && mb_strlen($word) < 3) {
                            continue;
                        }
                        if (in_array($word, $importedWords)) {
                            continue;
                        }
                        if ($maxCount > 0 && $imported >= $maxCount) {
                            break;
                        }

                        $existing = SensitiveWord::where('word', $word)
                            ->where('category_id', $category->id)
                            ->first();

                        if ($existing && !$force) {
                            $categorySkipped++;
                            $skippedWords++;
                            continue;
                        }

                        SensitiveWord::updateOrCreate(
                            [
                                'word' => $word,
                                'category_id' => $category->id,
                            ],
                            [
                                'severity' => $config['severity'],
                                'is_variant' => false,
                                'status' => WordStatus::Active->value,
                            ]
                        );
                        $importedWords[] = $word;
                        $imported++;
                        $totalWords++;
                    }
                    $categoryTotal += $imported;
                }

                $category->refreshWordCount();

                if ($categoryTotal > 0 || $categorySkipped > 0) {
                    $catName = $category->name;
                    $msg = "  {$catName} ({$categoryCode}): {$categoryTotal} 词已导入";
                    if ($categorySkipped > 0) {
                        $msg .= "，{$categorySkipped} 词已存在跳过";
                    }
                    $this->line($msg);
                }
            }
        });
    }
}
