/**
 * Zoff Wallet — wallet-state bridge content script (Manifest V3).
 *
 * Sole responsibility: mirror the web wallet's localStorage state
 * (`canton_wallet_keystore`, `canton-wallet-accounts`, `canton-wallet-locked`)
 * into `chrome.storage.local` so the extension popup can read it without
 * the user re-onboarding inside the popup.
 *
 * **This script is allowlisted** in `manifest.extension.json` to known
 * wallet origins (`https://*.zoff.app/*`, `http://localhost:5200/*`).
 * Restricting matches is the Session 3 fix for Session 2's "last writer
 * wins" footgun: previously the bridge ran on `<all_urls>` and any tab
 * could clobber the popup's view of which wallet was active. Two real
 * wallet origins can still race each other (zoff.app vs localhost dev),
 * which is why the popup also displays the synced origin in its header
 * — see `popup/page.tsx`.
 *
 * The page-world dApp provider injection (`injected.js`) and the
 * dApp ↔ background bridge live in the *other* content script
 * (`content.js`), which still runs on `<all_urls>`.
 */

;(function () {
  const ACCOUNTS_KEY = 'canton-wallet-accounts'
  const LOCK_KEY = 'canton-wallet-locked'
  // NB: keystore key uses underscores (set by `src/crypto/keystore.ts`),
  // unlike the dash-separated accounts/lock keys above. Don't normalise.
  const KEYSTORE_KEY = 'canton_wallet_keystore'
  const ACTIVE_INDEX_KEY = 'zoff-popup-active-index'
  const MIRROR_KEYS = {
    accounts: ACCOUNTS_KEY,
    locked: LOCK_KEY,
    keystore: KEYSTORE_KEY,
    activeIndex: ACTIVE_INDEX_KEY,
    origin: 'canton-wallet-origin',
    syncedAt: 'canton-wallet-synced-at',
  }

  let lastSyncedAccounts = null
  let lastSyncedLocked = null
  let lastSyncedKeystore = null
  let lastSyncedActiveIndex = null
  let pollHandle = null

  function readWalletStateFromPage() {
    try {
      const accountsRaw = localStorage.getItem(ACCOUNTS_KEY)
      if (!accountsRaw) return null
      let accounts
      try {
        accounts = JSON.parse(accountsRaw)
      } catch {
        return null
      }
      if (!Array.isArray(accounts) || accounts.length === 0) return null
      const locked = localStorage.getItem(LOCK_KEY) === 'true'
      const keystore = localStorage.getItem(KEYSTORE_KEY)
      const activeIndexRaw = localStorage.getItem(ACTIVE_INDEX_KEY)
      const activeIndex = activeIndexRaw !== null ? parseInt(activeIndexRaw, 10) : null
      return { accounts, locked, keystore, activeIndex }
    } catch {
      return null
    }
  }

  function clearExtensionMirror() {
    if (lastSyncedAccounts === null) return
    lastSyncedAccounts = null
    lastSyncedLocked = null
    lastSyncedKeystore = null
    lastSyncedActiveIndex = null
    try {
      chrome.storage.local.remove([
        MIRROR_KEYS.accounts,
        MIRROR_KEYS.locked,
        MIRROR_KEYS.keystore,
        MIRROR_KEYS.activeIndex,
        MIRROR_KEYS.origin,
        MIRROR_KEYS.syncedAt,
      ])
    } catch (err) {
      console.warn('[Zoff bridge] storage.local.remove failed:', err)
    }
  }

  function syncToExtension(force) {
    const state = readWalletStateFromPage()
    if (!state) {
      clearExtensionMirror()
      return false
    }

    const accountsSerialised = JSON.stringify(state.accounts)
    if (
      !force &&
      accountsSerialised === lastSyncedAccounts &&
      state.locked === lastSyncedLocked &&
      state.keystore === lastSyncedKeystore &&
      state.activeIndex === lastSyncedActiveIndex
    ) {
      return true
    }
    lastSyncedAccounts = accountsSerialised
    lastSyncedLocked = state.locked
    lastSyncedKeystore = state.keystore
    lastSyncedActiveIndex = state.activeIndex

    try {
      const payload = {
        [MIRROR_KEYS.accounts]: state.accounts,
        [MIRROR_KEYS.locked]: state.locked,
        [MIRROR_KEYS.origin]: location.origin,
        [MIRROR_KEYS.syncedAt]: Date.now(),
      }
      if (state.activeIndex !== null && Number.isFinite(state.activeIndex)) {
        payload[MIRROR_KEYS.activeIndex] = state.activeIndex
      }
      if (state.keystore !== null) {
        payload[MIRROR_KEYS.keystore] = state.keystore
      } else {
        chrome.storage.local.remove(MIRROR_KEYS.keystore)
      }
      chrome.storage.local.set(payload)
    } catch (err) {
      console.warn('[Zoff bridge] storage.local.set failed:', err)
    }
    return true
  }

  if (syncToExtension(true)) {
    pollHandle = setInterval(() => syncToExtension(false), 4000)
  } else {
    setTimeout(() => {
      if (syncToExtension(true) && !pollHandle) {
        pollHandle = setInterval(() => syncToExtension(false), 4000)
      }
    }, 2000)
  }

  window.addEventListener('storage', (e) => {
    if (
      e.key === ACCOUNTS_KEY ||
      e.key === LOCK_KEY ||
      e.key === KEYSTORE_KEY ||
      e.key === ACTIVE_INDEX_KEY ||
      e.key === null
    ) {
      syncToExtension(false)
    }
  })

  // Reverse sync: when the popup changes the active index via
  // chrome.storage.local, mirror it back to the page's localStorage
  // so WalletProvider picks it up via the 'storage' event.
  try {
    chrome.storage.onChanged.addListener((changes, area) => {
      if (area !== 'local') return
      if (ACTIVE_INDEX_KEY in changes) {
        const newVal = changes[ACTIVE_INDEX_KEY].newValue
        if (newVal !== undefined) {
          const current = localStorage.getItem(ACTIVE_INDEX_KEY)
          const newStr = String(newVal)
          if (current !== newStr) {
            lastSyncedActiveIndex = typeof newVal === 'number' ? newVal : parseInt(newStr, 10)
            localStorage.setItem(ACTIVE_INDEX_KEY, newStr)
          }
        }
      }
    })
  } catch {
    // chrome.storage may not be available in all contexts
  }
})()
