import React, { useEffect, useState } from 'react'
import { Text, TouchableOpacity } from 'react-native'
import { Platform, StyleSheet, StyleProp, ViewStyle } from 'react-native'
import { TextStyles } from '../Styles'
import { colors } from '../Colors'
import * as Clipboard from 'expo-clipboard'
import * as Haptics from 'expo-haptics'

import useSWR from 'swr/esm'
import useEraseLocalData from '../hooks/useEraseLocalData'
import manifest from '../appManifest'
import { usePushNotificationToken } from '../hooks/useNotifications'

const styles = StyleSheet.create({
  buildInfo: {
    ...TextStyles.micro,
    color: colors.midGray,
  },
})

type Props = {
  style: StyleProp<ViewStyle>
}

const useBackendStatuses = () => {
  // See type Health from ama-backend/src/api/delphi-types.ts
  interface DelphiHealth {
    api_version: string
    environment: string
    status: string
  }

  interface HealthResponse {
    environment: string
    delphi: 'timeout' | DelphiHealth
    userPreferences: 'timeout' | 'ok'
  }

  const swrOptions = {
    shouldRetryOnError: false,
    initialdata: {
      backend: 'loading',
      delphi: 'loading',
      preferences: 'loading',
    },
  }

  const { data, error } = useSWR<HealthResponse>(
    `_health`,
    async (path) => {
      const response = await fetch(`${manifest.extra.backendUrl}/${path}`)
      return await response.json()
    },
    swrOptions
  )

  if (error)
    return { backend: 'error', preferences: 'unknown', delphi: 'unknown' }
  if (!data)
    return { backend: 'loading', preferences: 'unknown', delphi: 'unknown' }

  return {
    backend: data.environment ?? 'unknown',
    preferences: data.userPreferences ?? 'unknown',
    delphi:
      data.delphi === 'timeout'
        ? 'timeout'
        : data.delphi?.environment ?? 'unknown',
  }
}

// encodes app _E_nv _b_ackend _E_nv _d_elphi _E_nv _p_references _E_nv
// Env is an overloaded term and means different things, and may be ambiguous
//
// example:
//   app Local backend Unknown delphi Stage-mobile preferences Ok
//   => aLbUdSpO
const useStatusesHash = () => {
  const app = manifest.extra.environment
  const statuses = useBackendStatuses()
  // { service: 'delphi', status: 'production' } => 'dP'
  const encodeServiceStatus = ([service, status]: [string, string]) =>
    `${service?.slice(0, 1)}${status?.slice(0, 1).toUpperCase()}`

  return Object.entries({ app, ...statuses })
    .map(encodeServiceStatus)
    .join('')
}

const getBuildNumber = () =>
  Platform.OS === 'android'
    ? manifest.android.versionCode.toString()
    : manifest.ios.buildNumber

export const BuildInfo: React.FC<Props> = ({ style }) => {
  const [showDetails, setShowDetails] = useState(false)
  const [token, setToken] = useState<string | null>(null)
  const { incrementCounter } = useEraseLocalData()
  const toggleDetails = () => {
    incrementCounter()
    setShowDetails(!showDetails)
  }

  const status = useStatusesHash()
  const notificationToken = usePushNotificationToken()
  useEffect(() => {
    if (notificationToken == null) return
    setToken(notificationToken?.split(/\[|\]/)?.[1])
  }, [notificationToken])
  const build = getBuildNumber()
  const { tag, commit } = manifest.extra.version.git
  const hash = commit.slice(0, 6)

  const details = { build, tag, hash, status, token }

  function copyDetails(shownDetails: string[][]) {
    const text = shownDetails.map((detail) => detail.join(' ')).join('\n')
    Clipboard.setString(text)
    Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Medium)
    console.log(text)
  }

  const Detail: React.FC<{ label: string; value: string }> = ({
    label,
    value,
  }) => (
    <Text style={styles.buildInfo} key={label}>
      {label} {value}
    </Text>
  )

  const shownDetails = showDetails
    ? Object.entries(details).map(([key, value]) => [
        key,
        value == null ? '' : value,
      ])
    : [['version', manifest.version]]

  return (
    <TouchableOpacity
      onPress={toggleDetails}
      onLongPress={() => copyDetails(shownDetails)}
      style={style}
    >
      {shownDetails.map(([label, value], i) => (
        <Detail label={label} value={value} key={i} />
      ))}
    </TouchableOpacity>
  )
}
