import type { QueryClient } from '@tanstack/react-query'
import type { Status } from 'src/lib/api-types'
import { queryKeys } from 'src/lib/queries/keys'

/**
 * All feed-level query keys that may contain post data.
 *
 * Resolved per call rather than as a module-level constant: every key now
 * starts with the active accountId (queryKeys factory prepends `acct()`),
 * which throws if read before a session exists. Inlining at call time keeps
 * resolution to runtime, when an account is guaranteed to be active.
 */
function postFeedKeys() {
  return [
    queryKeys.posts.homeFeed(),
    // Prefix — matches both 'all' and 'followed' network-feed variants.
    queryKeys.posts.networkFeedPrefix(),
    queryKeys.posts.bookmarks(),
    queryKeys.posts.selfLikes(),
  ]
}

/**
 * Shared utility for optimistically updating a post across all relevant query caches.
 * Used by useLikeMutation, useBookmarkMutation, and useDeletePostMutation.
 */
export function updatePostInCache(
  queryClient: QueryClient,
  postId: string,
  updater: (post: Status) => Status,
  extraKeys?: readonly (readonly string[])[]
) {
  // Single-post cache
  queryClient.setQueryData(queryKeys.posts.byId(postId), (old?: Status) =>
    old ? updater(old) : old
  )

  // All paginated feed caches
  const allFeedKeys = [...postFeedKeys(), ...(extraKeys ?? [])]
  for (const key of allFeedKeys) {
    queryClient.setQueriesData({ queryKey: key }, (old: any) => {
      if (!old?.pages) return old

      let changed = false
      const newPages = old.pages.map((page: any) => {
        // Page is an array directly
        if (Array.isArray(page)) {
          const newData = page.map((post: Status) => {
            if (post.id !== postId) return post
            changed = true
            return updater(post)
          })
          return changed ? newData : page
        }
        // Page has a .data array
        if (Array.isArray(page.data)) {
          let pageChanged = false
          const newData = page.data.map((post: Status) => {
            if (post.id !== postId) return post
            pageChanged = true
            return updater(post)
          })
          if (pageChanged) {
            changed = true
            return { ...page, data: newData }
          }
          return page
        }
        return page
      })
      return changed ? { ...old, pages: newPages } : old
    })
  }
}

/**
 * Snapshot query data for rollback — captures all feed keys + detail + extras.
 */
export function snapshotPostCaches(
  queryClient: QueryClient,
  postId: string,
  extraKeys?: readonly (readonly string[])[]
): Map<string, unknown> {
  const snapshot = new Map<string, unknown>()
  const allKeys = [...postFeedKeys(), queryKeys.posts.byId(postId), ...(extraKeys ?? [])]
  // `getQueriesData` does prefix matching, so `['fetchNetworkFeed']`
  // captures every variant (e.g. 'all', 'followed') in one pass.
  for (const key of allKeys) {
    const pairs = queryClient.getQueriesData({ queryKey: key })
    for (const [matchedKey, data] of pairs) {
      if (data !== undefined) {
        snapshot.set(JSON.stringify(matchedKey), data)
      }
    }
  }
  return snapshot
}

/**
 * Restore query data from a snapshot (used on mutation error).
 */
export function restorePostCaches(
  queryClient: QueryClient,
  snapshot: Map<string, unknown>
) {
  for (const [keyStr, value] of snapshot) {
    queryClient.setQueryData(JSON.parse(keyStr), value)
  }
}
