/* monkey-patch console methods to prefix messages with file:line for easier logs */
(() => {
  const cwd = process.cwd();
  const cwdRe = new RegExp("^[^/]*" + cwd.replace("/", "\\/") + "/([^:]*:[0-9]*).*$");
  const methods = ["log", "warn", "error", "info", "debug"] as const;

  function getCallerFileLine(): string {
    try {
      // Create an Error to capture stack
      const err = new Error();
      if (!err.stack) return "unknown:0 -";
      const lines = err.stack.split("\n").slice(1);
      // Find the first stack line that is not this file
      for (let i = 0; i < lines.length; i++) {
        const line = lines[i];
        if (!line) continue;
        if (line.indexOf("console-line") !== -1) continue; // skip this helper
        // Try to extract file:line from the line. Use a stricter capture so we
        // don't accidentally include leading whitespace or the 'at' token.
        const m = line.match(/\(?(\S+:\d+:\d+)\)?$/);
        if (m && m[1]) {
          return m[1].trim() + " -";
        }
      }
      // Fallback: try to extract file:line:col from the third stack line even
      // if it contains leading whitespace and the 'at' prefix. If that fails,
      // fall back to the cwd-based replace and trim whitespace.
      const fallback = err.stack.split("\n")[3] || "";
      const m2 = fallback.match(/\(?(\S+:\d+:\d+)\)?$/);
      if (m2 && m2[1]) return m2[1].trim() + " -";
      const replaced = fallback.replace(cwdRe, "$1 -").trim();
      return replaced || "unknown:0 -";
    } catch (e) {
      return "unknown:0 -";
    }
  }

  methods.forEach((method) => {
    const orig = (console as any)[method] || console.log;
    (console as any)[method] = function (...args: any[]) {
      try {
        const prefix = getCallerFileLine();

        // Separate Error objects from other args so we can print their stacks
        // line-by-line with the same prefix. This keeps stack traces intact
        // while ensuring every printed line shows the caller prefix.
        const errorArgs = args.filter((a: any) => a instanceof Error) as Error[];
        const otherArgs = args.filter((a: any) => !(a instanceof Error));

        // Print non-error args in a single call (preserving original formatting)
        const processedOther = otherArgs.map((a: any) => (a instanceof Error ? a.stack || a.toString() : a));
        if (processedOther.length > 0) {
          orig.apply(this, [prefix, ...processedOther]);
        }

        // For each Error, print each line of its stack as a separate prefixed log
        // entry so lines that begin with '    at' are not orphaned.
        errorArgs.forEach((err) => {
          const stack = err.stack || err.toString();
          stack.split("\n").forEach((line) => {
            orig.apply(this, [prefix, line]);
          });
        });
      } catch (e) {
        try {
          orig.apply(this, args);
        } catch (e2) {
          /* swallow */
        }
      }
    };
  });
})();
