#!/usr/bin/env python3
from pathlib import Path

path = Path("/Users/john/Project/Memind/server.mjs")
text = path.read_text()

if "function verifyWechatMpUrlChallenge" not in text:
    text = text.replace(
        '    acceptLink: env.H5_WECHAT_MP_ACCEPT_LINK !== "0"\n  };\n}',
        '    acceptLink: env.H5_WECHAT_MP_ACCEPT_LINK !== "0",\n    encodingAesKey: env.H5_WECHAT_MP_ENCODING_AES_KEY?.trim() ?? ""\n  };\n}',
        1,
    )
    old_sig = """function verifyWechatMpSignature({ token, timestamp, nonce, signature }) {
  const digest = crypto27.createHash("sha1").update([token, timestamp, nonce].sort().join("")).digest("hex");
  return digest === String(signature ?? "");
}"""
    new_sig = """function sha1WechatHex(parts) {
  return crypto27.createHash("sha1").update([...parts].sort().join("")).digest("hex");
}
function verifyWechatMpSignature({ token, timestamp, nonce, signature }) {
  return sha1WechatHex([token, timestamp, nonce]) === String(signature ?? "");
}
function verifyWechatMpMsgSignature({ token, timestamp, nonce, echoStr, msgSignature }) {
  return sha1WechatHex([token, timestamp, nonce, echoStr]) === String(msgSignature ?? "");
}
function decodeWechatMpAesKey(encodingAesKey) {
  const key = Buffer.from(`${String(encodingAesKey ?? "").trim()}=`, "base64");
  if (key.length !== 32) throw new Error("invalid encoding aes key");
  return key;
}
function decryptWechatMpPayload({ encodingAesKey, appId, encrypted }) {
  const key = decodeWechatMpAesKey(encodingAesKey);
  const iv = key.subarray(0, 16);
  const decipher = crypto27.createDecipheriv("aes-256-cbc", key, iv);
  decipher.setAutoPadding(false);
  const cipherBuf = Buffer.from(String(encrypted ?? ""), "base64");
  let decoded = Buffer.concat([decipher.update(cipherBuf), decipher.final()]);
  const pad = decoded.at(-1);
  if (!Number.isInteger(pad) || pad < 1 || pad > 32) throw new Error("invalid padding");
  decoded = decoded.subarray(0, decoded.length - pad);
  const content = decoded.subarray(16);
  const msgLen = content.readUInt32BE(0);
  const msg = content.subarray(4, 4 + msgLen).toString("utf8");
  const receivedAppId = content.subarray(4 + msgLen).toString("utf8");
  if (receivedAppId !== String(appId ?? "")) throw new Error("appid mismatch");
  return msg;
}
function verifyWechatMpUrlChallenge(query = {}, config = {}) {
  const timestamp = String(query.timestamp ?? "");
  const nonce = String(query.nonce ?? "");
  const echostr = String(query.echostr ?? "");
  const encryptType = String(query.encrypt_type ?? "").toLowerCase();
  if (encryptType === "aes") {
    const msgSignature = String(query.msg_signature ?? "");
    if (!config?.token || !config?.encodingAesKey || !config?.appId) {
      return { ok: false, status: 503, body: "wechat mp aes mode not configured" };
    }
    if (!verifyWechatMpMsgSignature({ token: config.token, timestamp, nonce, echoStr: echostr, msgSignature })) {
      return { ok: false, status: 403, body: "invalid signature" };
    }
    try {
      const plain = decryptWechatMpPayload({ encodingAesKey: config.encodingAesKey, appId: config.appId, encrypted: echostr });
      return { ok: true, status: 200, body: plain };
    } catch {
      return { ok: false, status: 403, body: "invalid echostr" };
    }
  }
  if (!verifyWechatMpSignature({ token: config?.token, timestamp, nonce, signature: String(query.signature ?? "") })) {
    return { ok: false, status: 403, body: "invalid signature" };
  }
  return { ok: true, status: 200, body: echostr };
}"""
    if old_sig not in text:
        raise SystemExit("signature block not found")
    text = text.replace(old_sig, new_sig, 1)
    text = text.replace(
        """  const verifyRequest = (query = {}) => verifyWechatMpSignature({
    token: config.token,
    timestamp: String(query.timestamp ?? ""),
    nonce: String(query.nonce ?? ""),
    signature: String(query.signature ?? "")
  });""",
        """  const verifyRequest = (query = {}) => verifyWechatMpSignature({
    token: config.token,
    timestamp: String(query.timestamp ?? ""),
    nonce: String(query.nonce ?? ""),
    signature: String(query.signature ?? "")
  });
  const verifyUrlChallenge = (query = {}) => verifyWechatMpUrlChallenge(query, config);""",
        1,
    )
    text = text.replace(
        "    verifyRequest,\n    handleInboundMessage,",
        "    verifyRequest,\n    verifyUrlChallenge,\n    handleInboundMessage,",
        1,
    )
    path.write_text(text)
    print("bundled server.mjs aes patch applied")
else:
    print("bundled aes helpers already present")

release = Path("/Users/john/Project/releases/memind-live-before-20260626-095637-d51df2f/wechat-mp.mjs")
if release.exists():
    Path("/Users/john/Project/Memind/wechat-mp.mjs").write_text(release.read_text())
    print("wechat-mp.mjs restored from release")
