function bridgeEventFromFeishu(data, env) {
  const message = objectValue(data.message);
  const sender = objectValue(data.sender);
  const senderId = objectValue(sender.sender_id);
  const isGroup = message.chat_type === "group";
  const peerId = isGroup
    ? stringValue(message.chat_id || "")
    : stringValue(senderId.open_id || senderId.user_id || "");
  const userId = stringValue(senderId.open_id || senderId.user_id || "");
  const messageId = stringValue(message.message_id || randomId());
  const eventId = stringValue(data.event_id || data.eventId || `${env.channelId}:${messageId}`);
  return {
    eventId,
    timestamp: new Date().toISOString(),
    deliveryAttempt: 1,
    connectionId: env.connectionId,
    channelId: env.channelId,
    kind: "feishu",
    accountId: env.accountId,
    peerId,
    peerType: isGroup ? "group" : "dm",
    userId,
    threadId: stringValue(message.chat_id || "") || null,
    messageId,
    messageType: stringValue(message.message_type || "text"),
    content: extractText(message),
    metadata: {
      chatId: message.chat_id || null,
      rawMessageType: message.message_type || null,
      senderType: sender.sender_type || null,
    },
  };
}

function bridgeEventFromNormalizedMessage(message, env, options = {}) {
  const maxMessageChars = positiveInt(options.maxMessageChars, 20000);
  const content = normalizedContent(message).trim();
  if (!content || content.length > maxMessageChars) {
    return null;
  }
  const isGroup = message.chatType === "group";
  const messageId = stringValue(message.messageId || randomId());
  const chatId = stringValue(message.chatId || "");
  const senderId = stringValue(message.senderId || "");
  const eventId = stringValue(rawEventId(message.raw) || `${env.channelId}:${messageId}`);
  return {
    eventId,
    timestamp: new Date().toISOString(),
    deliveryAttempt: 1,
    connectionId: env.connectionId,
    channelId: env.channelId,
    kind: "feishu",
    accountId: env.accountId,
    peerId: isGroup ? chatId : senderId,
    peerType: isGroup ? "group" : "dm",
    userId: senderId,
    threadId: chatId || null,
    messageId,
    messageType: stringValue(message.rawContentType || "text"),
    content,
    metadata: {
      chatId: chatId || null,
      rawMessageType: message.rawContentType || null,
      senderName: message.senderName || null,
      mentions: Array.isArray(message.mentions) ? message.mentions : [],
      mentionAll: Boolean(message.mentionAll),
      mentionedBot: Boolean(message.mentionedBot),
      resources: Array.isArray(message.resources) ? message.resources : [],
      createTime: message.createTime || null,
      rootId: message.rootId || null,
      threadId: message.threadId || null,
      replyToMessageId: message.replyToMessageId || null,
    },
  };
}

function buildChannelOptions({ appId, appSecret, domain, policy }) {
  const resolvedPolicy = policy || parsePolicyEnv(process.env);
  return {
    appId,
    appSecret,
    domain,
    includeRawEvent: true,
    source: "beaver",
    handshakeTimeoutMs: 15000,
    wsConfig: { pingTimeout: 10 },
    policy: {
      requireMention: resolvedPolicy.requireMentionInGroups,
      respondToMentionAll: resolvedPolicy.respondToMentionAll,
      dmMode: resolvedPolicy.dmMode,
      dmAllowlist: resolvedPolicy.allowFrom,
      groupAllowlist: resolvedPolicy.groupAllowFrom,
    },
    safety: {
      chatQueue: { enabled: true },
      staleMessageWindowMs: positiveInt(resolvedPolicy.staleMessageWindowMs, 10 * 60 * 1000),
      batch: {
        text: {
          delayMs: positiveInt(resolvedPolicy.textBatchDelayMs, 0),
          maxMessages: positiveInt(resolvedPolicy.textBatchMaxMessages, 10),
          maxChars: positiveInt(resolvedPolicy.textBatchMaxChars, resolvedPolicy.maxMessageChars),
        },
      },
    },
    outbound: {
      textChunkLimit: resolvedPolicy.maxMessageChars,
      retry: { maxAttempts: 3, baseDelayMs: 500 },
      ssrfGuard: true,
    },
  };
}

function parsePolicyEnv(env) {
  return {
    requireMentionInGroups: envBool(env.FEISHU_REQUIRE_MENTION_IN_GROUPS, true),
    respondToMentionAll: envBool(env.FEISHU_RESPOND_TO_MENTION_ALL, false),
    dmMode: stringValue(env.FEISHU_DM_MODE || "open") || "open",
    allowFrom: envList(env.FEISHU_ALLOW_FROM || env.FEISHU_DM_ALLOW_FROM || ""),
    groupAllowFrom: envList(env.FEISHU_GROUP_ALLOW_FROM || ""),
    maxMessageChars: positiveInt(env.FEISHU_MAX_MESSAGE_CHARS, 20000),
    textBatchDelayMs: positiveInt(env.FEISHU_TEXT_BATCH_DELAY_MS, 0),
    textBatchMaxMessages: positiveInt(env.FEISHU_TEXT_BATCH_MAX_MESSAGES, 10),
    textBatchMaxChars: positiveInt(env.FEISHU_TEXT_BATCH_MAX_CHARS, 20000),
    staleMessageWindowMs: positiveInt(env.FEISHU_STALE_MESSAGE_WINDOW_MS, 10 * 60 * 1000),
  };
}

function ignoreReason(data) {
  const sender = objectValue(data.sender);
  const senderType = stringValue(sender.sender_type || "").toLowerCase();
  if (senderType && senderType !== "user") {
    return `sender_type:${senderType}`;
  }
  const content = extractText(objectValue(data.message)).trim();
  if (content.startsWith("/feishu")) {
    return "feishu_command";
  }
  return "";
}

function normalizedContent(message) {
  const parts = [stringValue(message.content || "")];
  const resources = Array.isArray(message.resources) ? message.resources : [];
  for (const resource of resources) {
    if (!resource || typeof resource !== "object") {
      continue;
    }
    const type = stringValue(resource.type || "file");
    const fileName = stringValue(resource.fileName || "");
    parts.push(`[${type}${fileName ? `: ${fileName}` : ""}]`);
  }
  return parts.filter((part) => part.trim()).join("\n");
}

function rawEventId(raw) {
  const rawObject = objectValue(raw);
  const header = objectValue(rawObject.header);
  return rawObject.event_id || rawObject.eventId || header.event_id || header.eventId || "";
}

function extractText(message) {
  const content = message.content;
  if (typeof content !== "string") {
    return "";
  }
  try {
    const parsed = JSON.parse(content);
    if (parsed && parsed.text != null) {
      return String(parsed.text);
    }
  } catch (_error) {
    return content;
  }
  return content;
}

function envList(value) {
  return stringValue(value)
    .replace(/\n/g, ",")
    .split(",")
    .map((item) => item.trim())
    .filter(Boolean);
}

function envBool(value, defaultValue) {
  if (value == null || value === "") {
    return defaultValue;
  }
  return ["1", "true", "yes", "on"].includes(String(value).trim().toLowerCase());
}

function positiveInt(value, defaultValue) {
  const number = Number.parseInt(String(value ?? ""), 10);
  return Number.isFinite(number) && number > 0 ? number : defaultValue;
}

function objectValue(value) {
  return value && typeof value === "object" ? value : {};
}

function stringValue(value) {
  return value == null ? "" : String(value);
}

function randomId() {
  return `${Date.now()}-${Math.random().toString(16).slice(2)}`;
}

module.exports = {
  bridgeEventFromFeishu,
  bridgeEventFromNormalizedMessage,
  buildChannelOptions,
  ignoreReason,
  extractText,
  parsePolicyEnv,
};
