import { useQuery } from '@tanstack/react-query'
import Constants from 'expo-constants'
import * as Device from 'expo-device'
import * as Notifications from 'expo-notifications'
import { useCallback, useEffect, useState } from 'react'
import { ActivityIndicator, Platform } from 'react-native'
import { Switch } from 'src/components/form/Switch'
import { Card, FieldHelperText, FieldTitle } from 'src/components/ui'
import { listScopeKeys, useCacheMutation } from 'src/hooks/mutations/cacheMutation'
import { pushStateDisable, pushStateUpdate } from 'src/lib/api'
import { refreshStalePushRegistrations } from 'src/lib/push'
import { notificationQueries } from 'src/lib/queries/notifications'
import { AccountStore } from 'src/state/accountStore'
import {
  ScrollView,
  Separator,
  Text,
  useTheme,
  useToastController,
  View,
  YStack,
} from 'tamagui'

function handleRegistrationError(errorMessage: string) {
  alert(errorMessage)
  throw new Error(errorMessage)
}

async function registerForPushNotificationsAsync() {
  if (Platform.OS === 'android') {
    const channels = [
      {
        id: 'likes',
        name: 'Likes',
        importance: Notifications.AndroidImportance.DEFAULT,
      },
      {
        id: 'comments',
        name: 'Comments',
        importance: Notifications.AndroidImportance.HIGH,
      },
      {
        id: 'follows',
        name: 'New Followers',
        importance: Notifications.AndroidImportance.DEFAULT,
      },
      {
        id: 'mentions',
        name: 'Mentions',
        importance: Notifications.AndroidImportance.HIGH,
      },
      {
        id: 'direct',
        name: 'Direct Messages',
        importance: Notifications.AndroidImportance.MAX,
      },
    ]
    for (const channel of channels) {
      Notifications.setNotificationChannelAsync(channel.id, {
        name: channel.name,
        importance: channel.importance,
        vibrationPattern: [0, 250, 250, 250],
        lightColor: '#FF231F7C',
      })
    }
  }

  if (Device.isDevice) {
    const { status: existingStatus } = await Notifications.getPermissionsAsync()
    let finalStatus = existingStatus
    if (existingStatus !== 'granted') {
      const { status } = await Notifications.requestPermissionsAsync()
      finalStatus = status
    }
    if (finalStatus !== 'granted') {
      handleRegistrationError(
        'Permission not granted to get push token for push notification!'
      )
      return
    }
    const projectId =
      Constants?.expoConfig?.extra?.eas?.projectId ?? Constants?.easConfig?.projectId
    if (!projectId) {
      handleRegistrationError('Project ID not found')
    }
    try {
      const pushTokenString = (
        await Notifications.getExpoPushTokenAsync({
          projectId,
        })
      ).data
      return pushTokenString
    } catch (e: unknown) {
      handleRegistrationError(`${e}`)
    }
  } else {
    handleRegistrationError('Must use physical device for push notifications')
  }
}

export default function Page() {
  const [expoPushToken, setExpoPushToken] = useState('')
  const [updatingKeys, setUpdatingKeys] = useState(new Set())
  const theme = useTheme()
  const toast = useToastController()

  useEffect(() => {
    registerForPushNotificationsAsync()
      .then((token) => setExpoPushToken(token ?? ''))
      .catch((error: any) => setExpoPushToken(`${error}`))
  }, [])

  const mutation = useCacheMutation<
    unknown,
    { type: any; value: any; optimisticKey: any }
  >({
    mutationFn: async ({ type, value }) => {
      if (type === 'disable') return pushStateDisable()
      if (type === 'enable') {
        return pushStateUpdate({ notify_enabled: true, token: expoPushToken })
      }
      return pushStateUpdate({
        notify_enabled: true,
        token: expoPushToken,
        [type]: value,
      })
    },
    snapshotKeys: () => listScopeKeys(notificationQueries.pushState().queryKey),
    applyOptimistic: (qc, { type, value, optimisticKey }) => {
      qc.setQueryData(notificationQueries.pushState().queryKey, (old: any) => ({
        ...old,
        [type]: value,
      }))
      setUpdatingKeys((prev) => new Set([...prev, optimisticKey]))
    },
    onError: (_err, { optimisticKey }) => {
      setUpdatingKeys((prev) => {
        const next = new Set(prev)
        next.delete(optimisticKey)
        return next
      })
      toast.show('Update failed', {
        message: 'Unable to update notification setting. Please try again.',
        preset: 'error',
      })
    },
    onSuccess: (_data, { optimisticKey, type, value }, qc) => {
      setUpdatingKeys((prev) => {
        const next = new Set(prev)
        next.delete(optimisticKey)
        return next
      })
      qc.invalidateQueries({ queryKey: notificationQueries.pushState().queryKey })

      // The user just enabled push for the active account — this is also
      // the first moment we have a fresh device token in hand. Mark the
      // active account `pushRegisteredAt` so cold-start refresh skips it
      // tomorrow, and fan out the token to every other signed-in account
      // so multi-account users start receiving pushes everywhere without
      // waiting for an app relaunch (Phase 5, §9.2).
      const isEnable = type === 'enable' || (type === 'notify_enabled' && value === true)
      if (isEnable) {
        const activeId = AccountStore.activeId()
        if (activeId) {
          AccountStore.patch(activeId, {
            pushRegisteredAt: Date.now(),
            pushUnavailable: false,
          })
        }
        refreshStalePushRegistrations().catch(() => {})
      }
    },
  })

  const toggleMainState = useCallback(
    (val: any) => {
      const _type = val ? 'enable' : 'disable'
      const optimisticKey = 'notify_enabled'

      mutation.mutate({
        type: val ? 'notify_enabled' : 'disable',
        value: val,
        optimisticKey,
      })
    },
    [mutation]
  )

  const toggleNotificationSetting = useCallback(
    (settingType: any, value: any) => {
      mutation.mutate({
        type: settingType,
        value,
        optimisticKey: settingType,
      })
    },
    [mutation]
  )

  const { data: pushStateRes, status, error } = useQuery(notificationQueries.pushState())

  if (status === 'pending') {
    return (
      <View flex={1} justifyContent="center" alignItems="center">
        <ActivityIndicator color={theme.color.val} />
      </View>
    )
  }

  if (status === 'error') {
    return (
      <View flex={1} justifyContent="center" alignItems="center" p="$4">
        <Text color="$red10" textAlign="center" mb="$4">
          {error?.message || 'Failed to load settings'}
        </Text>
      </View>
    )
  }

  const notificationSettings = [
    {
      key: 'notify_follow',
      title: 'New Follower',
      description: 'jappleseed started following you',
    },
    {
      key: 'notify_like',
      title: 'Likes',
      description: 'jappleseed liked your post',
    },
    {
      key: 'notify_comment',
      title: 'Comments',
      description: 'jappleseed commented on your post',
    },
    {
      key: 'notify_mention',
      title: 'Mentions',
      description: 'jappleseed mentioned you in a post',
    },
  ]

  const RenderActivePanel = () => {
    return (
      <ScrollView flexShrink={1}>
        <Card
          tone="tertiary"
          py="$4"
          flexDirection="row"
          justifyContent="space-between"
          alignItems="center"
        >
          <YStack maxWidth="75%">
            <Text fontSize="$6" color={theme.color.val}>
              Enable Push Notifications
            </Text>
          </YStack>
          <View position="relative">
            <Switch
              size="$3"
              checked={pushStateRes.notify_enabled}
              onCheckedChange={toggleMainState}
              disabled={updatingKeys.has('notify_enabled')}
            >
              <Switch.Thumb transition="quicker" />
            </Switch>
            {updatingKeys.has('notify_enabled') && (
              <View
                position="absolute"
                top={-2}
                left={-2}
                right={-2}
                bottom={-2}
                justifyContent="center"
                alignItems="center"
                bg="rgba(255,255,255,0.8)"
                borderRadius="$2"
              >
                <ActivityIndicator size="small" />
              </View>
            )}
          </View>
        </Card>

        <YStack
          m="$3"
          borderColor="$borderColor"
          borderWidth={1}
          borderRadius={10}
          overflow="hidden"
        >
          {notificationSettings.map((setting, index) => (
            <View key={setting.key}>
              <Card
                tone="tertiary"
                flexDirection="row"
                justifyContent="space-between"
                opacity={
                  !pushStateRes.notify_enabled || !pushStateRes.has_token ? 0.5 : 1
                }
              >
                <YStack maxWidth="75%" gap="$2">
                  <FieldTitle>{setting.title}</FieldTitle>
                  <FieldHelperText>{setting.description}</FieldHelperText>
                </YStack>

                <View position="relative">
                  <Switch
                    size="$3"
                    disabled={
                      !pushStateRes.notify_enabled ||
                      !pushStateRes.has_token ||
                      updatingKeys.has(setting.key)
                    }
                    checked={pushStateRes[setting.key]}
                    onCheckedChange={(val) => toggleNotificationSetting(setting.key, val)}
                  >
                    <Switch.Thumb transition="quicker" />
                  </Switch>

                  {updatingKeys.has(setting.key) && (
                    <View
                      position="absolute"
                      top={-2}
                      left={-2}
                      right={-2}
                      bottom={-2}
                      justifyContent="center"
                      alignItems="center"
                      bg="rgba(255,255,255,0.8)"
                      borderRadius="$2"
                    >
                      <ActivityIndicator size="small" />
                    </View>
                  )}
                </View>
              </Card>

              {index < notificationSettings.length - 1 && (
                <Separator borderColor="$borderColor" />
              )}
            </View>
          ))}
        </YStack>
      </ScrollView>
    )
  }

  return <RenderActivePanel />
}
