/**
 * MAIN-world tap present on every http(s) page from document_start, before
 * any page script runs: wraps console.error/warn and global error handlers
 * so the recorder can attach a console dump to the report.
 *
 * It must live in the MAIN world — the page calls *its own* console object,
 * which the isolated content-script world never sees. And since MAIN-world
 * scripts have no extension messaging, everything goes out through
 * window.postMessage to the relay content script.
 */
interface BugtrailWindow {
  __bugtrailTap?: boolean;
}

const w = window as unknown as BugtrailWindow;
// the page could re-inject us (SPA frameworks re-running scripts) — wrap once
if (!w.__bugtrailTap) {
  w.__bugtrailTap = true;

  const MAX_TEXT = 2000;

  /** Formats a console argument; errors keep their message, objects go JSON. */
  function format(arg: unknown, depth = 0): string {
    if (arg == null) return String(arg);
    if (typeof arg === "string") return arg;
    if (arg instanceof Error) return arg.stack ?? `${arg.name}: ${arg.message}`;
    if (depth === 0 && (typeof arg === "object" || typeof arg === "function")) {
      try {
        return JSON.stringify(arg, (_key, value) => (typeof value === "bigint" ? String(value) : value)) ?? String(arg);
      } catch {
        // circular or throwing toJSON — fall back to the string form
        return String(arg);
      }
    }
    return String(arg);
  }

  function post(level: "error" | "warning", text: string) {
    try {
      window.postMessage({ source: "bugtrail-tap", type: "console", level, text: text.slice(0, MAX_TEXT), at: Date.now() }, window.location.origin);
    } catch {
      // structured clone can refuse exotic values — never break the page
    }
  }

  const wrap = (method: "error" | "warn", level: "error" | "warning") => {
    const original = console[method].bind(console);
    console[method] = (...args: unknown[]) => {
      try {
        post(level, args.map((arg) => format(arg)).join(" "));
      } catch {
        // never break the page's own logging
      }
      original(...args);
    };
  };
  wrap("error", "error");
  wrap("warn", "warning");

  window.addEventListener("error", (event) => {
    // resources (img/script) failing also fire "error" — only real JS errors carry a message
    if (event instanceof ErrorEvent && event.message) {
      post("error", `Uncaught ${event.message} (${event.filename}:${event.lineno})`);
    }
  });
  window.addEventListener("unhandledrejection", (event) => {
    post("error", `Unhandled rejection: ${format((event as PromiseRejectionEvent).reason)}`);
  });
}