// Shape of the /api/v1.2/stories/carousel response, typed narrowly around
// what the rail + viewer actually read. Backend adds extra fields; we don't
// model them until a surface needs them.

export type StoryMediaType = 'photo' | 'video'

export type StoryNode = {
  id: string
  pid?: string
  type: StoryMediaType
  src: string
  duration: number
  seen?: boolean
  created_at?: string
  preview?: string | null
  // Passthrough fields from raw v1.2 response so downstream consumers that
  // still read them (e.g. SelfStoriesModal's Story interface) keep working.
  url?: string
  viewed?: boolean
  view_count?: number
}

export type StoryGroup = {
  id?: string
  pid: string
  username?: string
  acct?: string
  avatar?: string
  local?: boolean
  seen?: boolean
  latest?: string
  nodes: StoryNode[]
}

export type StoryCarousel = {
  self: StoryNode[]
  nodes: StoryGroup[]
}

// The /api/v1.2/stories/carousel response does not match v1.1: author's stories
// are embedded in `nodes` with `is_author: true` (no top-level `self`), each
// group uses `profile_id`/`stories` rather than `pid`/`nodes`, and per-story
// fields are `url`/`viewed`/`duration (ms)` rather than `src`/`seen`/seconds.
// This normalizer collapses both shapes into one, and synthesizes `self` from
// the v1.2 author group so the viewer + rail can treat both responses the same.
export function normalizeCarousel(raw: unknown): StoryCarousel {
  const obj = (raw ?? {}) as Record<string, unknown>
  const rawNodes = Array.isArray(obj.nodes) ? (obj.nodes as unknown[]) : []

  const groups: StoryGroup[] = []
  let selfNodes: StoryNode[] = []

  for (const entry of rawNodes) {
    if (!entry || typeof entry !== 'object') continue
    const g = entry as Record<string, unknown>
    const user = (g.user as Record<string, unknown> | undefined) ?? {}

    const pidRaw = g.profile_id ?? g.pid ?? user.id
    if (pidRaw == null) continue
    const pid = String(pidRaw)

    const storiesRaw = Array.isArray(g.stories)
      ? (g.stories as unknown[])
      : Array.isArray(g.nodes)
        ? (g.nodes as unknown[])
        : []
    const storyNodes = storiesRaw.map(normalizeStoryNode).filter((n) => n.id)

    const isAuthor = Boolean(g.is_author ?? user.is_author ?? false)

    if (isAuthor) {
      // Pull the author's stories out into `self` and skip the group so the
      // viewer's self-synthesis and the rail's "others" list stay clean.
      selfNodes = storyNodes
      continue
    }

    groups.push({
      id: g.id != null ? String(g.id) : undefined,
      pid,
      username:
        (g.username as string | undefined) ?? (user.username as string | undefined),
      acct:
        (g.username_acct as string | undefined) ??
        (user.username_acct as string | undefined),
      avatar: (g.avatar as string | undefined) ?? (user.avatar as string | undefined),
      local: Boolean(g.local ?? user.local ?? false),
      seen: Boolean(g.hasViewed ?? g.seen ?? false),
      latest: g.latest as string | undefined,
      nodes: storyNodes,
    })
  }

  // v1.1 fallback: the legacy endpoint returned `self` as either an array of
  // nodes or an object `{ user, nodes }`. Only consult it if we didn't already
  // derive self from a v1.2 is_author group.
  if (selfNodes.length === 0) {
    if (Array.isArray(obj.self)) {
      selfNodes = (obj.self as unknown[]).map(normalizeStoryNode).filter((n) => n.id)
    } else if (obj.self && typeof obj.self === 'object') {
      const selfObj = obj.self as Record<string, unknown>
      if (Array.isArray(selfObj.nodes)) {
        selfNodes = (selfObj.nodes as unknown[])
          .map(normalizeStoryNode)
          .filter((n) => n.id)
      }
    }
  }

  return { self: selfNodes, nodes: groups }
}

function normalizeStoryNode(raw: unknown): StoryNode {
  const s = (raw ?? {}) as Record<string, unknown>
  const rawDuration = typeof s.duration === 'number' ? s.duration : 0
  // v1.2 returns milliseconds (≥ 1000); v1.1 returns seconds (typically 3–30).
  // Anything over 120 is assumed to be ms.
  const duration =
    rawDuration > 120 ? Math.round(rawDuration / 1000) : rawDuration > 0 ? rawDuration : 3

  const src = String(s.src ?? s.url ?? '')
  const viewed = Boolean(s.viewed ?? s.seen ?? false)

  return {
    id: s.id != null ? String(s.id) : '',
    pid: s.pid != null ? String(s.pid) : undefined,
    type: s.type === 'video' ? 'video' : 'photo',
    src,
    duration,
    seen: viewed,
    created_at: s.created_at as string | undefined,
    preview: (s.preview as string | null | undefined) ?? null,
    url: typeof s.url === 'string' ? s.url : src || undefined,
    viewed,
    view_count: typeof s.view_count === 'number' ? s.view_count : 0,
  }
}
