import crypto from 'node:crypto';
import { fetch as undiciFetch } from 'undici';
import { mergeMessageContent } from './message-stream.mjs';
import { reconcileAgentSession } from './session-reconcile.mjs';
import { isScheduleIntent, parseScheduleIntent } from './schedule-intent.mjs';

const DEFAULT_WECHAT_TOKEN_URL = 'https://api.weixin.qq.com/cgi-bin/stable_token';
const DEFAULT_WECHAT_CUSTOMER_SERVICE_URL =
  'https://api.weixin.qq.com/cgi-bin/message/custom/send';
const DEFAULT_WECHAT_JSAPI_TICKET_URL = 'https://api.weixin.qq.com/cgi-bin/ticket/getticket';
const DEFAULT_ACK_TEXT = '已收到，正在处理，完成后发到这里。';
const DEFAULT_PROGRESS_TEXT = '还在处理，请稍等片刻。';
const DEFAULT_STATUS_TEXT =
  '我在这边。上一条如果还没完成，我会继续把结果发给你；你也可以直接补一句要求。';
const DEFAULT_UNSUPPORTED_TEXT = '当前先支持文字消息，你可以直接发文字给我。';
const DEFAULT_UNBOUND_TEXT = '先点这里完成绑定，再继续和专属 Agent 对话：';
const DEFAULT_PROGRESS_DELAY_MS = 8000;

function parseXmlField(xml, field) {
  const cdataMatch = xml.match(new RegExp(`<${field}><!\\[CDATA\\[([\\s\\S]*?)\\]\\]><\\/${field}>`));
  if (cdataMatch) return cdataMatch[1];
  const plainMatch = xml.match(new RegExp(`<${field}>([\\s\\S]*?)<\\/${field}>`));
  return plainMatch ? plainMatch[1].trim() : '';
}

function parseWechatMessage(xml) {
  return {
    toUserName: parseXmlField(xml, 'ToUserName'),
    fromUserName: parseXmlField(xml, 'FromUserName'),
    createTime: Number(parseXmlField(xml, 'CreateTime') || 0),
    msgType: parseXmlField(xml, 'MsgType').toLowerCase(),
    content: parseXmlField(xml, 'Content'),
    msgId: parseXmlField(xml, 'MsgId'),
    event: parseXmlField(xml, 'Event').toLowerCase(),
    eventKey: parseXmlField(xml, 'EventKey'),
    encrypt: parseXmlField(xml, 'Encrypt'),
  };
}

function readJsonResponse(response) {
  return response.text().then((text) => {
    if (!response.ok) {
      throw new Error(text || `upstream ${response.status}`);
    }
    return text ? JSON.parse(text) : null;
  });
}

function deriveWechatEndpointFromUrl(sourceUrl, pathname) {
  if (!sourceUrl) return '';
  try {
    const url = new URL(sourceUrl);
    url.pathname = pathname;
    url.search = '';
    return url.toString();
  } catch {
    return '';
  }
}

function createUserMessage(text) {
  return {
    id: crypto.randomUUID(),
    role: 'user',
    created: Math.floor(Date.now() / 1000),
    content: [{ type: 'text', text }],
    metadata: {
      userVisible: true,
      agentVisible: true,
    },
  };
}

function messageVisibleText(message) {
  if (!message?.content) return '';
  return message.content
    .filter((item) => item.type === 'text' && typeof item.text === 'string')
    .map((item) => item.text)
    .join('');
}

function pushMessage(messages, incoming) {
  const last = messages[messages.length - 1];
  if (last?.id && incoming?.id && last.id === incoming.id) {
    return [
      ...messages.slice(0, -1),
      {
        ...last,
        content: mergeMessageContent(last.content, incoming.content),
      },
    ];
  }
  return [...messages, incoming];
}

async function executeSessionReply(apiFetch, sessionId, requestId, prompt) {
  const eventsResponse = await apiFetch(`/sessions/${sessionId}/events`, {
    method: 'GET',
    headers: { Accept: 'text/event-stream' },
  });
  if (!eventsResponse.ok || !eventsResponse.body) {
    const text = await eventsResponse.text().catch(() => '');
    throw new Error(text || '无法建立公众号消息事件流');
  }

  const replyResponse = await apiFetch(`/sessions/${sessionId}/reply`, {
    method: 'POST',
    body: JSON.stringify({
      request_id: requestId,
      user_message: createUserMessage(prompt),
    }),
  });
  if (!replyResponse.ok) {
    const text = await replyResponse.text().catch(() => '');
    throw new Error(text || 'Agent reply 请求失败');
  }
  replyResponse.body?.cancel().catch?.(() => {});

  const reader = eventsResponse.body.getReader();
  const decoder = new TextDecoder();
  let buffer = '';
  let messages = [];

  while (true) {
    const { value, done } = await reader.read();
    if (done) break;
    buffer += decoder.decode(value, { stream: true });
    const frames = buffer.split('\n\n');
    buffer = frames.pop() ?? '';
    for (const frame of frames) {
      let data = '';
      for (const line of frame.split('\n')) {
        if (line.startsWith('data:')) data += line.slice(5).trim();
      }
      if (!data) continue;
      let event;
      try {
        event = JSON.parse(data);
      } catch {
        continue;
      }
      const routingId = event.chat_request_id ?? event.request_id;
      if (routingId && routingId !== requestId) continue;

      if (event.type === 'Message' && event.message?.metadata?.userVisible) {
        const hasActionRequired = event.message.content?.some((item) => item.type === 'actionRequired');
        if (hasActionRequired) {
          throw new Error('当前回复需要人工确认，公众号通道暂不支持');
        }
        messages = pushMessage(messages, event.message);
      } else if (event.type === 'UpdateConversation') {
        messages = (event.conversation ?? []).filter((item) => item.metadata?.userVisible);
      } else if (event.type === 'Error') {
        throw new Error(event.error || '任务执行失败');
      } else if (event.type === 'Finish') {
        const assistant = [...messages].reverse().find((item) => item.role === 'assistant');
        return {
          text: messageVisibleText(assistant),
          tokenState: event.token_state ?? null,
        };
      }
    }
  }

  throw new Error('公众号消息事件流提前结束');
}

const WECHAT_CUSTOMER_TEXT_MAX_BYTES = 2048;

function splitWechatText(text, maxBytes = WECHAT_CUSTOMER_TEXT_MAX_BYTES) {
  const normalized = String(text ?? '').trim();
  if (!normalized) return ['我先收到了，但这次没有生成可发送的文本结果。'];

  const chunks = [];
  let offset = 0;
  while (offset < normalized.length) {
    let byteCount = 0;
    let end = offset;
    while (end < normalized.length) {
      const charBytes = Buffer.byteLength(normalized[end], 'utf8');
      if (byteCount + charBytes > maxBytes) break;
      byteCount += charBytes;
      end += 1;
    }
    if (end === offset) break;
    chunks.push(normalized.slice(offset, end));
    offset = end;
  }
  return chunks.length > 0 ? chunks : ['我先收到了，但这次没有生成可发送的文本结果。'];
}

export { splitWechatText, WECHAT_CUSTOMER_TEXT_MAX_BYTES };

function stripInternalWechatUsername(text) {
  return String(text ?? '').replace(/^\s*wx_[a-z0-9_]{4,64}\s*[,，、:：]\s*/i, '');
}

function convertMarkdownLinks(text) {
  return String(text ?? '').replace(/\[([^\]\n]{1,160})\]\((https?:\/\/[^\s)]+)\)/g, (_match, label, url) => {
    const cleanLabel = String(label ?? '').trim();
    const cleanUrl = String(url ?? '').trim();
    return cleanLabel ? `${cleanLabel}\n${cleanUrl}` : cleanUrl;
  });
}

function splitMarkdownTableRow(line) {
  const trimmed = String(line ?? '').trim();
  if (!trimmed.includes('|')) return null;
  return trimmed
    .replace(/^\|/, '')
    .replace(/\|$/, '')
    .split('|')
    .map((cell) => cell.trim());
}

function isMarkdownTableSeparator(line) {
  const cells = splitMarkdownTableRow(line);
  return Boolean(cells?.length) && cells.every((cell) => /^:?-{3,}:?$/.test(cell));
}

function convertMarkdownTables(text) {
  const lines = String(text ?? '').split('\n');
  const output = [];
  for (let index = 0; index < lines.length; index += 1) {
    const headers = splitMarkdownTableRow(lines[index]);
    if (!headers || !isMarkdownTableSeparator(lines[index + 1])) {
      output.push(lines[index]);
      continue;
    }

    const readableRows = [];
    index += 2;
    while (index < lines.length) {
      const cells = splitMarkdownTableRow(lines[index]);
      if (!cells) break;
      const parts = cells
        .map((cell, cellIndex) => {
          const header = headers[cellIndex] || `列${cellIndex + 1}`;
          return cell ? `${header}：${cell}` : '';
        })
        .filter(Boolean);
      if (parts.length) readableRows.push(`- ${parts.join('；')}`);
      index += 1;
    }
    index -= 1;
    output.push(...readableRows);
  }
  return output.join('\n');
}

function formatWechatOutboundText(text) {
  return stripInternalWechatUsername(
    convertMarkdownTables(convertMarkdownLinks(text))
      .replace(/^\s*#{1,6}\s+/gm, '')
      .replace(/^\s*---+\s*$/gm, '')
      .replace(/\n{3,}/g, '\n\n'),
  );
}

function isQuestionStatusProbe(text) {
  return /^[?？]+$/.test(String(text ?? '').trim());
}

function isSimpleGreeting(text) {
  const normalized = String(text ?? '')
    .trim()
    .replace(/[!！。.\s]+$/g, '')
    .toLowerCase();
  return /^(你好|您好|在吗|在不在|嗨|hi|hello|hey)$/.test(normalized);
}

function isConnectivityTest(text) {
  const normalized = String(text ?? '')
    .trim()
    .replace(/[!！。.\s]+$/g, '');
  return /^(测试\s*\d*|test\s*\d*)$/i.test(normalized);
}

function isTopicResetIntent(text) {
  const normalized = String(text ?? '').trim();
  return (
    /^(换(个)?话题|新问题|忽略之前|不管之前|重新开始|reset)$/i.test(normalized) ||
    /忽略.*之前|不要管.*之前|别管.*之前/.test(normalized)
  );
}

function buildWechatAgentPrompt(text) {
  const content = String(text ?? '').trim();
  return [
    '【微信服务号新消息】请只回答下面这条用户消息，不要主动延续无关的历史话题。',
    '若用户只是在测试连通性，请一句话确认收到即可，不要展开旧任务。',
    '',
    `用户消息：${content}`,
  ].join('\n');
}

function buildConnectivityTestReply(user) {
  const name = resolveWechatAddressName(user);
  return name
    ? `${name}，公众号消息通道正常，我收到你的测试了。`
    : '公众号消息通道正常，我收到你的测试了。';
}

function normalizeWechatName(value) {
  const name = String(value ?? '').trim();
  if (!name || /^wx_[a-z0-9_]{4,64}$/i.test(name)) return '';
  return name;
}

function resolveWechatAddressName(user) {
  return (
    normalizeWechatName(user?.nickname) ||
    normalizeWechatName(user?.displayName) ||
    normalizeWechatName(user?.username) ||
    ''
  );
}

function buildGreetingText(user) {
  const name = resolveWechatAddressName(user);
  return name ? `你好，${name}！我在呢，有什么需要？` : '你好！我在呢，有什么需要？';
}

function buildStatusText(user, fallbackText) {
  const name = resolveWechatAddressName(user);
  if (!name) return fallbackText;
  return `我在这边，${name}。上一条如果还没完成，我会继续把结果发给你；你也可以直接补一句要求。`;
}

function successResponse(task = null) {
  return {
    ok: true,
    status: 200,
    contentType: 'text/plain; charset=utf-8',
    body: 'success',
    ...(task ? { task } : {}),
  };
}

export function loadWechatMpConfig(env = process.env) {
  const appId = env.H5_WECHAT_MP_APP_ID?.trim() ?? env.H5_WECHAT_APP_ID?.trim() ?? '';
  const appSecret =
    env.H5_WECHAT_MP_APP_SECRET?.trim() ?? env.H5_WECHAT_APP_SECRET?.trim() ?? '';
  const token = env.H5_WECHAT_MP_TOKEN?.trim() ?? '';
  const publicBaseUrl = env.H5_PUBLIC_BASE_URL?.trim()?.replace(/\/$/, '') ?? '';
  const enabledFlag = env.H5_WECHAT_MP_ENABLED === '1';
  const bindPath = env.H5_WECHAT_MP_BIND_PATH?.trim() || '/auth/wechat/authorize?intent=login';
  return {
    enabled: enabledFlag && Boolean(appId && appSecret && token && publicBaseUrl),
    appId,
    appSecret,
    token,
    publicBaseUrl,
    bindPath,
    ackText: env.H5_WECHAT_MP_ACK_TEXT?.trim() || DEFAULT_ACK_TEXT,
    progressText: env.H5_WECHAT_MP_PROGRESS_TEXT?.trim() || DEFAULT_PROGRESS_TEXT,
    progressDelayMs: Math.max(
      0,
      Number(env.H5_WECHAT_MP_PROGRESS_DELAY_MS ?? DEFAULT_PROGRESS_DELAY_MS),
    ),
    statusText: env.H5_WECHAT_MP_STATUS_TEXT?.trim() || DEFAULT_STATUS_TEXT,
    unsupportedText: env.H5_WECHAT_MP_UNSUPPORTED_TEXT?.trim() || DEFAULT_UNSUPPORTED_TEXT,
    unboundTextPrefix: env.H5_WECHAT_MP_UNBOUND_TEXT_PREFIX?.trim() || DEFAULT_UNBOUND_TEXT,
    tokenUrl: env.H5_WECHAT_MP_TOKEN_URL?.trim() || DEFAULT_WECHAT_TOKEN_URL,
    customerServiceUrl:
      env.H5_WECHAT_MP_CUSTOMER_SERVICE_URL?.trim() || DEFAULT_WECHAT_CUSTOMER_SERVICE_URL,
    jsapiTicketUrl:
      env.H5_WECHAT_MP_JSAPI_TICKET_URL?.trim() ||
      deriveWechatEndpointFromUrl(env.H5_WECHAT_MP_TOKEN_URL?.trim(), '/cgi-bin/ticket/getticket') ||
      DEFAULT_WECHAT_JSAPI_TICKET_URL,
  };
}

export function verifyWechatMpSignature({ token, timestamp, nonce, signature }) {
  const digest = crypto
    .createHash('sha1')
    .update([token, timestamp, nonce].sort().join(''))
    .digest('hex');
  return digest === String(signature ?? '');
}

export function buildWechatTextReply({
  toUserName,
  fromUserName,
  content,
  createTime = Math.floor(Date.now() / 1000),
}) {
  return [
    '<xml>',
    `<ToUserName><![CDATA[${toUserName ?? ''}]]></ToUserName>`,
    `<FromUserName><![CDATA[${fromUserName ?? ''}]]></FromUserName>`,
    `<CreateTime>${Number(createTime) || Math.floor(Date.now() / 1000)}</CreateTime>`,
    '<MsgType><![CDATA[text]]></MsgType>',
    `<Content><![CDATA[${String(content ?? '')}]]></Content>`,
    '</xml>',
  ].join('');
}

export function createWechatMpService({
  config,
  userAuth,
  apiFetch,
  sessionApiFetch = null,
  scheduleService = null,
  wechatFetch = undiciFetch,
  logger = console,
}) {
  if (!config?.enabled) {
    return {
      enabled: false,
    };
  }
  config = {
    ...config,
    tokenUrl: config.tokenUrl || DEFAULT_WECHAT_TOKEN_URL,
    customerServiceUrl: config.customerServiceUrl || DEFAULT_WECHAT_CUSTOMER_SERVICE_URL,
    jsapiTicketUrl: config.jsapiTicketUrl || DEFAULT_WECHAT_JSAPI_TICKET_URL,
    progressText: config.progressText ?? DEFAULT_PROGRESS_TEXT,
    progressDelayMs: Math.max(0, Number(config.progressDelayMs ?? DEFAULT_PROGRESS_DELAY_MS)),
    statusText: config.statusText || DEFAULT_STATUS_TEXT,
  };

  let accessTokenCache = {
    token: null,
    expiresAt: 0,
  };
  let jsapiTicketCache = {
    ticket: null,
    expiresAt: 0,
  };

  const fetchForSession = (sessionId, pathname, init) =>
    sessionApiFetch ? sessionApiFetch(sessionId, pathname, init) : apiFetch(pathname, init);

  const buildBindUrl = () => {
    if (/^https?:\/\//i.test(config.bindPath)) return config.bindPath;
    const path = config.bindPath.startsWith('/') ? config.bindPath : `/${config.bindPath}`;
    return `${config.publicBaseUrl}${path}`;
  };

  const verifyRequest = (query = {}) =>
    verifyWechatMpSignature({
      token: config.token,
      timestamp: String(query.timestamp ?? ''),
      nonce: String(query.nonce ?? ''),
      signature: String(query.signature ?? ''),
    });

  const getStableAccessToken = async () => {
    if (accessTokenCache.token && accessTokenCache.expiresAt > Date.now() + 60_000) {
      return accessTokenCache.token;
    }
    const payload = await readJsonResponse(
      await wechatFetch(config.tokenUrl, {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({
          grant_type: 'client_credential',
          appid: config.appId,
          secret: config.appSecret,
          force_refresh: false,
        }),
      }),
    );
    if (!payload?.access_token) {
      throw new Error(payload?.errmsg || '获取微信 access_token 失败');
    }
    const expiresIn = Number(payload.expires_in ?? 7200);
    accessTokenCache = {
      token: payload.access_token,
      expiresAt: Date.now() + expiresIn * 1000,
    };
    return accessTokenCache.token;
  };

  const getJsapiTicket = async () => {
    if (jsapiTicketCache.ticket && jsapiTicketCache.expiresAt > Date.now() + 60_000) {
      return jsapiTicketCache.ticket;
    }
    const accessToken = await getStableAccessToken();
    const url = new URL(config.jsapiTicketUrl);
    url.searchParams.set('access_token', accessToken);
    url.searchParams.set('type', 'jsapi');
    const payload = await readJsonResponse(
      await wechatFetch(url.toString(), {
        method: 'GET',
        headers: { Accept: 'application/json' },
      }),
    );
    if (Number(payload?.errcode ?? 0) !== 0 || !payload?.ticket) {
      throw new Error(payload?.errmsg || `获取微信 jsapi_ticket 失败 (${payload?.errcode})`);
    }
    const expiresIn = Number(payload.expires_in ?? 7200);
    jsapiTicketCache = {
      ticket: payload.ticket,
      expiresAt: Date.now() + expiresIn * 1000,
    };
    return jsapiTicketCache.ticket;
  };

  const createJsSdkSignature = async (pageUrl) => {
    const normalizedUrl = String(pageUrl ?? '').split('#')[0];
    if (!/^https?:\/\//i.test(normalizedUrl)) {
      throw new Error('签名 URL 必须是完整 http(s) 地址');
    }
    const ticket = await getJsapiTicket();
    const nonceStr = crypto.randomBytes(12).toString('hex');
    const timestamp = Math.floor(Date.now() / 1000);
    const signature = crypto
      .createHash('sha1')
      .update(
        [
          `jsapi_ticket=${ticket}`,
          `noncestr=${nonceStr}`,
          `timestamp=${timestamp}`,
          `url=${normalizedUrl}`,
        ].join('&'),
      )
      .digest('hex');
    return {
      appId: config.appId,
      timestamp,
      nonceStr,
      signature,
      url: normalizedUrl,
      jsApiList: ['startRecord', 'stopRecord', 'onVoiceRecordEnd', 'translateVoice'],
    };
  };

  const sendCustomerServiceText = async (openid, content) => {
    const chunks = splitWechatText(formatWechatOutboundText(content));
    const accessToken = await getStableAccessToken();
    for (const chunk of chunks) {
      const payload = await readJsonResponse(
        await wechatFetch(
          `${config.customerServiceUrl}?access_token=${encodeURIComponent(accessToken)}`,
          {
            method: 'POST',
            headers: { 'Content-Type': 'application/json' },
            body: JSON.stringify({
              touser: openid,
              msgtype: 'text',
              text: {
                content: chunk,
              },
            }),
          },
        ),
      );
      if (Number(payload?.errcode ?? 0) !== 0) {
        throw new Error(payload?.errmsg || `微信客服消息发送失败 (${payload?.errcode})`);
      }
    }
  };

  const sendTextToUser = async (userId, content) => {
    const openid = await userAuth.getWechatOpenidForUser(userId, config.appId);
    if (!openid) {
      throw new Error('用户尚未绑定服务号，无法推送提醒');
    }
    await sendCustomerServiceText(openid, content);
  };

  const ensureWechatAgentSession = async ({
    userId,
    openid,
    forceNew = false,
    userContext = null,
  }) => {
    if (forceNew) {
      await userAuth.clearWechatAgentRoute(config.appId, openid);
    }
    const existingRoute = await userAuth.getWechatAgentRoute(config.appId, openid);
    if (existingRoute?.agentSessionId) {
      return existingRoute.agentSessionId;
    }

    const gate = await userAuth.canUseChat(userId);
    if (!gate.ok) {
      throw new Error(gate.message || '当前用户无法使用聊天能力');
    }

    const workingDir = await userAuth.resolveWorkingDir(userId);
    const sessionPolicy = await userAuth.getAgentSessionPolicy(userId);
    const publishLayout = await userAuth.getUserPublishLayout(userId);
    const started = await readJsonResponse(
      await apiFetch('/agent/start', {
        method: 'POST',
        body: JSON.stringify({
          working_dir: workingDir,
          enable_context_memory: sessionPolicy.enableContextMemory,
          ...(sessionPolicy.extensionOverrides
            ? { extension_overrides: sessionPolicy.extensionOverrides }
            : {}),
        }),
      }),
    );
    const sessionId = started?.id;
    if (!sessionId) {
      throw new Error('公众号专属 Agent 会话创建失败');
    }

    await userAuth.registerAgentSession(userId, sessionId);
    const addressName = resolveWechatAddressName(userContext);
    await reconcileAgentSession(
      (pathname, init) => fetchForSession(sessionId, pathname, init),
      sessionId,
      {
      workingDir,
      sessionPolicy,
      sandboxConstraints: publishLayout?.constraints ?? null,
      userContext: publishLayout
        ? {
            userId,
            displayName: addressName || publishLayout.displayName,
            username: publishLayout.username,
            slug: publishLayout.slug,
          }
        : null,
      tolerateInvalidWorkingDir: true,
      },
    );
    await userAuth.upsertWechatAgentRoute({
      userId,
      appId: config.appId,
      openid,
      agentSessionId: sessionId,
    });
    return sessionId;
  };

  const rememberWechatUserContext = async (sessionId, user) => {
    const addressName = resolveWechatAddressName(user);
    if (!addressName) return;
    try {
      await readJsonResponse(
        await fetchForSession(sessionId, '/agent/harness_remember', {
          method: 'POST',
          body: JSON.stringify({
            sessionId,
            title: '微信服务号用户',
            content: [
              `当前服务号用户名称：${addressName}`,
              '这是通过微信 openid 在 H5 绑定库中查询到的用户名称。',
              '回复时可以自然使用该名称称呼用户，不要使用 openid 或 wx_ 开头的内部用户名。',
            ].join('\n'),
          }),
        }),
      );
      await readJsonResponse(
        await fetchForSession(sessionId, '/agent/harness_bootstrap', {
          method: 'POST',
          body: JSON.stringify({ sessionId, force: true }),
        }),
      );
    } catch (err) {
      logger.warn?.('WeChat MP user context remember failed:', err);
    }
  };

  const runTextMessage = async ({ inbound, user }) => {
    const forceNew = isTopicResetIntent(inbound.content);
    let sessionId = await ensureWechatAgentSession({
      userId: user.userId,
      openid: inbound.fromUserName,
      userContext: user,
      forceNew,
    });
    await rememberWechatUserContext(sessionId, user);
    let finished = false;
    let progressTimer = null;
    if (config.progressDelayMs > 0 && config.progressText) {
      progressTimer = setTimeout(() => {
        if (finished) return;
        sendCustomerServiceText(inbound.fromUserName, config.progressText).catch((err) => {
          logger.warn?.('WeChat MP progress reply failed:', err);
        });
      }, config.progressDelayMs);
      progressTimer.unref?.();
    }

    const requestId = crypto.randomUUID();
    try {
      const reply = await executeSessionReply(
        (pathname, init) => fetchForSession(sessionId, pathname, init),
        sessionId,
        requestId,
        buildWechatAgentPrompt(inbound.content),
      );
      if (reply.tokenState) {
        await userAuth.billSessionUsage(user.userId, sessionId, reply.tokenState, requestId);
      }
      await sendCustomerServiceText(inbound.fromUserName, reply.text);
      return { sessionId };
    } catch (err) {
      const message = err instanceof Error ? err.message : String(err);
      const mayBeStaleSession =
        /403|404|not found|无权访问|session/i.test(message) && sessionId;
      if (mayBeStaleSession) {
        sessionId = await ensureWechatAgentSession({
          userId: user.userId,
          openid: inbound.fromUserName,
          forceNew: true,
          userContext: user,
        });
        await rememberWechatUserContext(sessionId, user);
        const retryId = crypto.randomUUID();
        const reply = await executeSessionReply(
          (pathname, init) => fetchForSession(sessionId, pathname, init),
          sessionId,
          retryId,
          buildWechatAgentPrompt(inbound.content),
        );
        if (reply.tokenState) {
          await userAuth.billSessionUsage(user.userId, sessionId, reply.tokenState, retryId);
        }
        await sendCustomerServiceText(inbound.fromUserName, reply.text);
        return { sessionId };
      }
      throw err;
    } finally {
      finished = true;
      if (progressTimer) clearTimeout(progressTimer);
    }
  };

  const handleScheduleIntent = async ({ inbound, user }) => {
    if (!scheduleService) return null;
    const intent = parseScheduleIntent(inbound.content);
    if (!isScheduleIntent(intent)) return null;

    if (intent.action === 'create_daily_todo_digest') {
      if (intent.needsClarification?.includes('digest_time')) {
        return '可以。你想每天几点收到当天待办记录？比如“每天早上 7 点发给我”。';
      }
      const subscription = await scheduleService.createDailyTodoDigest({
        userId: user.userId,
        hour: intent.hour,
        minute: intent.minute,
        timezone: process.env.H5_DEFAULT_TIMEZONE || 'Asia/Shanghai',
        channel: 'wechat',
        sourceChannel: 'wechat',
        sourceMessageId: inbound.msgId || null,
        sourceText: inbound.content,
      });
      const minuteText = subscription.minute === 0 ? '' : `${String(subscription.minute).padStart(2, '0')}分`;
      return `已设置：我会每天早上 ${subscription.hour}点${minuteText} 通过服务号把当天待办记录发给你。`;
    }

    if (intent.action === 'query_schedule') {
      const text = await scheduleService.buildTodoDigestText({
        userId: user.userId,
        timezone: process.env.H5_DEFAULT_TIMEZONE || 'Asia/Shanghai',
      });
      return text;
    }

    return null;
  };

  const handleInboundMessage = async (bodyText, query = {}) => {
    if (!verifyRequest(query)) {
      return { ok: false, status: 403, body: 'invalid signature' };
    }
    const inbound = parseWechatMessage(String(bodyText ?? ''));
    if (!inbound.fromUserName || !inbound.toUserName || !inbound.msgType) {
      return { ok: false, status: 400, body: 'invalid xml' };
    }
    if (String(query.encrypt_type ?? '').toLowerCase() === 'aes' || inbound.encrypt) {
      return {
        ok: true,
        status: 200,
        contentType: 'application/xml; charset=utf-8',
        body: buildWechatTextReply({
          toUserName: inbound.fromUserName,
          fromUserName: inbound.toUserName,
          content: '当前公众号回调需使用明文模式接入，请联系管理员切换后再试。',
        }),
      };
    }

    if (inbound.msgType === 'event' && inbound.event === 'subscribe') {
      return {
        ok: true,
        status: 200,
        contentType: 'application/xml; charset=utf-8',
        body: buildWechatTextReply({
          toUserName: inbound.fromUserName,
          fromUserName: inbound.toUserName,
          content: `${config.unboundTextPrefix}\n${buildBindUrl()}`,
        }),
      };
    }

    if (inbound.msgType !== 'text' || !inbound.content.trim()) {
      return {
        ok: true,
        status: 200,
        contentType: 'application/xml; charset=utf-8',
        body: buildWechatTextReply({
          toUserName: inbound.fromUserName,
          fromUserName: inbound.toUserName,
          content: config.unsupportedText,
        }),
      };
    }

    const boundUser = await userAuth.findWechatUserByOpenid(config.appId, inbound.fromUserName);
    if (!boundUser) {
      return {
        ok: true,
        status: 200,
        contentType: 'application/xml; charset=utf-8',
        body: buildWechatTextReply({
          toUserName: inbound.fromUserName,
          fromUserName: inbound.toUserName,
          content: `${config.unboundTextPrefix}\n${buildBindUrl()}`,
        }),
      };
    }
    if (boundUser.status === 'disabled') {
      return {
        ok: true,
        status: 200,
        contentType: 'application/xml; charset=utf-8',
        body: buildWechatTextReply({
          toUserName: inbound.fromUserName,
          fromUserName: inbound.toUserName,
          content: '当前账号不可用，请联系管理员处理。',
        }),
      };
    }

    if (isQuestionStatusProbe(inbound.content)) {
      return {
        ok: true,
        status: 200,
        contentType: 'application/xml; charset=utf-8',
        body: buildWechatTextReply({
          toUserName: inbound.fromUserName,
          fromUserName: inbound.toUserName,
          content: buildStatusText(boundUser, config.statusText),
        }),
      };
    }

    if (isSimpleGreeting(inbound.content)) {
      return {
        ok: true,
        status: 200,
        contentType: 'application/xml; charset=utf-8',
        body: buildWechatTextReply({
          toUserName: inbound.fromUserName,
          fromUserName: inbound.toUserName,
          content: buildGreetingText(boundUser),
        }),
      };
    }

    if (isConnectivityTest(inbound.content)) {
      return {
        ok: true,
        status: 200,
        contentType: 'application/xml; charset=utf-8',
        body: buildWechatTextReply({
          toUserName: inbound.fromUserName,
          fromUserName: inbound.toUserName,
          content: buildConnectivityTestReply(boundUser),
        }),
      };
    }

    if (inbound.msgId && typeof userAuth.recordWechatMpMessage === 'function') {
      const recorded = await userAuth.recordWechatMpMessage({
        appId: config.appId,
        openid: inbound.fromUserName,
        msgId: inbound.msgId,
      });
      if (!recorded?.inserted) {
        return {
          ok: true,
          status: 200,
          contentType: 'application/xml; charset=utf-8',
          body: buildWechatTextReply({
            toUserName: inbound.fromUserName,
            fromUserName: inbound.toUserName,
            content: buildStatusText(boundUser, config.statusText),
          }),
        };
      }
    }

    const scheduleReply = await handleScheduleIntent({ inbound, user: boundUser });
    if (scheduleReply) {
      if (inbound.msgId && typeof userAuth.finishWechatMpMessage === 'function') {
        await userAuth.finishWechatMpMessage({
          appId: config.appId,
          openid: inbound.fromUserName,
          msgId: inbound.msgId,
          status: 'done',
          agentSessionId: null,
        });
      }
      return {
        ok: true,
        status: 200,
        contentType: 'application/xml; charset=utf-8',
        body: buildWechatTextReply({
          toUserName: inbound.fromUserName,
          fromUserName: inbound.toUserName,
          content: scheduleReply,
        }),
      };
    }

    const task = runTextMessage({
      inbound,
      user: boundUser,
    })
      .then(async ({ sessionId } = {}) => {
        if (inbound.msgId && typeof userAuth.finishWechatMpMessage === 'function') {
          await userAuth.finishWechatMpMessage({
            appId: config.appId,
            openid: inbound.fromUserName,
            msgId: inbound.msgId,
            status: 'done',
            agentSessionId: sessionId,
          });
        }
      })
      .catch(async (err) => {
        if (inbound.msgId && typeof userAuth.finishWechatMpMessage === 'function') {
          await userAuth.finishWechatMpMessage({
            appId: config.appId,
            openid: inbound.fromUserName,
            msgId: inbound.msgId,
            status: 'failed',
          }).catch(() => {});
        }
        logger.error?.('WeChat MP background reply failed:', err);
        return sendCustomerServiceText(
          inbound.fromUserName,
          `这次转发到专属 Agent 失败了：${err instanceof Error ? err.message : String(err)}`,
        ).catch(() => {});
      });

    return {
      ok: true,
      status: 200,
      contentType: 'application/xml; charset=utf-8',
      body: buildWechatTextReply({
        toUserName: inbound.fromUserName,
        fromUserName: inbound.toUserName,
        content: config.ackText,
      }),
      task,
    };
  };

  const getRouteStatusForUser = async (userId) => {
    const openid = await userAuth.getWechatOpenidForUser(userId, config.appId);
    if (!openid) {
      return {
        enabled: true,
        bound: false,
        appId: config.appId,
        openid: null,
        agentSessionId: null,
        routeStatus: null,
        updatedAt: null,
      };
    }
    const route = await userAuth.getWechatAgentRoute(config.appId, openid);
    return {
      enabled: true,
      bound: true,
      appId: config.appId,
      openid,
      agentSessionId: route?.agentSessionId ?? null,
      routeStatus: route?.status ?? null,
      updatedAt: route?.updatedAt ?? null,
    };
  };

  const recreateRouteForUser = async (userId) => {
    const openid = await userAuth.getWechatOpenidForUser(userId, config.appId);
    if (!openid) {
      return {
        ok: false,
        message: '当前账号尚未绑定该服务号',
      };
    }
    const sessionId = await ensureWechatAgentSession({
      userId,
      openid,
      forceNew: true,
    });
    const route = await userAuth.getWechatAgentRoute(config.appId, openid);
    return {
      ok: true,
      route: {
        enabled: true,
        bound: true,
        appId: config.appId,
        openid,
        agentSessionId: sessionId,
        routeStatus: route?.status ?? 'active',
        updatedAt: route?.updatedAt ?? null,
      },
    };
  };

  return {
    enabled: true,
    verifyRequest,
    handleInboundMessage,
    getRouteStatusForUser,
    recreateRouteForUser,
    createJsSdkSignature,
    sendTextToUser,
  };
}
