/**
 * Zoff Wallet — universal content script (Manifest V3).
 *
 * Runs on `<all_urls>` so dApps anywhere can call `window.cantonWallet.*`.
 *
 * The provider itself (`injected.js`) is registered as a separate
 * content_script with `"world": "MAIN"` in the manifest, so it runs
 * synchronously at document_start in the page's main world — wins the
 * race against any in-page `<script src=...wallet-provider.js>` fallback.
 * (Session 4 fix: previously DOM-injected via `chrome.runtime.getURL`,
 * which was async and lost the race against parser-blocking page scripts.)
 *
 * This isolated-world content script's only job is to bridge the page's
 * `window.postMessage` JSON-RPC envelopes to the background service worker
 * via `chrome.runtime.sendMessage`, and post replies back to the page
 * world. Each request carries a UUID `reqId` so concurrent calls don't
 * collide.
 *
 * The wallet-state ↔ chrome.storage mirror lives in a *separate* content
 * script (`bridge.js`) allowlisted to known wallet origins only.
 */

;(function () {
  // ── dApp ↔ background JSON-RPC bridge ───────────────────────────────────
  //
  // The injected provider posts envelopes of the shape:
  //   { target: 'zoff-content', reqId, method, params }
  //
  // We forward the inner method call to the background SW and post the
  // reply back to the page world as:
  //   { target: 'zoff-injected', reqId, result }   on success
  //   { target: 'zoff-injected', reqId, error }    on failure
  //
  // The error shape mirrors EIP-1193 / JSON-RPC: `{ code, message, data? }`.
  window.addEventListener('message', (event) => {
    if (event.source !== window) return
    const data = event.data
    if (!data || typeof data !== 'object') return
    if (data.target !== 'zoff-content') return
    const { reqId, method, params } = data
    if (typeof reqId !== 'string' || typeof method !== 'string') return

    let replied = false
    function reply(payload) {
      if (replied) return
      replied = true
      window.postMessage(
        { target: 'zoff-injected', reqId, ...payload },
        location.origin,
      )
    }

    try {
      chrome.runtime.sendMessage(
        { type: 'zoff-rpc', reqId, method, params, origin: location.origin },
        (response) => {
          if (chrome.runtime.lastError) {
            reply({
              error: {
                code: -32603,
                message:
                  chrome.runtime.lastError.message ||
                  'Service worker error',
              },
            })
            return
          }
          if (!response || typeof response !== 'object') {
            reply({ error: { code: -32603, message: 'Empty SW response' } })
            return
          }
          // Approval-required methods (`connect`, `signAndSubmit`) reply
          // with `{deferred: true}` immediately, then push the real
          // result later via `chrome.tabs.sendMessage` once the user
          // closes the approval popup. We do NOT reply here in that
          // case — we wait for the push, which is forwarded by the
          // listener below. This decouples the result from the SW's
          // message-channel lifetime, so the SW can die mid-approval
          // and still wake up to deliver the result.
          if (response.deferred === true) return
          if ('error' in response && response.error) {
            reply({ error: response.error })
          } else {
            reply({ result: response.result })
          }
        },
      )
    } catch (err) {
      reply({
        error: {
          code: -32603,
          message: err instanceof Error ? err.message : 'Bridge error',
        },
      })
    }
  })

  // ── 3. Push messages from the SW back to the page ──────────────────────
  //
  // Two kinds:
  //   - `zoff-rpc-result` — deferred approval result, keyed by reqId
  //   - `zoff-event` — provider events (`accountChanged`, `disconnect`)
  chrome.runtime.onMessage.addListener((message) => {
    if (!message || typeof message !== 'object') return

    if (message.type === 'zoff-rpc-result') {
      window.postMessage(
        {
          target: 'zoff-injected',
          reqId: message.reqId,
          result: message.result,
          error: message.error,
        },
        location.origin,
      )
      return
    }

    if (message.type === 'zoff-event') {
      window.postMessage(
        {
          target: 'zoff-injected-event',
          event: message.event,
          payload: message.payload,
        },
        location.origin,
      )
      return
    }
  })
})()
