const Lark = require("@larksuiteoapi/node-sdk");
const {
  bridgeEventFromNormalizedMessage,
  buildChannelOptions,
  parsePolicyEnv,
} = require("./feishu_event_utils");

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 policy = parsePolicyEnv(process.env);
const env = { connectionId, channelId, accountId };

const channel = Lark.createLarkChannel(buildChannelOptions({ appId, appSecret, domain, policy }));
channel.on({
  message: async (message) => {
    const event = bridgeEventFromNormalizedMessage(message, env, { maxMessageChars: policy.maxMessageChars });
    if (!event) {
      log("feishu_inbound_ignored", {
        connectionId,
        messageId: message.messageId,
        reason: "empty_or_oversized",
      });
      return;
    }
    log("feishu_inbound_message", {
      connectionId,
      eventId: event.eventId,
      messageId: event.messageId,
      peerId: event.peerId,
      peerType: event.peerType,
      textLength: event.content.length,
    });
    await postJson(`${bridgeBaseUrl}/api/channel-connector-bridge/events`, event);
  },
  reject: (event) => {
    log("feishu_inbound_ignored", {
      connectionId,
      messageId: event.messageId,
      peerId: event.chatId,
      userId: event.senderId,
      reason: event.reason,
    });
  },
  error: (error) => {
    log("feishu_ws_error", {
      connectionId,
      code: error.code || "unknown",
      error: redact(String(error && error.message ? error.message : error)),
    });
  },
  reconnecting: () => log("feishu_ws_reconnecting", { connectionId }),
  reconnected: () => log("feishu_ws_reconnected", { connectionId }),
});

process.on("SIGTERM", () => {
  channel.disconnect().finally(() => process.exit(0));
});
process.on("SIGINT", () => {
  channel.disconnect().finally(() => process.exit(0));
});

channel.connect().then(() => {
  log("feishu_ws_ready", {
    connectionId,
    requireMentionInGroups: policy.requireMentionInGroups,
    dmMode: policy.dmMode,
    groupAllowlistSize: policy.groupAllowFrom.length,
    dmAllowlistSize: policy.allowFrom.length,
  });
}).catch((error) => {
  log("feishu_ws_start_failed", { error: redact(String(error && error.message ? error.message : error)) });
  process.exit(1);
});

setInterval(() => {
  const status = channel.getConnectionStatus();
  if (status) {
    log("feishu_ws_status", { connectionId, ...status });
  }
}, 60000).unref();

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 log(event, fields) {
  console.log(JSON.stringify({ event, ...fields }));
}

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