"""FastHTML webhook routes, deduplication, logging setup, and uvicorn server startup."""

# AUTOGENERATED! DO NOT EDIT! File to edit: ../nbs/04_app.ipynb.

# %% auto #0
__all__ = ['log', 'app', 'rt', 'is_dup', 'parse_xml', 'wecom_verify', 'wecom_receive', 'start_bot', 'stop_bot']

# %% ../nbs/04_app.ipynb #6de6df91
import asyncio, logging, os, threading, time
import uvicorn
from fasthtml.common import *
from starlette.requests import Request
from starlette.responses import PlainTextResponse
import xml.etree.ElementTree as ET

from .config import TOKEN, DEDUP_WINDOW
from .crypto import msg_sig, decrypt
from .dialog import process_message

# %% ../nbs/04_app.ipynb #b487034e
os.makedirs('/app/data/clawlog', exist_ok=True)
logging.basicConfig(
    filename='/app/data/clawlog/wecom.log', level=logging.INFO,
    format='%(asctime)s %(message)s', force=True
)
log = logging.getLogger('wecom')

# %% ../nbs/04_app.ipynb #344c9c98
_seen: dict[str, float] = {}

def is_dup(
    mid: str  # WeCom MsgId to check
) -> bool:
    "Return True if `mid` was seen within the last `DEDUP_WINDOW` s (deduplicates WeCom retries)."
    now = time.time()
    for k in [k for k, v in _seen.items() if now - v > DEDUP_WINDOW]: del _seen[k]
    if mid in _seen: return True
    _seen[mid] = now
    return False

# %% ../nbs/04_app.ipynb #caf942a2
def parse_xml(
    s: str  # XML string from WeCom
) -> dict:
    "Parse a WeCom XML message into a `{tag: text}` dict."
    return {c.tag: c.text for c in ET.fromstring(s)}

# %% ../nbs/04_app.ipynb #1b2e5e02
app, rt = fast_app(port=8000)

@rt('/wecom', methods=['GET'])
async def wecom_verify(request: Request):
    p = request.query_params
    sig, ts, nonce, echo = p.get('msg_signature',''), p.get('timestamp',''), p.get('nonce',''), p.get('echostr','')
    if msg_sig(TOKEN, ts, nonce, echo) != sig:
        return PlainTextResponse('signature mismatch', status_code=403)
    decrypted, _ = decrypt(echo)
    log.info('✅ URL 验证成功')
    return PlainTextResponse(decrypted)

@rt('/wecom', methods=['POST'])
async def wecom_receive(request: Request):
    p = request.query_params
    sig, ts, nonce = p.get('msg_signature',''), p.get('timestamp',''), p.get('nonce','')
    body = await request.body()
    log.info(f'📩 收到 POST, body长度: {len(body)}')
    try:
        outer = parse_xml(body.decode('utf-8'))
        enc = outer.get('Encrypt', '')
        if msg_sig(TOKEN, ts, nonce, enc) != sig:
            log.info('❌ 签名验证失败')
            return PlainTextResponse('signature mismatch', status_code=403)
        xml_text, corp_id = decrypt(enc)
        msg = parse_xml(xml_text)
        msg_type, mid = msg.get('MsgType',''), msg.get('MsgId','')
        user_id, content = msg.get('FromUserName',''), msg.get('Content','')
        log.info(f'📩 消息 [{msg_type}] from {user_id}: {content}')
        if msg_type == 'text' and content and not is_dup(mid):
            asyncio.create_task(process_message(user_id, content))
    except Exception as e:
        log.info(f'❌ 解析失败: {e}')
    return PlainTextResponse('')

# %% ../nbs/04_app.ipynb #59d3410e
def start_bot() -> uvicorn.Server:
    "Start the uvicorn server in a daemon thread and return the server instance."
    config = uvicorn.Config(
        app,
        host='0.0.0.0',
        port=8000,
        reload=True,
        access_log=False,
    )
    server = uvicorn.Server(config)
    threading.Thread(target=server.run, daemon=True).start()
    log.info('🚀 服务已启动')
    print('🚀 Webhook 服务已启动 (port 8000)')
    return server

# %% ../nbs/04_app.ipynb #2a5b75c8
def stop_bot(server: uvicorn.Server) -> None:
    "Signal the uvicorn server to shut down gracefully."
    server.should_exit = True
