import { emitterFor, httpTransport, CloudEvent } from "cloudevents";
import { nanoid } from "nanoid";
import { isJSONValue } from "../../../shared/src/json.js";
import { must } from "../../../shared/src/must.js";
import { promiseVoid } from "../../../shared/src/resolved-promises.js";
import { sleep } from "../../../shared/src/sleep.js";
import { parse } from "../../../shared/src/valita.js";
import { union, string, number, boolean, record, object } from "@badrap/valita";
const MAX_PUBLISH_ATTEMPTS = 6;
const INITIAL_PUBLISH_BACKOFF_MS = 500;
let publishFn = (lc, { type }) => {
  lc.warn?.(
    `Cannot publish "${type}" event before initEventSink(). This is only expected in unit tests.`
  );
  return promiseVoid;
};
const attributeValueSchema = union(string(), number(), boolean());
const eventSchema = record(attributeValueSchema);
const extensionsObjectSchema = object({ extensions: eventSchema });
function initEventSink(lc, { taskID, cloudEvent }) {
  if (!cloudEvent.sinkEnv) {
    publishFn = (lc2, event) => {
      lc2.info?.(`ZeroEvent: ${event.type}`, event);
      return promiseVoid;
    };
    return;
  }
  let overrides = {};
  if (cloudEvent.extensionOverridesEnv) {
    const strVal = must(process.env[cloudEvent.extensionOverridesEnv]);
    const { extensions } = parse(JSON.parse(strVal), extensionsObjectSchema);
    overrides = extensions;
  }
  function createCloudEvent(data) {
    const { type, time } = data;
    return new CloudEvent({
      id: nanoid(),
      source: taskID,
      type,
      time,
      data,
      ...overrides
    });
  }
  const sinkURI = must(process.env[cloudEvent.sinkEnv]);
  const emit = emitterFor(httpTransport(sinkURI));
  lc.debug?.(`Publishing ZeroEvents to ${sinkURI}`);
  publishFn = async (lc2, event) => {
    const cloudEvent2 = createCloudEvent(event);
    lc2.debug?.(`Publishing CloudEvent: ${cloudEvent2.type}`);
    for (let i = 0; i < MAX_PUBLISH_ATTEMPTS; i++) {
      if (i > 0) {
        await sleep(INITIAL_PUBLISH_BACKOFF_MS * 2 ** (i - 1));
      }
      try {
        await emit(cloudEvent2);
        lc2.info?.(`Published CloudEvent: ${cloudEvent2.type}`, cloudEvent2);
        return;
      } catch (e) {
        lc2.warn?.(`Error publishing ${cloudEvent2.type} (attempt ${i + 1})`, e);
      }
    }
  };
}
function publishEvent(lc, event) {
  void publishFn(lc, event);
}
async function publishCriticalEvent(lc, event) {
  await publishFn(lc, event);
}
function makeErrorDetails(e) {
  const err = e instanceof Error ? e : new Error(String(e));
  const errorDetails = {
    name: err.name,
    message: err.message,
    stack: err.stack,
    cause: err.cause ? makeErrorDetails(err.cause) : void 0
  };
  for (const [field, value] of Object.entries(err)) {
    if (isJSONValue(value, [])) {
      errorDetails[field] = value;
    }
  }
  return errorDetails;
}
export {
  initEventSink,
  makeErrorDetails,
  publishCriticalEvent,
  publishEvent
};
//# sourceMappingURL=events.js.map
