import {
  renderFrame
} from "./chunk-32KD5IHZ.js";
import "./chunk-TYEOAFH3.js";
import "./chunk-PZ5AY32C.js";

// src/console/trace.ts
import { promises as fs } from "fs";
import path from "path";
import readline from "readline";
import { createReadStream } from "fs";
var ANSI_CLEAR_HOME = "\x1B[2J\x1B[H";
var ANSI_HIDE_CURSOR = "\x1B[?25l";
var ANSI_SHOW_CURSOR = "\x1B[?25h";
var DEFAULT_REPLAY_DELAY_MS = 2e3;
var MAX_REPLAY_DELAY_MS = 6e4;
var MIN_REPLAY_DELAY_MS = 0;
async function openTraceRecorder(filePath, options = {}) {
  const ensureParentDir = options.ensureParentDir ?? true;
  if (ensureParentDir) {
    const parent = path.dirname(filePath);
    if (parent && parent !== "." && parent !== "/") {
      await fs.mkdir(parent, { recursive: true });
    }
  }
  const handle = await fs.open(filePath, "a");
  let closed = false;
  let lastError = null;
  let writeChain = Promise.resolve();
  const queueWrite = (line) => {
    const next = writeChain.then(async () => {
      if (closed) return;
      try {
        await handle.write(line);
      } catch (err) {
        const msg = describeError(err);
        lastError = msg;
        throw err;
      }
    });
    writeChain = next.catch(() => {
    });
    return next;
  };
  return {
    append: async (snapshot) => {
      if (closed) return;
      let line;
      try {
        line = JSON.stringify(snapshot) + "\n";
      } catch (err) {
        lastError = `serialize failed: ${describeError(err)}`;
        return;
      }
      try {
        await queueWrite(line);
      } catch {
      }
    },
    close: async (signal) => {
      if (closed) return;
      try {
        if (signal) {
          await Promise.race([
            writeChain,
            new Promise((_, reject) => {
              if (signal.aborted) {
                reject(makeAbortError());
                return;
              }
              signal.addEventListener("abort", () => reject(makeAbortError()), {
                once: true
              });
            })
          ]);
        } else {
          await writeChain;
        }
      } catch {
      }
      closed = true;
      try {
        await handle.close();
      } catch (err) {
        lastError = describeError(err);
      }
    },
    getLastError: () => lastError
  };
}
async function replayTrace(filePath, options = {}) {
  const output = options.output ?? process.stdout;
  const speed = normalizeSpeed(options.speed);
  const sleep = options.sleep ?? ((ms) => sleepAbortable(ms, options.signal));
  const manageCursor = options.manageCursor ?? true;
  const nowFn = options.now ?? ((snapshot) => {
    const ms = Date.parse(snapshot.capturedAt);
    return Number.isFinite(ms) ? ms : Date.now();
  });
  const stream = createReadStream(filePath, { encoding: "utf-8" });
  const rl = readline.createInterface({ input: stream, crlfDelay: Infinity });
  let framesRendered = 0;
  let framesSkipped = 0;
  let lastSnapshot = null;
  let prevCapturedMs = null;
  if (manageCursor) safeWrite(output, ANSI_HIDE_CURSOR);
  try {
    for await (const rawLine of rl) {
      if (options.signal?.aborted) break;
      const line = rawLine.trim();
      if (line.length === 0) continue;
      const snapshot = parseSnapshotLine(line);
      if (snapshot === null) {
        framesSkipped += 1;
        continue;
      }
      const capturedMs = Date.parse(snapshot.capturedAt);
      let waitMs = 0;
      if (prevCapturedMs !== null && Number.isFinite(capturedMs)) {
        const rawDelta = capturedMs - prevCapturedMs;
        waitMs = computeReplayDelay(rawDelta, speed);
      } else if (prevCapturedMs !== null) {
        waitMs = computeReplayDelay(DEFAULT_REPLAY_DELAY_MS, speed);
      }
      if (waitMs > 0) {
        try {
          await sleep(waitMs);
        } catch (err) {
          if (isAbortError(err) || options.signal?.aborted) break;
          throw err;
        }
        if (options.signal?.aborted) break;
      }
      let frame;
      try {
        frame = renderFrame({
          snapshot,
          renderError: null,
          now: () => nowFn(snapshot, framesRendered)
        });
      } catch (err) {
        frame = `remnic console replay: render failed: ${describeError(err)}
`;
      }
      safeWrite(output, ANSI_CLEAR_HOME);
      safeWrite(output, frame);
      framesRendered += 1;
      lastSnapshot = snapshot;
      if (Number.isFinite(capturedMs)) prevCapturedMs = capturedMs;
    }
  } finally {
    rl.close();
    stream.close();
    if (manageCursor) safeWrite(output, ANSI_SHOW_CURSOR);
  }
  return { framesRendered, framesSkipped, lastSnapshot };
}
function parseSnapshotLine(line) {
  let parsed;
  try {
    parsed = JSON.parse(line);
  } catch {
    return null;
  }
  if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
    return null;
  }
  return parsed;
}
function computeReplayDelay(rawDeltaMs, speed) {
  if (!Number.isFinite(rawDeltaMs)) return 0;
  if (rawDeltaMs <= 0) return 0;
  if (!Number.isFinite(speed)) return 0;
  const adjusted = rawDeltaMs / speed;
  if (!Number.isFinite(adjusted)) return 0;
  if (adjusted <= MIN_REPLAY_DELAY_MS) return MIN_REPLAY_DELAY_MS;
  if (adjusted > MAX_REPLAY_DELAY_MS) return MAX_REPLAY_DELAY_MS;
  return adjusted;
}
function parseSpeedFlag(raw) {
  if (raw === void 0 || raw === null) return 1;
  const num = typeof raw === "number" ? raw : Number(raw);
  if (!Number.isFinite(num) && num !== Infinity) {
    throw new Error(
      `invalid --speed value: ${JSON.stringify(raw)} (must be a positive number)`
    );
  }
  if (num <= 0) {
    throw new Error(
      `invalid --speed value: ${JSON.stringify(raw)} (must be > 0)`
    );
  }
  return num;
}
function normalizeSpeed(speed) {
  if (speed === void 0) return 1;
  if (!Number.isFinite(speed) && speed !== Infinity) return 1;
  if (speed <= 0) return 1;
  return speed;
}
function sleepAbortable(ms, signal) {
  return new Promise((resolve, reject) => {
    if (signal?.aborted) {
      reject(makeAbortError());
      return;
    }
    const timer = setTimeout(() => {
      if (signal && onAbort) signal.removeEventListener("abort", onAbort);
      resolve();
    }, ms);
    let onAbort = null;
    if (signal) {
      onAbort = () => {
        clearTimeout(timer);
        reject(makeAbortError());
      };
      signal.addEventListener("abort", onAbort, { once: true });
    }
  });
}
function makeAbortError() {
  if (typeof DOMException !== "undefined") {
    return new DOMException("aborted", "AbortError");
  }
  const err = new Error("aborted");
  err.name = "AbortError";
  return err;
}
function isAbortError(err) {
  return typeof err === "object" && err !== null && "name" in err && err.name === "AbortError";
}
async function flushWithTimeout(flush, timeoutMs) {
  const TIMEOUT_SENTINEL = /* @__PURE__ */ Symbol("flush-timeout");
  let error = null;
  let timeoutHandle = null;
  const ac = new AbortController();
  const flushPromise = flush(ac.signal).then(
    () => void 0,
    (err) => {
      error = err;
      return void 0;
    }
  );
  const timeoutPromise = new Promise((resolve) => {
    timeoutHandle = setTimeout(() => resolve(TIMEOUT_SENTINEL), timeoutMs);
    if (typeof timeoutHandle.unref === "function") {
      timeoutHandle.unref();
    }
  });
  try {
    const winner = await Promise.race([
      flushPromise,
      timeoutPromise
    ]);
    if (winner === TIMEOUT_SENTINEL) {
      ac.abort();
      return { flushed: false, timedOut: true, error: null };
    }
    return { flushed: true, timedOut: false, error };
  } finally {
    if (timeoutHandle !== null) {
      clearTimeout(timeoutHandle);
    }
  }
}
function safeWrite(output, chunk) {
  try {
    output.write(chunk);
  } catch {
  }
}
function describeError(err) {
  if (err instanceof Error) return err.message;
  try {
    return String(err);
  } catch {
    return "unknown error";
  }
}
export {
  computeReplayDelay,
  flushWithTimeout,
  isAbortError,
  openTraceRecorder,
  parseSnapshotLine,
  parseSpeedFlag,
  replayTrace,
  sleepAbortable
};
//# sourceMappingURL=trace-C5ETWBEF.js.map