// Profile UI chrome — per-protocol capability table.
//
// The profile screens render the same React tree on both Pixelfed and
// Mastodon, but a handful of pure-data details vary: which tab options to
// show in the filter menu, which strings are valid tab ids, whether the
// statuses feed renders as a media grid or a vertical list, and which
// React Query cache slice to read for the currently-selected tab.
//
// Today this is two protocols. Adding a third (Misskey, Pleroma, …) is a
// single-record edit on `CHROME_BY_TYPE`. TypeScript's exhaustiveness check
// on `Record<AccountType, ProfileChrome>` ensures no protocol can ship
// without its chrome defined.
//
// Why not on `ProtocolAdapter`? The adapter is the wire-shape abstraction:
// every method hits the network, asserts an active context via the
// `pixelfedActiveContext()` / `mastodonActiveContext()` firewall, and
// returns a typed response envelope. Chrome is pure UI metadata — sync,
// side-effect-free, lookup-table shaped — and it should be reachable
// without an active account (account-picker previews, settings sketches).
// Keeping the two concerns separate preserves the adapter's contract.
//
// Spec reference: §1 layer model (Layer 7 chrome), §7.3 "what you do *not*
// touch" — the chrome is one of the things you *do* touch when adding a
// new protocol; the capability table makes that a 1-record edit.

import type { FilterOption } from 'src/components/menu/intents'
import type { AccountType } from 'src/state/accountStore'

// `posts` and `microblog` are both Pixelfed-shaped values, intentionally
// preserved so both protocols can name the first tab `posts`. On Mastodon
// the single chronological "all posts" stream renders under it; on Pixelfed
// it doubles as the media-grid tab with `only_media=true`. `microblog`
// only exists on Pixelfed.
export type ProfileTab = 'posts' | 'microblog' | 'likes' | 'bookmarks'
export type PublicProfileTab = 'posts' | 'microblog'

// Picks the cache slice for `postQueries.profileStatuses(userId, variant)`.
// Pixelfed splits its profile feed into two independent caches — `grid`
// (server-side `only_media=true`) and `microblog` (no media filter) — so
// the two tabs render their own data without one starving the other.
// Mastodon has a single chronological stream and always uses `all`.
export type ProfileStatusesVariant = 'grid' | 'microblog' | 'all'

/**
 * Per-protocol profile UI chrome. Every screen that varies between
 * protocols should read its variation off this record rather than branching
 * on `accountType` directly.
 */
export type ProfileChrome = {
  // Arrays are not marked readonly so they slot into menu intent types
  // (`FilterIntent.options: FilterOption[]`) without ceremony. They're
  // defined at module scope on the per-protocol objects below and never
  // exposed via a setter, so the practical immutability holds.
  readonly selfTabs: FilterOption[]
  readonly publicTabs: FilterOption[]
  readonly isSelfTab: (v: unknown) => v is ProfileTab
  readonly isPublicTab: (v: unknown) => v is PublicProfileTab
  readonly isGridTab: (tab: string) => boolean
  readonly statusesVariant: (tab: string) => ProfileStatusesVariant
}

const GRID_OPTION: FilterOption = { value: 'posts', label: 'Grid', icon: 'grid' }
const MICROBLOG_OPTION: FilterOption = {
  value: 'microblog',
  label: 'Microblog',
  icon: 'list',
}
// Mastodon's single "Posts" tab — chronological feed including media + text
// + replies + boosts. Reuses the `posts` value so the renderer's existing
// `activeTab === 'posts'` branch still names the right tab; the
// grid-vs-feed decision moves to `chrome.isGridTab`.
const POSTS_OPTION: FilterOption = { value: 'posts', label: 'Posts', icon: 'list' }
const LIKES_OPTION: FilterOption = { value: 'likes', label: 'Likes', icon: 'heart' }
const BOOKMARKS_OPTION: FilterOption = {
  value: 'bookmarks',
  label: 'Bookmarks',
  icon: 'bookmark',
}

const PIXELFED_CHROME: ProfileChrome = {
  selfTabs: [GRID_OPTION, MICROBLOG_OPTION, LIKES_OPTION, BOOKMARKS_OPTION],
  publicTabs: [GRID_OPTION, MICROBLOG_OPTION],
  isSelfTab: (v): v is ProfileTab =>
    v === 'posts' || v === 'microblog' || v === 'likes' || v === 'bookmarks',
  isPublicTab: (v): v is PublicProfileTab => v === 'posts' || v === 'microblog',
  // Pixelfed's `posts` tab is the media grid; `microblog` and the like /
  // bookmark tabs render as a vertical list.
  isGridTab: (tab) => tab === 'posts',
  statusesVariant: (tab) => (tab === 'microblog' ? 'microblog' : 'grid'),
}

// Mastodon profiles render a single chronological stream. `PostGridCell`
// bails on `!media`, so a grid over a text-heavy Mastodon feed reads as a
// sea of holes — never grid. The Microblog tab is also Pixelfed-specific
// terminology and has no Mastodon equivalent, so the tab lists drop it.
const MASTODON_CHROME: ProfileChrome = {
  selfTabs: [POSTS_OPTION, LIKES_OPTION, BOOKMARKS_OPTION],
  publicTabs: [POSTS_OPTION],
  isSelfTab: (v): v is ProfileTab => v === 'posts' || v === 'likes' || v === 'bookmarks',
  isPublicTab: (v): v is PublicProfileTab => v === 'posts',
  isGridTab: () => false,
  statusesVariant: () => 'all',
}

// `Record<AccountType, …>` makes the table exhaustive: TypeScript fails the
// build if a protocol is added to `AccountType` without a chrome entry.
const CHROME_BY_TYPE: Readonly<Record<AccountType, ProfileChrome>> = {
  pixelfed: PIXELFED_CHROME,
  mastodon: MASTODON_CHROME,
}

/**
 * Resolve the profile chrome for a given account type. Pure lookup — cheap
 * enough to call inline, though consumers often `useMemo` it once and pass
 * the resulting object around.
 */
export function profileChrome(accountType: AccountType): ProfileChrome {
  return CHROME_BY_TYPE[accountType]
}
