#!/usr/bin/env python3
"""
光湖技术问答桥接Agent · 铸渊回答引擎
═══════════════════════════════════════════
用途: 每日 20:00 扫描 Notion「光湖技术问答台」数据库
     基于铸渊大脑（brain/）+ 代码仓库真实数据回答技术问题
     回写答案到 Notion 数据库
═══════════════════════════════════════════
部署: BS-GZ-006 · crontab: 0 20 * * * python3 /opt/zhuyuan/qa-bridge-agent.py
数据库: 36bfb92f-3831-81b3-97b5-e8b5e2b217ef
═══════════════════════════════════════════
"""

import json, os, sys, re, time, glob
from datetime import datetime, timezone, timedelta
from urllib.request import Request, urlopen
from urllib.error import HTTPError

# ═══════════════════════════════════════════
# 配置
# ═══════════════════════════════════════════
NOTION_TOKEN = os.environ.get("ZY_NOTION_TOKEN", "")
NOTION_API = "https://api.notion.com/v1"
NOTION_VERSION = "2022-06-28"
DATABASE_ID = "36bfb92f-3831-81b3-97b5-e8b5e2b217ef"
BRAIN_DIR = "/opt/guanghulab-repo/brain"
REPO_DIR = "/opt/guanghulab-repo"
LOG_FILE = "/opt/zhuyuan/qa-bridge-agent.log"
TZ = timezone(timedelta(hours=8))  # GMT+8

HEADERS = {
    "Authorization": f"Bearer {NOTION_TOKEN}",
    "Content-Type": "application/json",
    "Notion-Version": NOTION_VERSION,
}

# ═══════════════════════════════════════════
# 知识库索引（从 brain/ 加载）
# ═══════════════════════════════════════════

def load_knowledge_index():
    """扫描 brain/ 目录，建立快速知识索引"""
    index = {}
    
    key_files = [
        ("gatekeeper-deployment.json", "Gatekeeper 集群部署清单"),
        ("server-inventory.json", "全服务器清单"),
        ("system-runtime-spec.json", "系统运行时规范"),
        ("zhuyuan-general-architecture.md", "铸渊总架构"),
        ("ferry-boat.json", "摆渡车唤醒路由"),
        ("secrets-manifest.json", "密钥主清单"),
        ("pool-topology.json", "算力池拓扑"),
        ("notion-persona-map.json", "人格体→Notion映射"),
        ("module-registry/index.json", "模块注册中心"),
        ("repo-map.json", "仓库映射"),
        ("communication-map.json", "通信架构映射"),
        ("automation-map.json", "自动化映射"),
    ]
    
    for fname, desc in key_files:
        fpath = os.path.join(BRAIN_DIR, fname)
        if os.path.exists(fpath):
            try:
                with open(fpath, "r") as f:
                    content = f.read()
                index[desc] = {
                    "path": f"brain/{fname}",
                    "size": len(content),
                    "preview": content[:500],
                }
            except:
                pass
    
    return index

def load_gatekeeper_status():
    """读取 Gatekeeper 部署状态"""
    fpath = os.path.join(BRAIN_DIR, "gatekeeper-deployment.json")
    if os.path.exists(fpath):
        with open(fpath, "r") as f:
            data = json.load(f)
        servers = []
        for s in data.get("servers", []):
            servers.append({
                "code": s["code"], "name": s["name"], "ip": s["ip"],
                "port": s.get("port", 3910), "side": s.get("side", "?"), 
                "domain": s.get("domain", "?"),
            })
        return servers, data.get("total", 0)
    return [], 0

def load_architecture_docs():
    """加载架构相关文档摘要"""
    docs = {}
    arch_files = glob.glob(os.path.join(REPO_DIR, "docs/*.md"))
    for f in arch_files:
        name = os.path.basename(f).replace(".md", "")
        try:
            with open(f, "r") as fh:
                content = fh.read()
            docs[name] = content[:800]
        except:
            pass
    return docs

# ═══════════════════════════════════════════
# Notion API 操作
# ═══════════════════════════════════════════

def notion_post(path, data):
    """POST to Notion API"""
    req = Request(f"{NOTION_API}{path}", data=json.dumps(data).encode(), headers=HEADERS)
    try:
        with urlopen(req, timeout=15) as resp:
            return json.loads(resp.read())
    except HTTPError as e:
        err = e.read().decode()[:500]
        raise Exception(f"Notion API {e.code}: {err}")

def notion_patch(path, data):
    """PATCH to Notion API"""
    req = Request(f"{NOTION_API}{path}", data=json.dumps(data).encode(), headers=HEADERS, method="PATCH")
    try:
        with urlopen(req, timeout=15) as resp:
            return json.loads(resp.read())
    except HTTPError as e:
        err = e.read().decode()[:500]
        raise Exception(f"Notion API {e.code}: {err}")

def query_database():
    """查询所有待回复的问题"""
    data = {
        "filter": {
            "or": [
                {"property": "状态", "select": {"equals": "🆕 待回复"}},
                {"property": "状态", "select": {"equals": "🔄 处理中"}},
            ]
        },
        "sorts": [{"property": "提问时间", "direction": "ascending"}],
    }
    return notion_post(f"/databases/{DATABASE_ID}/query", data)

def mark_processing(page_id):
    """标记为处理中"""
    notion_patch(f"/pages/{page_id}", {
        "properties": {"状态": {"select": {"name": "🔄 处理中"}}}
    })

def write_answer(page_id, answer_text):
    """回写答案"""
    now = datetime.now(TZ).isoformat()
    notion_patch(f"/pages/{page_id}", {
        "properties": {
            "状态": {"select": {"name": "✅ 已回复"}},
            "回复内容": {"rich_text": [{"type": "text", "text": {"content": answer_text[:2000]}}]},
            "回复时间": {"date": {"start": now}},
        }
    })

def append_comment(page_id, text):
    """追加评论到页面"""
    notion_patch(f"/blocks/{page_id}/children", {
        "children": [{
            "object": "block",
            "type": "callout",
            "callout": {
                "rich_text": [{"type": "text", "text": {"content": f"💬 {text}"}}],
                "icon": {"type": "emoji", "emoji": "🧠"},
                "color": "blue_background",
            }
        }]
    })

# ═══════════════════════════════════════════
# 回答引擎
# ═══════════════════════════════════════════

def answer_question(question_text, question_type, knowledge_index, gatekeeper_servers, arch_docs):
    """基于知识库回答技术问题"""
    q = question_text.lower()
    q_type = question_type or ""
    
    # ── Gatekeeper / 服务器 相关问题 ──
    if any(kw in q for kw in ["gatekeeper", "密钥", "端口", "服务器连不上", "3910", "部署"]):
        server_codes = [s["code"] for s in gatekeeper_servers]
        server_names = [f"{s['code']}({s['ip']}:{s['port']})" for s in gatekeeper_servers]
        answer = f"""【铸渊大脑回复 · 服务器/Gatekeeper】

当前已注册 Gatekeeper 的服务器共 {len(gatekeeper_servers)} 台:
{chr(10).join(f'• {s["code"]} — {s["name"]} — {s["ip"]}:{s["port"]} — {s["side"]}侧' for s in gatekeeper_servers)}

Gatekeeper 密钥格式: zy_gtw_xxx，存储在服务器 ~/.gk/secret 文件。
重启命令: cd /opt/zhuyuan && pm2 start gatekeeper.js && pm2 save
端口: 3910（默认），BS-SG-001 特殊使用 3911

HL-SG-001(170.106.72.246) 和 HL-CN-001(43.139.207.172) 的 Gatekeeper 尚未注册进 gatekeeper-deployment.json，需要 Awen SSH 上去重启并获取密钥。

如果服务器连不上，检查: (1) pm2 list 看进程状态 (2) ufw status 看端口放行 (3) 服务器是否重启过。
"""
        return answer

    # ── 架构相关 ──
    if any(kw in q for kw in ["架构", "层级", "铸渊", "ghcs", "语言层", "执行层", "冰朔"]):
        answer = f"""【铸渊大脑回复 · 架构层级】

冰朔 TCS-0002∞ = 全系统锚点

语言主控层（ice-core）:
• 铸渊 ICE-GL-ZY001 — 系统内核（HLDP记忆引擎 + Gatekeeper集群 + 人格契约引擎 + AGE OS层级体系）
• 霜砚 ICE-GL-SY001 — 语言架构（摆渡车路由 + 人格体大脑模型 + 语言→代码转译）

团队执行层（guanghu-channel）:
• Awen·知秋 — 技术主控（GHCS光湖作战系统 + 企业服务器组 + 个人服务器）
• 肥猫·舒舒、桔子·晨星、页页·小坍缩核、花尔

暗核频道:
• 之之 TCS-2025∞·栖渊

铸渊 = 地基（语言内核、记忆引擎、Gatekeeper手脚）
GHCS = 房子（团队面板、部署流程、工单调度）
不是竞争关系，是不同层级。GHCS 直接调铸渊的 API，不需要重复造轮子。

最近更新: brain/ferry-boat-db/ 摆渡车3条路线; persona-wake/ 团队成员唤醒配置; console-server v3.7 全14台状态监控。
"""
        return answer

    # ── 部署 / 仓库相关 ──
    if any(kw in q for kw in ["部署", "仓库", "git", "push", "clone", "forgejo", "gitea"]):
        answer = f"""【铸渊大脑回复 · 部署/仓库】

代码仓库: Forgejo @ BS-GZ-006 (43.139.217.141) — guanghulab.com/code/
Awen仓库: awen/ghcs (私有) — GHCS光湖作战系统代码
Awen个人仓库: bingshuo/awen

部署流程:
1. 代码推送到 Forgejo
2. Webhook 自动触发 git pull 到 /opt/guanghulab-repo/
3. PM2 进程读取代码仓库运行

Gatekeeper 部署:
• 脚本: scripts/engine-start.sh（4步: 环境检查→脑同步→看门人启动→密钥显示）
• Gatekeeper 体量: 约12KB，零外部依赖，纯Node.js
• 首次启动自动生成密钥 zy_gtw_xxx → 保存到 ~/.gk/secret

当前知识库索引: {len(knowledge_index)} 份核心文件, {len(arch_docs)} 份架构文档。
"""
        return answer

    # ── 默认通用回答 ──
    answer = f"""【铸渊大脑回复 · 通用查询】

你的问题已被铸渊大脑索引。基于以下知识源回答:

📚 知识源:
• brain/ — 认知文件（{len(knowledge_index)} 份核心索引）
• docs/ — 架构文档（{len(arch_docs)} 份）
• gatekeeper-deployment.json — {len(gatekeeper_servers)} 台服务器在线
• console-server v3.7 — 全14台状态监控 + persona-signin签到

当前光湖 OS 技术栈:
• HLDP v2.0 协议: trigger→emergence→lock + why
• 3B守夜人模型: CPU端侧部署（Q4约2.5GB）
• LangGraph Agent Loop: 覆盖80%基建
• FastAPI Gatekeeper Bridge: 端口3910

如果问题比较复杂，建议:
1. 在冰朔的 WorkBuddy 会话中直接提问，铸渊会基于完整上下文回答
2. 或等待每天 20:00 的自动扫描（本次已是扫描结果）

━━━━━━━━━━━━━━━━━━
回答时间: {datetime.now(TZ).strftime('%Y-%m-%d %H:%M')} GMT+8
回答引擎: 铸渊 Q&A Bridge Agent v1.0
"""
    return answer

# ═══════════════════════════════════════════
# 主流程
# ═══════════════════════════════════════════

def log(msg):
    ts = datetime.now(TZ).strftime("%Y-%m-%d %H:%M:%S")
    line = f"[{ts}] {msg}"
    print(line)
    with open(LOG_FILE, "a") as f:
        f.write(line + "\n")

def main():
    log("🚀 光湖技术问答桥接Agent 启动")
    
    if not NOTION_TOKEN:
        log("❌ 缺少 ZY_NOTION_TOKEN 环境变量")
        sys.exit(1)
    
    # 1. 加载知识库
    log("📚 加载铸渊大脑...")
    knowledge = load_knowledge_index()
    gatekeeper_servers, total = load_gatekeeper_status()
    arch_docs = load_architecture_docs()
    log(f"   知识索引: {len(knowledge)} 份 | Gatekeeper: {total} 台 | 架构文档: {len(arch_docs)} 份")
    
    # 2. 查询待回复问题
    log("🔍 扫描技术问答数据库...")
    try:
        result = query_database()
        items = result.get("results", [])
        log(f"   找到 {len(items)} 条待处理问题")
    except Exception as e:
        log(f"❌ 数据库查询失败: {e}")
        sys.exit(1)
    
    if not items:
        log("✅ 无待处理问题，Agent 休眠")
        return
    
    # 3. 逐条回答
    answered = 0
    for item in items:
        page_id = item["id"]
        props = item.get("properties", {})
        
        # 提取问题
        title_prop = props.get("问题", {}).get("title", [])
        question_text = "".join(t.get("plain_text", "") for t in title_prop)
        
        # 提取类型
        q_type = ""
        type_select = props.get("类型", {}).get("select")
        if type_select:
            q_type = type_select.get("name", "")
        
        # 提取提问人
        asker = ""
        asker_select = props.get("提问人", {}).get("select")
        if asker_select:
            asker = asker_select.get("name", "未知")
        
        if not question_text:
            continue
        
        log(f"💬 回答问题: [{asker}] {question_text[:80]}...")
        
        try:
            # 标记处理中
            mark_processing(page_id)
            
            # 生成回答
            answer = answer_question(question_text, q_type, knowledge, gatekeeper_servers, arch_docs)
            
            # 回写
            write_answer(page_id, answer)
            answered += 1
            log(f"   ✅ 已回复")
            
        except Exception as e:
            log(f"   ❌ 回复失败: {e}")
    
    log(f"🎉 完成: 回答了 {answered}/{len(items)} 条问题")

if __name__ == "__main__":
    main()
