import {
  feedItemId,
  FeedItemSchema,
  type Crumb,
  type FeedItem,
} from '@crumbs/core/schemas'

import {
  crumbDropped,
  crumbSynced,
  trailDropped,
  trailPatched,
} from '@/lib/entity-sync'
import {unwrap} from '@/lib/errors'
import {withPatchedTrail, withSyncedCrumb} from '@/lib/feed-items'
import {
  createPagedStore,
  pagedHooks,
  pageParams,
} from '@/lib/paged-resource'
import {rpc} from '@/lib/rpc'
import {fromSchema} from '@/lib/snapshot-cache'
import {pagedSnapshot} from '@/lib/snapshots'

export type {FeedItem}

// recent activity by the viewer and their friends: cursor-paged via the
// shared primitive. item ids are type-prefixed (`crumb:<id>`), entity ids
// live on the nested crumb/trail
export const useFeedStore = createPagedStore<FeedItem>(
  async (cursor) => {
    const {items, nextCursor} = await unwrap(
      rpc.feed.$get({query: pageParams(cursor)}),
    )
    return {items, nextCursor}
  },
  pagedSnapshot('feed', 1, fromSchema(FeedItemSchema)),
)

// a new crumb or trail needs its server-built author + item id, so mutations
// refetch the first page rather than prepending locally
export const refreshFeed = () => useFeedStore.getState().load()

crumbSynced.subscribe((crumb) =>
  useFeedStore.setState((s) => ({
    items: s.items.map(withSyncedCrumb(crumb)),
  })),
)
crumbDropped.subscribe((id) =>
  useFeedStore.getState().removeItem(feedItemId('crumb', id)),
)
trailPatched.subscribe(({id, patch}) =>
  useFeedStore.setState((s) => ({
    items: s.items.map(withPatchedTrail(id, patch)),
  })),
)
trailDropped.subscribe((id) =>
  useFeedStore.getState().removeItem(feedItemId('trail', id)),
)

export const feedList = pagedHooks(useFeedStore)
export const {
  useItems: useFeed,
  useLoading: useFeedLoading,
  useLoadingMore: useFeedLoadingMore,
  useReachedEnd: useFeedReachedEnd,
  useError: useFeedError,
  useLoad: useLoadFeed,
  useLoadMore: useLoadMoreFeed,
} = feedList

// the freshest loaded copy of a crumb in the feed, for detail fallbacks
export const useFeedCrumb = (id: string): Crumb | undefined =>
  useFeedStore((s) => {
    const item = s.items.find(
      (i) => i.type === 'crumb' && i.crumb.id === id,
    )
    return item?.type === 'crumb' ? item.crumb : undefined
  })
