(() => {
  console.log("[CoinGate Reader] interceptor loaded");

  /*
   * ---------------------------------------------------------
   * 1. Intercept fetch()
   * ---------------------------------------------------------
   */

  const originalFetch = window.fetch;

  window.fetch = async function (...args) {
    try {
      const request = args[0];

      const url =
        typeof request === "string"
          ? request
          : request?.url;

      if (url && url.includes("/api/events")) {
        console.log(
          "[CoinGate Reader] /api/events fetch:",
          url
        );

        let body = args[1]?.body;

        if (body) {
          inspectRequestBody(body, "fetch");
        }
      }
    } catch (err) {
      console.error(
        "[CoinGate Reader] fetch inspection error:",
        err
      );
    }

    return originalFetch.apply(this, args);
  };


  /*
   * ---------------------------------------------------------
   * 2. Intercept XMLHttpRequest
   * ---------------------------------------------------------
   */

  const originalOpen = XMLHttpRequest.prototype.open;
  const originalSend = XMLHttpRequest.prototype.send;

  XMLHttpRequest.prototype.open = function (
    method,
    url,
    ...rest
  ) {
    this.__coinGateUrl = url;
    this.__coinGateMethod = method;

    return originalOpen.call(
      this,
      method,
      url,
      ...rest
    );
  };

  XMLHttpRequest.prototype.send = function (body) {
    try {
      const url = this.__coinGateUrl;

      if (
        url &&
        String(url).includes("/api/events")
      ) {
        console.log(
          "[CoinGate Reader] /api/events XHR:",
          url
        );

        if (body) {
          inspectRequestBody(body, "xhr");
        }
      }
    } catch (err) {
      console.error(
        "[CoinGate Reader] XHR inspection error:",
        err
      );
    }

    return originalSend.call(this, body);
  };


  /*
   * ---------------------------------------------------------
   * 3. Intercept clipboard
   * ---------------------------------------------------------
   */

  if (navigator.clipboard?.writeText) {
    const originalWriteText =
      navigator.clipboard.writeText.bind(
        navigator.clipboard
      );

    navigator.clipboard.writeText = function (text) {
      console.log(
        "[CoinGate Reader] Clipboard:",
        text
      );

      sendToExtension({
        type: "CLIPBOARD",
        value: text
      });

      return originalWriteText(text);
    };
  }


  /*
   * ---------------------------------------------------------
   * Inspect request body
   * ---------------------------------------------------------
   */

  function inspectRequestBody(body, source) {
    try {
      if (typeof body === "string") {
        console.log(
          `[CoinGate Reader] ${source} body:`,
          body
        );

        sendToExtension({
          type: "EVENT_REQUEST",
          source,
          body
        });

        try {
          const json = JSON.parse(body);

          inspectObject(json);
        } catch {
          // Not JSON.
        }

        return;
      }

      if (body instanceof URLSearchParams) {
        const value = Object.fromEntries(body.entries());

        console.log(
          "[CoinGate Reader] URLSearchParams:",
          value
        );

        sendToExtension({
          type: "EVENT_REQUEST",
          source,
          body: value
        });

        inspectObject(value);

        return;
      }

      if (body instanceof FormData) {
        const value = {};

        for (const [key, val] of body.entries()) {
          value[key] = val;
        }

        console.log(
          "[CoinGate Reader] FormData:",
          value
        );

        sendToExtension({
          type: "EVENT_REQUEST",
          source,
          body: value
        });

        inspectObject(value);

        return;
      }

      console.log(
        "[CoinGate Reader] Unknown body:",
        body
      );
    } catch (err) {
      console.error(
        "[CoinGate Reader] Body inspection failed:",
        err
      );
    }
  }


  /*
   * ---------------------------------------------------------
   * Recursively search event payload
   * ---------------------------------------------------------
   */

  function inspectObject(object) {
    const found = [];

    recursiveSearch(object, found);

    for (const value of found) {
      console.log(
        "[CoinGate Reader] Possible address:",
        value
      );

      sendToExtension({
        type: "ADDRESS_FOUND",
        address: value
      });
    }
  }


  function recursiveSearch(value, found) {
    if (typeof value === "string") {
      /*
       * EVM
       */
      const evmMatches =
        value.match(
          /0x[a-fA-F0-9]{40}/g
        );

      if (evmMatches) {
        found.push(...evmMatches);
      }

      /*
       * Solana
       *
       * Keep this loose because Solana addresses
       * are base58 and don't have a fixed prefix.
       */
      return;
    }

    if (
      value &&
      typeof value === "object"
    ) {
      for (const child of Object.values(value)) {
        recursiveSearch(child, found);
      }
    }
  }


  /*
   * ---------------------------------------------------------
   * Send data to isolated content script
   * ---------------------------------------------------------
   */

  function sendToExtension(data) {
    window.postMessage(
      {
        source: "COINGATE_ADDRESS_READER",
        ...data
      },
      "*"
    );
  }
})();
