const Lark = require("@larksuiteoapi/node-sdk");

const appId = requireEnv("FEISHU_APP_ID");
const appSecret = requireEnv("FEISHU_APP_SECRET");
const connectionId = requireEnv("FEISHU_CONNECTION_ID");
const channelId = requireEnv("FEISHU_CHANNEL_ID");
const accountId = process.env.FEISHU_ACCOUNT_ID || `feishu:${appId}`;
const bridgeBaseUrl = requireEnv("BEAVER_BRIDGE_BASE_URL").replace(/\/+$/, "");
const bridgeToken = requireEnv("BEAVER_BRIDGE_TOKEN");
const domain = (process.env.FEISHU_DOMAIN || "feishu").toLowerCase() === "lark"
  ? Lark.Domain.Lark
  : Lark.Domain.Feishu;

const wsClient = new Lark.WSClient({
  appId,
  appSecret,
  domain,
  loggerLevel: Lark.LoggerLevel.info,
  onReady: () => log("feishu_ws_ready", {}),
  onError: (error) => log("feishu_ws_error", { error: redact(String(error && error.message ? error.message : error)) }),
  onReconnecting: () => log("feishu_ws_reconnecting", {}),
  onReconnected: () => log("feishu_ws_reconnected", {}),
  handshakeTimeoutMs: 15000,
  wsConfig: { pingTimeout: 10 },
});

const dispatcher = new Lark.EventDispatcher({}).register({
  "im.message.receive_v1": async (data) => {
    const event = bridgeEventFromFeishu(data);
    log("feishu_inbound_message", {
      connectionId,
      eventId: event.eventId,
      messageId: event.messageId,
      peerId: event.peerId,
      textLength: event.content.length,
    });
    await postJson(`${bridgeBaseUrl}/api/channel-connector-bridge/events`, event);
  },
});

process.on("SIGTERM", () => {
  wsClient.close({ force: true });
  process.exit(0);
});
process.on("SIGINT", () => {
  wsClient.close({ force: true });
  process.exit(0);
});

wsClient.start({ eventDispatcher: dispatcher }).catch((error) => {
  log("feishu_ws_start_failed", { error: redact(String(error && error.message ? error.message : error)) });
  process.exit(1);
});

setInterval(() => {
  if (typeof wsClient.getConnectionStatus === "function") {
    log("feishu_ws_status", wsClient.getConnectionStatus());
  }
}, 60000).unref();

function bridgeEventFromFeishu(data) {
  const message = objectValue(data.message);
  const sender = objectValue(data.sender);
  const senderId = objectValue(sender.sender_id);
  const peerId = stringValue(senderId.open_id || senderId.user_id || "");
  const messageId = stringValue(message.message_id || randomId());
  const eventId = stringValue(data.event_id || data.eventId || `${channelId}:${messageId}`);
  return {
    eventId,
    timestamp: new Date().toISOString(),
    deliveryAttempt: 1,
    connectionId,
    channelId,
    kind: "feishu",
    accountId,
    peerId,
    peerType: message.chat_type === "group" ? "group" : "dm",
    userId: peerId,
    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,
    },
  };
}

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;
}

async function postJson(url, payload) {
  const response = await fetch(url, {
    method: "POST",
    headers: {
      "Authorization": `Bearer ${bridgeToken}`,
      "Content-Type": "application/json",
    },
    body: JSON.stringify(payload),
  });
  if (!response.ok) {
    const text = await response.text();
    throw new Error(`bridge post failed ${response.status}: ${text.slice(0, 300)}`);
  }
}

function requireEnv(name) {
  const value = process.env[name];
  if (!value) {
    throw new Error(`${name} is required`);
  }
  return value;
}

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)}`;
}

function log(event, fields) {
  console.log(JSON.stringify({ event, ...fields }));
}

function redact(text) {
  return text
    .replace(appSecret, "***")
    .replace(bridgeToken, "***");
}
