function makeTimings(type, desc) {
  const timings = {
    [type]: [{ desc, start: performance.now() }]
  };
  Object.defineProperty(timings, "toString", {
    value: function() {
      return getServerTimeHeader(timings);
    },
    enumerable: false
  });
  return timings;
}
function createTimer(type, desc) {
  const start = performance.now();
  return {
    end(timings) {
      let timingType = timings[type];
      if (!timingType) {
        timingType = timings[type] = [];
      }
      timingType.push({ desc, time: performance.now() - start });
    }
  };
}
async function time(fn, {
  type,
  desc,
  timings
}) {
  const timer = createTimer(type, desc);
  const promise = typeof fn === "function" ? fn() : fn;
  if (!timings)
    return promise;
  const result = await promise;
  timer.end(timings);
  return result;
}
function getServerTimeHeader(timings) {
  if (!timings)
    return "";
  return Object.entries(timings).map(([key, timingInfos]) => {
    const dur = timingInfos.reduce((acc, timingInfo) => {
      const time2 = timingInfo.time ?? performance.now() - timingInfo.start;
      return acc + time2;
    }, 0).toFixed(1);
    const desc = timingInfos.map((t) => t.desc).filter(Boolean).join(" & ");
    return [
      key.replaceAll(/(:| |@|=|;|,|\/|\\)/g, "_"),
      desc ? `desc=${JSON.stringify(desc)}` : null,
      `dur=${dur}`
    ].filter(Boolean).join(";");
  }).join(",");
}
function combineServerTimings(headers1, headers2) {
  const newHeaders = new Headers(headers1);
  newHeaders.append("Server-Timing", headers2.get("Server-Timing") ?? "");
  return newHeaders.get("Server-Timing") ?? "";
}
function cachifiedTimingReporter(timings) {
  if (!timings)
    return;
  return ({ key }) => {
    const cacheRetrievalTimer = createTimer(
      `cache:${key}`,
      `${key} cache retrieval`
    );
    let getFreshValueTimer;
    return (event) => {
      switch (event.name) {
        case "getFreshValueStart":
          getFreshValueTimer = createTimer(
            `getFreshValue:${key}`,
            `request forced to wait for a fresh ${key} value`
          );
          break;
        case "getFreshValueSuccess":
          getFreshValueTimer?.end(timings);
          break;
        case "done":
          cacheRetrievalTimer.end(timings);
          break;
      }
    };
  };
}
export {
  cachifiedTimingReporter,
  combineServerTimings,
  getServerTimeHeader,
  makeTimings,
  time
};
