// Protocol Adapter interface.
//
// Halflight is multi-protocol (Pixelfed + Mastodon). Operations whose
// endpoint, request shape, response shape, or fallback chain differs between
// the two live behind this interface. Each protocol provides exactly one
// implementation; dispatch happens at the single `activeProtocol()` chokepoint
// (see `./index.ts`). The runtime `pixelfedActiveContext()` /
// `mastodonActiveContext()` firewall in `src/lib/api-context.ts` stays in
// place as the backstop — when a shared call site routes through the adapter,
// the adapter picks the right concrete implementation, and the firewall
// catches anything that slips past.
//
// The contract is the place to start when adding a new shared operation:
//   1. Add a method here.
//   2. Implement on both `pixelfedAdapter.ts` and `mastodonAdapter.ts`.
//   3. Add a SPEC.md row documenting the per-protocol wire shape.
//   4. Migrate call sites to `activeProtocol().yourOperation(...)`.
//
// TypeScript fails the build if either adapter omits a method, which is the
// single biggest preventive measure this layer buys for free.

import type { Account, Status, Tag } from '../api-types'

export type ProtocolKind = 'pixelfed' | 'mastodon'

/**
 * Parsed handle for cross-protocol account resolution. Built once by
 * `parseHandle` in `./handle.ts` and passed through the adapter so the
 * adapter never has to slice strings.
 */
export type { AccountQueryHandle } from './handle'

/**
 * Result of an account-lookup call. The three-state outcome keeps `rejected`
 * (malformed input, server refused webfinger) distinct from `not-found`
 * (server responded cleanly, no such account) so the UI can pick the right
 * recovery affordance.
 */
export type AccountLookupResult =
  | { kind: 'found'; account: Account }
  | { kind: 'not-found' }
  | { kind: 'rejected'; reason: string }

/**
 * Opaque pagination cursor. Both protocols use `max_id` / `min_id` / `since_id`
 * for status timelines; Mastodon additionally exposes Link-header cursors on
 * some endpoints. The adapter is free to ignore unused fields.
 */
export type Cursor = {
  maxId?: string
  minId?: string
  sinceId?: string
}

export type HashtagFeedOptions = {
  cursor: Cursor
  // Pixelfed defaults `only_media=true` server-side; Mastodon defaults false.
  // The adapter forwards this verbatim so the grid surface stays comparable.
  onlyMedia?: boolean
  // Pixelfed honours sensitivity flags server-side via `_pe=1`; Mastodon
  // returns sensitive posts and clients filter (some users do want them).
  // Default: filter sensitive — matches the photo-grid expectation.
  filterSensitive?: boolean
}

/**
 * One page of a hashtag-tag timeline. `items` is already filtered to the
 * media shape callers expect (the per-protocol filter happens inside the
 * adapter — Pixelfed reads `pf_type`, Mastodon reads `media_attachments[].type`).
 */
export type HashtagFeedPage = {
  items: Status[]
  nextCursor: Cursor | undefined
}

export type SearchAccountsOptions = {
  // When true, Mastodon performs webfinger + ActivityPub `inbox` discovery
  // for accounts the server has never seen — required for remote handle
  // resolution. Rate-limited; the adapter caches results to keep `resolve=true`
  // quotas reasonable.
  resolve: boolean
  limit?: number
}

/**
 * Number of notifications newer than the current marker. Mastodon ≥ 4.3
 * exposes a dedicated `/api/v1/notifications/unread_count` endpoint; Pixelfed
 * derives the count from the marker + `min_id` list (capped at 40).
 */
export type NotificationUnreadCount = number

export interface ProtocolAdapter {
  readonly kind: ProtocolKind

  // ---- Account lookup (Bugs #1, #3) -------------------------------------
  // Canonical entry for tap-a-mention and the "Search for @user@host"
  // recovery affordance. Each adapter implements its own fallback chain;
  // see `SPEC.md` for the wire-level table.
  lookupAccount(
    query: import('./handle').AccountQueryHandle
  ): Promise<AccountLookupResult>

  // ---- Hashtag info (Bug #2) --------------------------------------------
  fetchHashtagInfo(name: string): Promise<Tag>

  // ---- Hashtag timeline (Bug #4) ----------------------------------------
  fetchHashtagFeed(name: string, opts: HashtagFeedOptions): Promise<HashtagFeedPage>

  // ---- Hashtag related --------------------------------------------------
  // Pixelfed exposes `/api/v1/tags/:id/related`; Mastodon has no equivalent.
  // Adapter returns `[]` on Mastodon so UI surfaces degrade gracefully without
  // the caller branching on protocol.
  fetchHashtagRelated(name: string): Promise<Tag[]>

  // ---- Search accounts (Phase 4) ----------------------------------------
  searchAccounts(query: string, opts: SearchAccountsOptions): Promise<Account[]>

  // ---- Notification unread count (Phase 3) -----------------------------
  getNotificationUnreadCount(): Promise<NotificationUnreadCount>
}
