import { APP_NAME } from 'constants/global'

import { OnchainKitProvider } from '@coinbase/onchainkit'
import { sdk } from '@farcaster/miniapp-sdk'
import * as Sentry from '@sentry/react'

// On deploy, Vite renames every hashed chunk. In-flight users with a stale
// index.html try to import the old chunk URL and the dynamic import rejects
// with "Importing a module script failed" (Chrome/Firefox) or a `text/html`
// MIME error (iOS Safari) because Netlify's SPA fallback serves index.html.
// Reload once on chunk preload failure so the user picks up the new bundle;
// the sessionStorage flag prevents a refresh loop if the new bundle itself
// fails to load. After a few seconds of clean boot we clear the flag so a
// later deploy in the same tab can still recover. See JAVASCRIPT-REACT-4.
if (typeof window !== 'undefined') {
  const RELOAD_FLAG = 'teller_chunk_reload_attempted'
  window.addEventListener('vite:preloadError', (event) => {
    if (sessionStorage.getItem(RELOAD_FLAG)) return
    // Suppress the default rethrow so the recovery reload doesn't also
    // surface as an unhandled rejection in Sentry / error boundaries.
    event.preventDefault()
    sessionStorage.setItem(RELOAD_FLAG, '1')
    window.location.reload()
  })
  setTimeout(() => sessionStorage.removeItem(RELOAD_FLAG), 5000)
}

const sentryDsn = import.meta.env.VITE_SENTRY_DSN
if (import.meta.env.PROD && sentryDsn) {
  // Hosts whose HTTP responses are legitimately large and outside our control.
  // Sentry's performance_large_http_payload detector flags these as issues; we
  // drop the spans before send so the detector has nothing to inspect. See
  // JAVASCRIPT-REACT-3 and JAVASCRIPT-REACT-7.
  const KNOWN_LARGE_HOSTS = [
    'whitelisted-tokens-middleware-production.up.railway.app',
    'raw.githubusercontent.com',
  ]

  Sentry.init({
    dsn: sentryDsn,
    environment: import.meta.env.MODE,
    sendDefaultPii: true,
    integrations: [
      Sentry.browserTracingIntegration(),
      Sentry.replayIntegration(),
    ],
    tracesSampleRate: 1.0,
    tracePropagationTargets: ['localhost', /^https:\/\/yourserver\.io\/api/],
    replaysSessionSampleRate: 0.1,
    replaysOnErrorSampleRate: 1.0,
    enableLogs: true,
    // iOS Safari aggressively evicts IndexedDB; the Amplitude session-replay
    // plugin doesn't guard the upgradeneeded event. These show up as
    // unhandled rejections from third-party SDKs we can't patch.
    // See JAVASCRIPT-REACT-B, REACT-V, REACT-15.
    ignoreErrors: [
      /UnknownError: Database deleted by request of the user/,
      /UnknownError: The operation failed for an unknown transient reason/,
      /evaluating 'e\.objectStoreNames'/,
      /Cannot read properties of undefined \(reading 'objectStoreNames'\)/,
    ],
    beforeSendTransaction(event) {
      if (event.spans?.length) {
        event.spans = event.spans.filter((span) => {
          if (span.op !== 'http.client') return true
          const url =
            (span.data?.['http.url'] as string | undefined) ??
            (span.description as string | undefined) ??
            ''
          let hostname: string
          try {
            hostname = new URL(url).hostname
          } catch {
            return true
          }
          return !KNOWN_LARGE_HOSTS.some(
            (host) => hostname === host || hostname.endsWith('.' + host)
          )
        })
      }
      return event
    },
  })
}

import {
  darkTheme,
  lightTheme,
  RainbowKitProvider,
} from '@rainbow-me/rainbowkit'
import '@rainbow-me/rainbowkit/styles.css'
import spindl from '@spindl-xyz/attribution'
import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
import React, { PropsWithChildren, useEffect, useMemo, Suspense } from 'react'
import ReactDOM from 'react-dom/client'
import { ReactQueryDevtools } from '@tanstack/react-query-devtools'
import { Provider } from 'react-redux'
import { RouterProvider } from 'react-router-dom'
import { router } from 'utils/createRouter'
import { config } from 'utils/createWagmiClient'
import GTM from 'utils/GTM'
import { initializeWalletConnectStorage } from 'utils/walletConnectInitializer'
import { useAccount, useBlockNumber, WagmiProvider } from 'wagmi'
import { base } from 'wagmi/chains'
import { store } from './store'
import { hashFn } from '@wagmi/core/query'

import { useAppSelector } from 'hooks'
import { selectOfflineChain } from 'services/globalSlice'
import { ThemeProvider, useTheme } from 'contexts/ThemeContext'
import './App.scss'
import './index.scss'
import './polyfills'

// defer non-critical startup work
const idle = (fn: () => void) =>
  (window as any).requestIdleCallback
    ? (window as any).requestIdleCallback(fn)
    : setTimeout(fn, 0)

const queryClient = new QueryClient({
  defaultOptions: {
    queries: {
      queryKeyHashFn: hashFn,
      retry: 1,
      refetchOnWindowFocus: false,
    },
  },
})

spindl.configure({
  sdkKey: import.meta.env.VITE_SDK_SPINDL_KEY,
})

const TxErrorWrapper: React.FC<PropsWithChildren> = ({ children }) => {
  const { address, chain } = useAccount()
  const { data: blockNumber } = useBlockNumber({ watch: false }) // avoid live subscription

  const errLog = console.error
  console.error = (...args) => {
    const err = args[0] as unknown
    const data = (args as any)[1]

    if (err && String(err).includes('contract') && data?.abi) {
      // encodeFunctionData is only needed on error; load on demand
      import('viem').then(({ encodeFunctionData }) => {
        const functionData = encodeFunctionData({
          abi: data.abi,
          functionName: data.functionName,
          args: data.args,
        })
        console.group('Transaction Error')
        console.group('Tenderly Simulation Link')
        console.log(
          `https://dashboard.tenderly.co/teller/v2/simulator/new?block=${blockNumber}&from=${address}&contractAddress=${data.contractAddress}&rawFunctionInput=${functionData}&network=${chain?.id}&blockIndex=0&gas=8000000&gasPrice=0&value=0&headerBlockNumber=&headerTimestamp=`
        )
        console.groupEnd()
        console.warn(err)
        console.groupEnd()
      })
      return
    }

    errLog(...args)
  }

  return <>{children}</>
}

const Main = () => {
  const { theme: appTheme } = useTheme()

  // memoize theme object to avoid re-creates
  const rainbowTheme = useMemo(
    () =>
      appTheme === 'dark'
        ? darkTheme({
            accentColor: '#00C5C1',
            accentColorForeground: '#0C2825',
            borderRadius: 'small',
          })
        : lightTheme({
            accentColor: '#0C2825',
            accentColorForeground: 'white',
            borderRadius: 'small',
          }),
    [appTheme]
  )

  const offlineChain = useAppSelector(selectOfflineChain)

  useEffect(() => {
    idle(() => {
      try {
        sdk.actions.ready()
      } catch {}
      // GTM after paint/idle
      try {
        if (import.meta.env.PROD) GTM.init()
      } catch {}
      // WalletConnect storage after paint/idle
      initializeWalletConnectStorage().catch(() => {})

      if ('serviceWorker' in navigator) {
        navigator.serviceWorker
          .register('/sw.js')
          .then((registration) => {
            console.log('SW registered:', registration.scope)

            registration.addEventListener('updatefound', () => {
              const newWorker = registration.installing
              if (newWorker) {
                newWorker.addEventListener('statechange', () => {
                  if (
                    newWorker.state === 'installed' &&
                    navigator.serviceWorker.controller
                  ) {
                    console.log('New SW available - reload to update')
                  }
                })
              }
            })
          })
          .catch((error) => {
            console.error('SW registration failed:', error)
          })
      }
    })

    // hide bootstrap loader ASAP after mount
    const globalLoader = document.getElementById('global-loader')
    if (globalLoader) globalLoader.style.display = 'none'
  }, [])

  return (
    <Suspense fallback={null}>
      <WagmiProvider config={config}>
        <QueryClientProvider client={queryClient}>
          <OnchainKitProvider
            apiKey={
              import.meta.env.VITE_ONCHAINKIT_API_KEY ||
              import.meta.env.VITE_CDP_CLIENT_API_KEY
            }
            chain={offlineChain ?? base}
            config={{
              appearance: {
                mode: appTheme,
                name: 'Teller',
                logo: 'https://app.teller.org/assets/teller-logo.webp',
              },
              wallet: { display: 'modal', preference: 'all' },
            }}
            miniKit={{
              enabled: true,
            }}
          >
            <RainbowKitProvider
              modalSize="compact"
              appInfo={{ appName: APP_NAME }}
              theme={rainbowTheme}
            >
              <TxErrorWrapper>
                <RouterProvider router={router} />
              </TxErrorWrapper>
            </RainbowKitProvider>
          </OnchainKitProvider>
          {import.meta.env.VITE_SHOW_TANSTACK_DEVTOOLS && (
            <ReactQueryDevtools initialIsOpen={false} />
          )}
        </QueryClientProvider>
      </WagmiProvider>
    </Suspense>
  )
}

ReactDOM.createRoot(document.getElementById('root') as HTMLElement).render(
  <Provider store={store}>
    <ThemeProvider>
      <Main />
    </ThemeProvider>
  </Provider>
)
