import { parseBooleanToken } from "../boolean.js";
import { createLogger } from "../logger/index.js";
const logger = createLogger("shared").child({ component: "profiler" });
const ROUND_PRECISION = 1e3;
const NS_PER_MS = BigInt(1e6);
const BIGINT_ZERO = BigInt(0);
function now() {
  return process.hrtime.bigint();
}
function roundDuration(ns) {
  if (ns <= BIGINT_ZERO) return 0;
  const ms = Number(ns) / Number(NS_PER_MS);
  return Math.round(ms * ROUND_PRECISION) / ROUND_PRECISION;
}
function mergeExtra(base, extra) {
  if (!base && !extra) return void 0;
  if (!base) return extra ? { ...extra } : void 0;
  if (!extra) return base ? { ...base } : void 0;
  return { ...base, ...extra };
}
function serializeNode(node) {
  const base = {
    label: node.label,
    durationMs: roundDuration(node.durationNs)
  };
  if (node.extra && Object.keys(node.extra).length > 0) {
    base.extra = node.extra;
  }
  base.type = node.type;
  if (node.type === "span" && node.children.length > 0) {
    base.children = node.children.map(serializeNode);
  }
  return base;
}
class ProfilerImpl {
  constructor(options) {
    this.closed = false;
    this.root = options.root;
    this.span = options.span;
    this.parent = options.parent;
    this.lastTimestamp = options.span ? options.span.lastTimestamp : options.root.lastTimestamp;
  }
  get enabled() {
    return this.root.enabled;
  }
  mark(label, extra) {
    if (!this.enabled || this.closed) return;
    const timestamp = now();
    const duration = timestamp - this.lastTimestamp;
    const node = {
      type: "mark",
      label,
      start: timestamp,
      durationNs: duration >= BIGINT_ZERO ? duration : BIGINT_ZERO,
      extra: extra ? { ...extra } : void 0
    };
    this.appendNode(node);
    this.updateTimestamps(timestamp);
  }
  section(label, extra) {
    if (!this.enabled || this.closed) {
      return disabledSection;
    }
    const child = this.child(label, extra);
    return {
      end: (childExtra) => child.end(childExtra)
    };
  }
  async measure(label, fn, extra) {
    if (!this.enabled || this.closed) {
      return Promise.resolve(fn());
    }
    const child = this.child(label);
    try {
      const result = await Promise.resolve(fn());
      const payload = typeof extra === "function" ? extra(result) : extra;
      child.end(payload);
      return result;
    } catch (err) {
      child.end({ error: err instanceof Error ? err.message : String(err) });
      throw err;
    }
  }
  record(label, durationMs, extra) {
    if (!this.enabled || this.closed) return;
    const safeDurationMs = Number.isFinite(durationMs) && durationMs > 0 ? durationMs : 0;
    const durationNs = BigInt(Math.round(safeDurationMs * Number(NS_PER_MS)));
    const node = {
      type: "record",
      label,
      start: this.lastTimestamp,
      durationNs: durationNs >= BIGINT_ZERO ? durationNs : BIGINT_ZERO,
      extra: extra ? { ...extra } : void 0
    };
    this.appendNode(node);
    this.updateTimestamps(this.lastTimestamp + node.durationNs);
  }
  child(label, extra) {
    if (!this.enabled || this.closed) return disabledProfiler;
    const timestamp = now();
    const span = {
      type: "span",
      label,
      start: timestamp,
      durationNs: BIGINT_ZERO,
      extra: extra ? { ...extra } : void 0,
      children: [],
      lastTimestamp: timestamp,
      closed: false
    };
    this.appendNode(span);
    this.updateTimestamps(timestamp);
    return new ProfilerImpl({ root: this.root, span, parent: this });
  }
  end(extra) {
    if (!this.enabled || this.closed) return;
    const current = now();
    if (this.span) {
      this.closeSpan(current, extra);
    } else {
      this.closeRoot(current, extra);
    }
  }
  appendNode(node) {
    if (this.span) {
      this.span.children.push(node);
      this.span.lastTimestamp = node.start;
    } else {
      this.root.nodes.push(node);
    }
  }
  updateTimestamps(newTimestamp) {
    if (this.span) {
      this.span.lastTimestamp = newTimestamp;
    } else {
      this.root.lastTimestamp = newTimestamp;
    }
    this.lastTimestamp = newTimestamp;
  }
  closeSpan(closedAt, extra) {
    if (!this.span || this.span.closed) return;
    const effectiveEnd = closedAt >= this.span.start ? closedAt : this.span.start;
    this.span.durationNs = effectiveEnd - this.span.start;
    this.span.extra = mergeExtra(this.span.extra, extra);
    this.span.closed = true;
    this.closed = true;
    this.updateTimestamps(effectiveEnd);
    if (this.parent) {
      this.parent.updateTimestamps(effectiveEnd);
    } else {
      this.root.lastTimestamp = effectiveEnd;
    }
  }
  closeRoot(closedAt, extra) {
    if (this.root.closed) return;
    const endTime = closedAt >= this.root.startedAt ? closedAt : this.root.startedAt;
    this.root.lastTimestamp = endTime;
    this.root.meta = mergeExtra(this.root.meta, extra);
    this.root.closed = true;
    this.closed = true;
    const totalNs = endTime - this.root.startedAt;
    const snapshot = {
      scope: this.root.scope,
      target: this.root.target,
      totalMs: roundDuration(totalNs),
      tree: this.root.nodes.map(serializeNode)
    };
    if (this.root.meta && Object.keys(this.root.meta).length > 0) {
      snapshot.meta = this.root.meta;
    }
    try {
      logger.info("Profiler snapshot", { label: this.root.loggerLabel, snapshot });
    } catch {
    }
  }
}
const disabledSection = { end: () => {
} };
const disabledProfiler = {
  get enabled() {
    return false;
  },
  mark: () => {
  },
  section: () => disabledSection,
  measure: async (_label, fn) => Promise.resolve(fn()),
  record: () => {
  },
  child: () => disabledProfiler,
  end: () => {
  }
};
function createProfiler(options) {
  if (options.enabled === false) return disabledProfiler;
  const startedAt = now();
  const enabledFlag = options.enabled !== void 0 ? options.enabled : true;
  const root = {
    scope: options.scope,
    target: options.target,
    label: options.label ?? options.scope,
    loggerLabel: options.loggerLabel ?? "[profile]",
    enabled: enabledFlag,
    startedAt,
    lastTimestamp: startedAt,
    nodes: [],
    closed: false,
    meta: options.meta ? { ...options.meta } : void 0
  };
  return new ProfilerImpl({ root });
}
function normalizeProfilerTokens(input) {
  if (!input) return [];
  return input.split(",").map((token) => token.trim().toLowerCase()).filter((token) => token.length > 0);
}
function profilerMatches(target, tokens) {
  if (!tokens.length) return false;
  const lower = target.toLowerCase();
  return tokens.some((token) => {
    if (token === "*" || token === "all" || parseBooleanToken(token) === true) return true;
    if (token.endsWith("*")) {
      const prefix = token.slice(0, -1);
      return prefix.length === 0 ? true : lower.startsWith(prefix);
    }
    return token === lower;
  });
}
const DEFAULT_ENV_KEYS = [
  "OM_PROFILE",
  "NEXT_PUBLIC_OM_PROFILE",
  "OM_CRUD_PROFILE",
  "NEXT_PUBLIC_OM_CRUD_PROFILE",
  "OM_QE_PROFILE",
  "NEXT_PUBLIC_OM_QE_PROFILE"
];
function resolveProfilerTokens(envKeys = DEFAULT_ENV_KEYS) {
  const tokens = [];
  envKeys.forEach((key) => {
    const raw = process.env[key];
    if (!raw) return;
    const parsed = normalizeProfilerTokens(raw);
    if (parsed.length) tokens.push(...parsed);
  });
  return Array.from(new Set(tokens));
}
function shouldEnableProfiler(target, options) {
  const tokens = options?.tokens ?? resolveProfilerTokens(options?.envKeys);
  if (!tokens.length) return false;
  return profilerMatches(target, tokens);
}
const disabledProfilerInstance = disabledProfiler;
export {
  createProfiler,
  disabledProfilerInstance,
  normalizeProfilerTokens,
  profilerMatches,
  resolveProfilerTokens,
  shouldEnableProfiler
};
//# sourceMappingURL=index.js.map
