const clients = new Map();

function subscribe(req, res, options = {}) {
  const id = `${Date.now()}:${Math.random().toString(16).slice(2)}`;
  const user = req.session?.user || null;
  const scopes = new Set(Array.isArray(options.scopes) ? options.scopes.filter(Boolean) : []);
  const client = {
    id,
    res,
    user,
    scopes,
    connectedAt: Date.now(),
    validate: typeof options.validate === "function" ? options.validate : null
  };
  clients.set(id, client);

  res.writeHead(200, {
    "Content-Type": "text/event-stream",
    "Cache-Control": "no-store, no-transform",
    Connection: "keep-alive",
    "X-Accel-Buffering": "no"
  });
  send(client, "server:status", {
    status: "connected",
    message: "Lumi event stream connected.",
    connected_at: client.connectedAt
  });
  for (const initial of options.initialEvents || []) {
    if (initial?.event) send(client, initial.event, initial.payload || {});
  }

  const keepAlive = setInterval(() => {
    if (!clientIsValid(client)) return disconnect(client);
    send(client, "server:status", { status: "heartbeat", at: Date.now() });
  }, 25000);

  req.on("close", () => {
    clearInterval(keepAlive);
    clients.delete(id);
  });
}

function publish(event, payload = {}, options = {}) {
  let delivered = 0;
  for (const client of clients.values()) {
    if (!clientIsValid(client)) {
      disconnect(client);
      continue;
    }
    if (!canReceive(client.user, options, client)) continue;
    send(client, event, payload);
    delivered += 1;
  }
  return delivered;
}

function clientIsValid(client) {
  if (!client?.validate) return true;
  try { return client.validate() !== false; }
  catch { return false; }
}

function disconnect(client) {
  clients.delete(client.id);
  try { client.res.end(); } catch {}
}

function send(client, event, payload) {
  try {
    client.res.write(`event: ${event}\n`);
    client.res.write(`data: ${JSON.stringify({ ...payload, event, at: Date.now() })}\n\n`);
  } catch {
    clients.delete(client.id);
  }
}

function canReceive(user, options = {}, client = null) {
  if (client?.scopes?.size && !options.scope) return false;
  if (options.scope && !client?.scopes?.has(options.scope)) return false;
  const role = options.role || "public";
  if (role === "public") return true;
  if (!user) return false;
  if (role === "user") return true;
  if (role === "mod") return Boolean(user.isMod || user.isAdmin);
  if (role === "admin") return Boolean(user.isAdmin);
  return false;
}

module.exports = {
  publishWebEvent: publish,
  subscribeWebEvents: subscribe
};
