#!/usr/bin/env python3
"""小艺 ↔ Agent67 桥接 — PaaS agent: ag_e7e376960efe (lambda-xiaoyi)"""
import json, time, hmac, hashlib, base64, ssl, uuid, threading, sys

AK = 'd031c15a2c7e4ad7a8c71312c31816c9'
SK = '8FB5757D89AA777C31EE542A7C0BF966347143004C3591766762CF429AC21BBB'
AGENT_ID = 'agent935da52acaeb48d09f6adc858c7d3ae2'
WS1 = 'wss://hag.cloud.huawei.com/openclaw/v1/ws/link'
WS2 = 'wss://116.63.174.231/openclaw/v1/ws/link'
PAAS_AGENT_ID = 'ag_e7e376960efe'
PAAS_API_KEY = 'ap_8c8694e7f49d9af60c30a92ae17971d6'

import os
from pathlib import Path
_project_root = Path(__file__).resolve().parent
sys.path.insert(0, str(_project_root))
sys.path.insert(0, str(_project_root / 'agentexample'))
_assistant = None

LOG = open('/tmp/xiaoyi_test.log', 'w', buffering=1)
def log(msg):
    line = f'[{time.strftime("%H:%M:%S")}] {msg}'
    print(line, flush=True)
    LOG.write(line + '\n')
    LOG.flush()

def headers():
    ts = str(int(time.time() * 1000))
    sig = base64.b64encode(hmac.new(SK.encode(), ts.encode(), hashlib.sha256).digest()).decode()
    return {'x-access-key': AK, 'x-sign': sig, 'x-ts': ts, 'x-agent-id': AGENT_ID}

def wrap(session_id, task_id, msg_id, result_obj):
    jsonrpc = {'jsonrpc': '2.0', 'id': msg_id, 'result': result_obj}
    return json.dumps({
        'msgType': 'agent_response', 'agentId': AGENT_ID,
        'sessionId': session_id, 'taskId': task_id,
        'msgDetail': json.dumps(jsonrpc),
    })

def run_server(name, url):
    import websocket
    sslopt = {}
    if '116.63' in url:
        sslopt = {'cert_reqs': ssl.CERT_NONE, 'check_hostname': False}

    ws = websocket.WebSocket(sslopt=sslopt)
    ws.timeout = 600
    log(f'[{name}] Connecting...')
    ws.connect(url, header=headers())
    log(f'[{name}] CONNECTED')

    # init
    ws.send(json.dumps({'msgType': 'clawd_bot_init', 'agentId': AGENT_ID}))
    log(f'[{name}] init sent')

    # heartbeat thread
    def heartbeat():
        while True:
            time.sleep(25)
            try:
                ws.send(json.dumps({'msgType': 'heartbeat', 'agentId': AGENT_ID, 'timestamp': int(time.time()*1000)}))
            except:
                break
    t = threading.Thread(target=heartbeat, daemon=True)
    t.start()

    # listen
    while True:
        try:
            raw = ws.recv()
            if not raw:
                continue
            data = json.loads(raw)
            if data.get('msgType') == 'heartbeat':
                continue

            log(f'📩 [{name}] {json.dumps(data, ensure_ascii=False)[:300]}')

            params = data.get('params', {})
            task_id = params.get('id', data.get('id', ''))
            session_id = params.get('sessionId', data.get('sessionId', ''))
            msg_id = data.get('id', '')
            parts = params.get('message', {}).get('parts', [])
            text = ' '.join(p.get('text', '') for p in parts if p.get('kind') == 'text').strip()

            if not text or not task_id:
                continue

            top_session = data.get('sessionId', '')
            log(f'  User: {text}')
            log(f'  param_session={session_id}  top_session={top_session}')

            sid = top_session

            # 定期发 working 状态防止小艺超时
            agent_done = threading.Event()
            agent_reply = [None]

            def keep_alive():
                """每 8 秒发一次 reasoningText，保持连接活跃"""
                tick = 0
                msgs = ['正在思考...', '分析问题中...', '调用工具中...', '处理数据...', '即将完成...']
                while not agent_done.is_set():
                    time.sleep(8)
                    if agent_done.is_set():
                        break
                    tick += 1
                    # 用 reasoningText (原始插件的做法)，不用 status working
                    ws.send(wrap(sid, task_id, msg_id, {
                        'taskId': task_id, 'kind': 'artifact-update',
                        'append': True, 'lastChunk': True, 'final': False,
                        'artifact': {'artifactId': f'reason_{int(time.time()*1000)}',
                            'parts': [{'kind': 'reasoningText', 'reasoningText': msgs[tick % len(msgs)] + '\n'}]},
                    }))
                    log(f'  [{name}] ♻ reasoning #{tick}')

            ka_thread = threading.Thread(target=keep_alive, daemon=True)
            ka_thread.start()

            t0 = time.time()
            # 调用 agent67 (Claude Code)
            try:
                from agent67.core.assistant import PersonalAssistant
                global _assistant
                if _assistant is None:
                    _assistant = PersonalAssistant(model="sonnet", use_api=False)
                reply = _assistant.chat(text)
            except Exception as e:
                log(f'  [{name}] agent error: {e}')
                reply = f'🐂 lambda67: 抱歉，处理出错了: {e}'
            finally:
                agent_done.set()
            art_id = f'artifact_{int(time.time()*1000)}'

            # 1. 内容 (append=true, final=false)
            ws.send(wrap(sid, task_id, msg_id, {
                'taskId': task_id, 'kind': 'artifact-update',
                'append': True, 'lastChunk': False, 'final': False,
                'artifact': {'artifactId': art_id, 'parts': [{'kind': 'text', 'text': reply}]},
            }))
            log(f'  [{name}] → content (sid={sid[:16]})')

            # 2. 空结束标记 (append=true, final=true, text="")
            ws.send(wrap(sid, task_id, msg_id, {
                'taskId': task_id, 'kind': 'artifact-update',
                'append': True, 'lastChunk': True, 'final': True,
                'artifact': {'artifactId': art_id, 'parts': [{'kind': 'text', 'text': ''}]},
            }))
            log(f'  [{name}] → final (sid={sid[:16]})')

            # 3. status completed — 告诉小艺结束了
            status_id = f'done_{int(time.time()*1000)}'
            ws.send(wrap(sid, task_id, status_id, {
                'taskId': task_id, 'kind': 'status-update', 'final': True,
                'status': {'state': 'completed', 'message': {'role': 'agent', 'parts': []}},
            }))
            log(f'  [{name}] → status completed')

            # 记录到 PaaS
            try:
                import urllib.request as _ur
                _paas_body = json.dumps({
                    'input': text[:500], 'output': reply[:2000],
                    'status': 'completed', 'duration_ms': int((time.time() - t0) * 1000),
                    'steps': 1, 'source': 'xiaoyi-openclaw',
                }).encode()
                _req = _ur.Request(
                    f'http://127.0.0.1:8067/api/v1/agents/{PAAS_AGENT_ID}/runs/record',
                    data=_paas_body,
                    headers={'Content-Type': 'application/json', 'Authorization': f'Bearer {PAAS_API_KEY}'},
                    method='POST')
                _ur.urlopen(_req, timeout=5)
            except Exception:
                pass

            log(f'  [{name}] DONE ✓')

        except Exception as e:
            log(f'[{name}] ERROR: {e}')
            break

log('=== 小艺测试 v2 (websocket-client, 无自动 ping) ===')
t1 = threading.Thread(target=run_server, args=('S1', WS1), daemon=True)
t2 = threading.Thread(target=run_server, args=('S2', WS2), daemon=True)
t1.start()
t2.start()
t1.join()
t2.join()
