import {useCallback, useEffect, useMemo, useState} from 'react'
import {
  ActivityIndicator,
  type ListRenderItemInfo,
  StyleSheet,
  View,
} from 'react-native'
import {msg} from '@lingui/core/macro'
import {useLingui} from '@lingui/react'

import {useInitialNumToRender} from '#/lib/hooks/useInitialNumToRender'
import {usePostViewTracking} from '#/lib/hooks/usePostViewTracking'
import {cleanError} from '#/lib/strings/errors'
import {s} from '#/lib/styles'
import {logger} from '#/logger'
import {useModerationOpts} from '#/state/preferences/moderation-opts'
import {useNotificationFeedQuery} from '#/state/queries/notifications/feed'
import {EmptyState} from '#/view/com/util/EmptyState'
import {ErrorMessage} from '#/view/com/util/error/ErrorMessage'
import {List, type ListProps, type ListRef} from '#/view/com/util/List'
import {NotificationFeedLoadingPlaceholder} from '#/view/com/util/LoadingPlaceholder'
import {LoadMoreRetryBtn} from '#/view/com/util/LoadMoreRetryBtn'
import {useTheme} from '#/alf'
import {Bell_Stroke2_Corner0_Rounded as BellIcon} from '#/components/icons/Bell'
import {NotificationFeedItem} from './NotificationFeedItem'

const EMPTY_FEED_ITEM = {_reactKey: '__empty__'}
const LOAD_MORE_ERROR_ITEM = {_reactKey: '__load_more_error__'}
const LOADING_ITEM = {_reactKey: '__loading__'}

export function NotificationFeed({
  filter,
  enabled,
  scrollElRef,
  onPressTryAgain,
  onScrolledDownChange,
  ListHeaderComponent,
  refreshNotifications,
}: {
  filter: 'all' | 'mentions'
  enabled: boolean
  scrollElRef?: ListRef
  onPressTryAgain?: () => void
  onScrolledDownChange: (isScrolledDown: boolean) => void
  ListHeaderComponent?: ListProps['ListHeaderComponent']
  refreshNotifications: () => Promise<void>
}) {
  const initialNumToRender = useInitialNumToRender()
  const [isPTRing, setIsPTRing] = useState(false)
  const t = useTheme()
  const {_} = useLingui()
  const moderationOpts = useModerationOpts()
  const trackPostView = usePostViewTracking('Notifications')
  const {
    data,
    isFetching,
    isFetched,
    isError,
    error,
    hasNextPage,
    isFetchingNextPage,
    fetchNextPage,
  } = useNotificationFeedQuery({
    enabled: enabled && !!moderationOpts,
    filter,
  })
  // previously, this was `!isFetching && !data?.pages[0]?.items.length`
  // however, if the first page had no items (can happen in the mentions tab!)
  // it would flicker the empty state whenever it was loading.
  // therefore, we need to find if *any* page has items. in 99.9% of cases,
  // the `.find()` won't need to go any further than the first page -sfn
  const isEmpty =
    !isFetching && !data?.pages.find(page => page.items.length > 0)

  const items = useMemo(() => {
    let arr: any[] = []
    if (isFetched) {
      if (isEmpty) {
        arr = arr.concat([EMPTY_FEED_ITEM])
      } else if (data) {
        for (const page of data?.pages) {
          arr = arr.concat(page.items)
        }
      }
      if (isError && !isEmpty) {
        arr = arr.concat([LOAD_MORE_ERROR_ITEM])
      }
    } else {
      arr.push(LOADING_ITEM)
    }
    return arr
  }, [isFetched, isError, isEmpty, data])

  const onRefresh = useCallback(async () => {
    try {
      setIsPTRing(true)
      await refreshNotifications()
    } catch (err) {
      logger.error('Failed to refresh notifications feed', {
        message: err,
      })
    } finally {
      setIsPTRing(false)
    }
  }, [refreshNotifications, setIsPTRing])

  const onEndReached = useCallback(async () => {
    if (isFetching || !hasNextPage || isError) return

    try {
      await fetchNextPage()
    } catch (err) {
      logger.error('Failed to load more notifications', {message: err})
    }
  }, [isFetching, hasNextPage, isError, fetchNextPage])

  const onPressRetryLoadMore = useCallback(() => {
    fetchNextPage()
  }, [fetchNextPage])

  const renderItem = useCallback(
    ({item, index}: ListRenderItemInfo<any>) => {
      if (item === EMPTY_FEED_ITEM) {
        return (
          <EmptyState
            icon={BellIcon}
            message={_(msg`No notifications yet!`)}
            style={styles.emptyState}
          />
        )
      } else if (item === LOAD_MORE_ERROR_ITEM) {
        return (
          <LoadMoreRetryBtn
            label={_(
              msg`There was an issue fetching notifications. Tap here to try again.`,
            )}
            onPress={onPressRetryLoadMore}
          />
        )
      } else if (item === LOADING_ITEM) {
        return <NotificationFeedLoadingPlaceholder />
      }
      return (
        <NotificationFeedItem
          highlightUnread={filter === 'all'}
          item={item}
          moderationOpts={moderationOpts!}
          hideTopBorder={index === 0}
        />
      )
    },
    [moderationOpts, _, onPressRetryLoadMore, filter],
  )

  const FeedFooter = useCallback(
    () =>
      isFetchingNextPage ? (
        <View style={styles.feedFooter}>
          <ActivityIndicator color={t.palette.primary_500} />
        </View>
      ) : (
        <View />
      ),
    [isFetchingNextPage, t.palette.primary_500],
  )

  useEffect(() => {
    if (!enabled) {
      setIsPTRing(false)
    }
  }, [enabled])

  return (
    <View style={s.hContentRegion}>
      {error && (
        <ErrorMessage
          message={cleanError(error)}
          onPressTryAgain={onPressTryAgain}
        />
      )}
      <List
        testID="notifsFeed"
        ref={scrollElRef}
        data={items}
        keyExtractor={item => item._reactKey}
        renderItem={renderItem}
        ListHeaderComponent={ListHeaderComponent}
        ListFooterComponent={FeedFooter}
        refreshing={isPTRing}
        onRefresh={onRefresh}
        onEndReached={onEndReached}
        onEndReachedThreshold={2}
        onScrolledDownChange={onScrolledDownChange}
        onItemSeen={item => {
          if (
            (item.type === 'reply' ||
              item.type === 'mention' ||
              item.type === 'quote') &&
            item.subject
          ) {
            trackPostView(item.subject)
          }
        }}
        contentContainerStyle={s.contentContainer}
        desktopFixedHeight
        initialNumToRender={initialNumToRender}
        windowSize={11}
        sideBorders={false}
        removeClippedSubviews={true}
      />
    </View>
  )
}

const styles = StyleSheet.create({
  feedFooter: {paddingTop: 20},
  emptyState: {paddingVertical: 40},
})
