/** Media post types — shared filter used by profile grids and hashtag feeds */
export const MEDIA_POST_TYPES = ['photo', 'photo:album', 'video'] as const

export function isMediaPost(status: {
  pf_type?: string
  media_attachments: unknown[]
}): boolean {
  return (
    MEDIA_POST_TYPES.includes(status.pf_type as any) &&
    status.media_attachments.length > 0
  )
}

/**
 * Writing-class pf_type values — text, articles, events. Quotes are layered
 * on top of an underlying post (pf_type carries the host type + the `quote`
 * sidecar provides the relation) and are matched separately via `post.quote`.
 */
export const WRITING_POST_TYPES = ['text', 'article', 'event'] as const

/**
 * A single lens taxonomy over the Home timeline. `writing` collapses the
 * previous text / quotes / articles lenses into one category; events retain
 * a standalone lens because of their distinct card UI and empty-state CTA.
 * Boosts (reblogs) are judged by the inner status so a boosted event still
 * matches `events`.
 */
export type PostLens = 'photos' | 'events' | 'writing'

type LensInput = {
  pf_type?: string
  media_attachments?: unknown[]
  reblog?: { pf_type?: string; media_attachments?: unknown[] } | null
  quote?: { state?: string } | null
}

function isWritingPost(post: LensInput): boolean {
  const effective = post.reblog ?? post
  const t = effective.pf_type
  if (WRITING_POST_TYPES.includes(t as any)) return true
  if (post.quote?.state) return true
  // Defensive fallback: a federated status that arrives without a pf_type and
  // carries no media is still writing — keeps the lens honest when upstream
  // transformers drop the field.
  return !t && (effective.media_attachments?.length ?? 0) === 0
}

export function matchesLens(post: LensInput, lens: PostLens): boolean {
  const effective = post.reblog ?? post
  const t = effective.pf_type
  const mediaCount = effective.media_attachments?.length ?? 0
  switch (lens) {
    case 'photos':
      return MEDIA_POST_TYPES.includes(t as any) && mediaCount > 0
    case 'events':
      return t === 'event'
    case 'writing':
      return isWritingPost(post)
    default:
      return true
  }
}

/** Shared cursor calculation — replaces duplicates in posts.ts and hashtags.ts */
export function nextCursorFromLowestId(items: { id: string }[]): string | undefined {
  if (items.length === 0) return undefined
  const lowest = items.reduce((min, item) =>
    BigInt(item.id) < BigInt(min.id) ? item : min
  )
  return (BigInt(lowest.id) - 1n).toString()
}

/** Typed response shapes */
export type LinkPaginatedResponse<T> = {
  data: T[]
  nextPage: string | undefined
  prevPage: string | undefined
}

export type CursorPaginatedResponse<T> = {
  data: T[]
  nextCursor: string | undefined
}

/** Reusable getNextPageParam functions */
export const linkPageParam = (lastPage: LinkPaginatedResponse<unknown>) =>
  lastPage.nextPage

export const cursorPageParam = (lastPage: CursorPaginatedResponse<unknown>) =>
  lastPage.nextCursor

export const pageNumberParam = <T>(lastPage: { data: T[] }, allPages: { data: T[] }[]) =>
  lastPage.data.length > 0 ? allPages.length + 1 : undefined
