import { useIsFocused } from '@react-navigation/native'
import { useInfiniteQuery, useQueryClient } from '@tanstack/react-query'
import { useRouter } from 'expo-router'
import { useCallback, useMemo, useRef } from 'react'
import { deleteStatusV1 } from 'src/lib/api'
import type { Status } from 'src/lib/api-types'
import { haptic } from 'src/lib/haptics'
import { postQueries } from 'src/lib/queries/posts'
import { storyQueries } from 'src/lib/queries/stories'
import { emit } from 'src/lib/tapestry/emit'
import { postScopeKeys, useCacheMutation } from './mutations/cacheMutation'
import { useShareMutation } from './mutations/useShareMutation'

// useHomeFeed — encapsulates the home timeline query + delete/share mutations.
// Like handling lives inside FeedPost (useLikeMutation), so it isn't here.
// Returns everything a screen needs to drive a TimelineList: flat data, paging
// state, a refetch that also invalidates the story carousel, and handlers
// pre-bound to the router.

export function useHomeFeed(opts?: { listId?: string | null }) {
  const queryClient = useQueryClient()
  const router = useRouter()
  const isFocused = useIsFocused()
  const listId = opts?.listId ?? null

  const {
    data,
    fetchNextPage,
    hasNextPage,
    isFetchingNextPage,
    isRefetching,
    refetch: baseRefetch,
    isFetching,
    status,
    isError,
    error,
  } = useInfiniteQuery({ ...postQueries.feed({ listId }), subscribed: isFocused })

  // Delete optimistically filters the post out of every feed cache that
  // could render it, not only the home feed — so navigating from a search
  // result or profile grid into the detail and deleting doesn't leave a
  // stale thumbnail behind.
  const deletePostMutation = useCacheMutation<unknown, string>({
    label: 'Deleting post…',
    mutationFn: (id) => deleteStatusV1(id),
    snapshotKeys: (id) => postScopeKeys(id),
    onSuccess: () => {
      emit('d')
    },
    applyOptimistic: (qc, id) => {
      for (const key of postScopeKeys(id)) {
        qc.setQueriesData({ queryKey: key as any }, (old: any) => {
          if (!old) return old
          // Single-post cache: drop it entirely so consumers fall through
          // to a "not found" path.
          if (!old.pages && old?.id === id) return undefined
          if (!old.pages) return old
          let changed = false
          const newPages = old.pages.map((page: any) => {
            if (Array.isArray(page)) {
              const filtered = page.filter((p: Status) => p.id !== id)
              if (filtered.length !== page.length) {
                changed = true
                return filtered
              }
              return page
            }
            if (Array.isArray(page.data)) {
              const filtered = page.data.filter((p: Status) => p.id !== id)
              if (filtered.length !== page.data.length) {
                changed = true
                return { ...page, data: filtered }
              }
              return page
            }
            return page
          })
          return changed ? { ...old, pages: newPages } : old
        })
      }
    },
  })

  const { handleShare } = useShareMutation()

  const refetch = useCallback(() => {
    queryClient.invalidateQueries({ queryKey: storyQueries.carousel().queryKey })
    return baseRefetch().then((r) => {
      haptic.light()
      return r
    })
  }, [queryClient, baseRefetch])

  const posts = useMemo<Status[]>(
    () => (data?.pages ? data.pages.flatMap((page) => page?.data ?? []) : []),
    [data?.pages]
  )

  const lastPageUrls = useMemo(() => {
    if (!data?.pages?.length) return [] as string[]
    const last = data.pages[data.pages.length - 1]
    return (
      (last?.data
        ?.map((p: Status) => p.media_attachments?.[0]?.url)
        .filter(Boolean) as string[]) ?? []
    )
  }, [data?.pages])

  const onOpenComments = useCallback((id: string) => router.push(`/post/${id}`), [router])
  const onDeletePost = useCallback(
    (id: string) => deletePostMutation.mutate(id),
    [deletePostMutation]
  )
  // Read posts via a ref so onShare's identity is stable across pagination
  // appends. A direct closure over `posts` would invalidate onShare every
  // time the flatMap produced a fresh array, invalidating the handlers
  // object and forcing TimelineList's renderItem to rebind.
  const postsRef = useRef(posts)
  postsRef.current = posts
  const onShare = useCallback(
    (id: string) => {
      const post = postsRef.current.find((p) => p.id === id)
      handleShare(id, !!post?.reblogged)
    },
    [handleShare]
  )

  const handleEndReached = useCallback(() => {
    if (hasNextPage && !isFetching && !isFetchingNextPage) fetchNextPage()
  }, [hasNextPage, isFetching, isFetchingNextPage, fetchNextPage])

  return {
    posts,
    lastPageUrls,
    isFetching,
    isFetchingNextPage,
    isRefetching,
    hasNextPage,
    status,
    isError,
    error,
    refetch,
    onEndReached: handleEndReached,
    handlers: { onOpenComments, onDeletePost, onShare },
  }
}
