import {useCallback, useEffect, useMemo, useState} from 'react'
import {ActivityIndicator, Platform, View} from 'react-native'
import ReactNativeDeviceAttest from 'react-native-device-attest'
import {msg} from '@lingui/core/macro'
import {useLingui} from '@lingui/react'
import {nanoid} from 'nanoid/non-secure'

import {createFullHandle} from '#/lib/strings/handles'
import {logger} from '#/logger'
import {useSignupContext} from '#/screens/Signup/state'
import {CaptchaWebView} from '#/screens/Signup/StepCaptcha/CaptchaWebView'
import {atoms as a, useTheme} from '#/alf'
import {FormError} from '#/components/forms/FormError'
import {useAnalytics} from '#/analytics'
import {GCP_PROJECT_ID, IS_ANDROID, IS_IOS, IS_NATIVE, IS_WEB} from '#/env'
import {BackNextButtons} from '../BackNextButtons'

const CAPTCHA_PATH =
  IS_WEB || GCP_PROJECT_ID === 0
    ? '/gate/signup'
    : '/gate/signup/attempt-attest'

export function StepCaptcha() {
  if (IS_WEB) {
    return <StepCaptchaInner />
  } else {
    return <StepCaptchaNative />
  }
}

export function StepCaptchaNative() {
  const [token, setToken] = useState<string>()
  const [payload, setPayload] = useState<string>()
  const [ready, setReady] = useState(false)

  useEffect(() => {
    void (async () => {
      logger.debug('trying to generate attestation token...')
      try {
        if (IS_IOS) {
          logger.debug('starting to generate devicecheck token...')
          const token = await ReactNativeDeviceAttest.getDeviceCheckToken()
          setToken(token)
          logger.debug(`generated devicecheck token: ${token}`)
        } else {
          const {token, payload} =
            await ReactNativeDeviceAttest.getIntegrityToken('signup')
          setToken(token)
          setPayload(base64UrlEncode(payload))
        }
      } catch (err) {
        const e = err as Error
        logger.error(e)
      } finally {
        setReady(true)
      }
    })()
  }, [])

  if (!ready) {
    return <View />
  }

  return <StepCaptchaInner token={token} payload={payload} />
}

function StepCaptchaInner({
  token,
  payload,
}: {
  token?: string
  payload?: string
}) {
  const t = useTheme()
  const {_} = useLingui()
  const ax = useAnalytics()
  const theme = useTheme()
  const {state, dispatch} = useSignupContext()

  const [completed, setCompleted] = useState(false)

  const stateParam = useMemo(() => nanoid(15), [])
  const url = useMemo(() => {
    const newUrl = new URL(state.serviceUrl)
    newUrl.pathname = CAPTCHA_PATH
    newUrl.searchParams.set(
      'handle',
      createFullHandle(state.handle, state.userDomain),
    )
    newUrl.searchParams.set('state', stateParam)
    newUrl.searchParams.set('colorScheme', theme.name)

    if (IS_NATIVE && token) {
      newUrl.searchParams.set('platform', Platform.OS)
      newUrl.searchParams.set('token', token)
      if (IS_ANDROID && payload) {
        newUrl.searchParams.set('payload', payload)
      }
    }

    return newUrl.href
  }, [
    state.serviceUrl,
    state.handle,
    state.userDomain,
    stateParam,
    theme.name,
    token,
    payload,
  ])

  const onSuccess = useCallback(
    (code: string) => {
      setCompleted(true)
      ax.metric('signup:captchaSuccess', {})
      dispatch({
        type: 'submit',
        task: {verificationCode: code, mutableProcessed: false},
      })
    },
    [ax, dispatch],
  )

  const onError = useCallback(
    (error?: unknown) => {
      dispatch({
        type: 'setError',
        value: _(msg`Error receiving captcha response.`),
      })
      ax.metric('signup:captchaFailure', {})
      logger.error('Signup Flow Error', {
        registrationHandle: state.handle,
        error,
      })
    },
    [_, ax, dispatch, state.handle],
  )

  const onBackPress = useCallback(() => {
    logger.error('Signup Flow Error', {
      errorMessage:
        'User went back from captcha step. Possibly encountered an error.',
      registrationHandle: state.handle,
    })

    dispatch({type: 'prev'})
  }, [dispatch, state.handle])

  return (
    <>
      <View style={[a.gap_lg, a.pt_lg]}>
        <View
          style={[
            a.w_full,
            a.overflow_hidden,
            {minHeight: 510},
            completed && [a.align_center, a.justify_center],
          ]}>
          {!completed ? (
            <CaptchaWebView
              url={url}
              stateParam={stateParam}
              state={state}
              onComplete={() => setCompleted(true)}
              onSuccess={onSuccess}
              onError={onError}
            />
          ) : (
            <ActivityIndicator size="large" color={t.palette.primary_500} />
          )}
        </View>
        <FormError error={state.error} />
      </View>
      <BackNextButtons
        hideNext
        isLoading={state.isLoading}
        onBackPress={onBackPress}
      />
    </>
  )
}

function base64UrlEncode(data: string): string {
  const encoder = new TextEncoder()
  const bytes = encoder.encode(data)

  const binaryString = String.fromCharCode(...bytes)
  const base64 = btoa(binaryString)

  return base64.replace(/\+/g, '-').replace(/\//g, '_').replace(/[=]/g, '')
}
