'use client'

import { useCallback, useEffect, useMemo, useRef, useState } from 'react'
import {
  ChevronDown,
  ChevronLeft,
  ChevronRight,
  ChevronUp,
  Loader2,
  Search,
  Settings,
  Trash2,
  X,
} from 'lucide-react'
import { toast } from 'sonner'
import {
  bulkDeletePosts,
  bulkUpdatePosts,
  fetchPosts,
  type Post,
  type PostStatus,
  type PostsResult,
  type PostType,
} from '../../lib/posts-service.js'
import {
  PostStatusBadge,
  PostTypeBadge,
  SeoScoreIndicator,
  paletteFor,
} from '../../components/posts/badges.js'
import { ConfirmDialog } from '../../components/ui/ConfirmDialog.js'

/**
 * All Posts management surface — table with type tabs, controls row,
 * sortable headers, bulk actions, pagination. Data flows through the
 * posts-service so swapping to a server endpoint later only touches
 * the service file.
 *
 * Routing:
 *   - Row click → /posts/{postTypeId}/{postId}/edit (the section-based Post
 *     editor, seeded from the post type's template).
 *   - "Manage types" → /posts/types.
 *   - "+ New Post" lives in the top bar (computePageActions); the page
 *     itself doesn't render that button.
 */

interface PostsListViewProps {
  onNavigate: (path: string) => void
}

type SortBy = 'title' | 'type' | 'author' | 'status' | 'date' | 'seo'
type Dir = 'asc' | 'desc'

const PAGE_SIZE = 10

export function PostsListView({ onNavigate }: PostsListViewProps) {
  const [activeType, setActiveType] = useState<string>('all')
  const [search, setSearch] = useState('')
  const [debouncedSearch, setDebouncedSearch] = useState('')
  const [status, setStatus] = useState<PostStatus | 'all'>('all')
  const [author, setAuthor] = useState<string>('all')
  const [sortBy, setSortBy] = useState<SortBy>('date')
  const [dir, setDir] = useState<Dir>('desc')
  const [page, setPage] = useState(1)
  const [selected, setSelected] = useState<Set<string>>(new Set())

  const [data, setData] = useState<PostsResult | null>(null)
  const [loading, setLoading] = useState(true)
  const [error, setError] = useState<string | null>(null)
  const [refreshToken, setRefreshToken] = useState(0)
  const [pendingBulk, setPendingBulk] = useState<null | 'delete' | 'publish' | 'draft'>(null)

  const debounceTimer = useRef<ReturnType<typeof setTimeout> | null>(null)
  useEffect(() => {
    if (debounceTimer.current) clearTimeout(debounceTimer.current)
    debounceTimer.current = setTimeout(() => {
      setDebouncedSearch(search.trim())
      setPage(1)
    }, 250)
    return () => {
      if (debounceTimer.current) clearTimeout(debounceTimer.current)
    }
  }, [search])

  // Re-fetch when any input that affects the result set changes.
  useEffect(() => {
    let cancelled = false
    setLoading(true)
    setError(null)
    fetchPosts({
      postType: activeType,
      status,
      author,
      search: debouncedSearch,
      sortBy,
      sortDirection: dir,
      page,
      pageSize: PAGE_SIZE,
    })
      .then((res) => {
        if (cancelled) return
        setData(res)
        setLoading(false)
      })
      .catch((err: unknown) => {
        if (cancelled) return
        setError(err instanceof Error ? err.message : 'Failed to load posts')
        setLoading(false)
      })
    return () => {
      cancelled = true
    }
  }, [activeType, status, author, debouncedSearch, sortBy, dir, page, refreshToken])

  // Whenever the active type tab changes, reset paging to page 1.
  useEffect(() => {
    setPage(1)
    setSelected(new Set())
  }, [activeType, debouncedSearch, status, author])

  // Selection is scoped to the rows visible on the current page: bulk
  // actions only ever touch `selectedPosts` (the current page's selected
  // rows), so clear the selection whenever the page changes. Without this
  // the bulk bar count keeps counting rows from earlier pages while the
  // publish/delete handlers silently operate on nothing.
  useEffect(() => {
    setSelected(new Set())
  }, [page])

  const total = data?.total ?? 0
  const totalAll = useMemo(
    () => (data?.types ?? []).reduce((sum, t) => sum + t.postCount, 0),
    [data?.types],
  )
  const totalPages = Math.max(1, Math.ceil(total / PAGE_SIZE))
  const posts = data?.posts ?? []
  const types = data?.types ?? []
  const authors = data?.authors ?? []

  const handleSort = useCallback(
    (field: SortBy) => {
      if (sortBy === field) {
        setDir(dir === 'asc' ? 'desc' : 'asc')
      } else {
        setSortBy(field)
        setDir(field === 'date' || field === 'seo' ? 'desc' : 'asc')
      }
    },
    [sortBy, dir],
  )

  const clearFilters = () => {
    setSearch('')
    setStatus('all')
    setAuthor('all')
    setActiveType('all')
    setPage(1)
  }

  const filtersAreActive =
    debouncedSearch !== '' || status !== 'all' || author !== 'all' || activeType !== 'all'

  // ─── Selection / bulk actions ─────────────────────────────────────

  const toggleOne = (id: string) =>
    setSelected((s) => {
      const next = new Set(s)
      if (next.has(id)) next.delete(id)
      else next.add(id)
      return next
    })

  const allOnPageSelected = posts.length > 0 && posts.every((p) => selected.has(p.id))
  const toggleAllOnPage = (checked: boolean) =>
    setSelected((s) => {
      const next = new Set(s)
      if (checked) for (const p of posts) next.add(p.id)
      else for (const p of posts) next.delete(p.id)
      return next
    })

  const selectedPosts = useMemo(() => posts.filter((p) => selected.has(p.id)), [posts, selected])

  const runBulkUpdate = async (target: PostStatus) => {
    const ids = selectedPosts.map((p) => ({ postTypeId: p.postTypeId, id: p.id }))
    if (ids.length === 0) return
    const verb = target === 'PUBLISHED' ? 'Publishing' : 'Updating'
    const promise = bulkUpdatePosts(ids, { status: target })
    toast.promise(promise, {
      loading: `${verb} ${ids.length} post${ids.length === 1 ? '' : 's'}…`,
      success: (r) => `${r.ok} updated${r.failed ? `, ${r.failed} failed` : ''}`,
      error: 'Bulk update failed',
    })
    await promise
    setSelected(new Set())
    setRefreshToken((t) => t + 1)
  }

  const runBulkDelete = async () => {
    const ids = selectedPosts.map((p) => ({ postTypeId: p.postTypeId, id: p.id }))
    if (ids.length === 0) return
    const promise = bulkDeletePosts(ids)
    toast.promise(promise, {
      loading: `Deleting ${ids.length} post${ids.length === 1 ? '' : 's'}…`,
      success: (r) => `${r.ok} deleted${r.failed ? `, ${r.failed} failed` : ''}`,
      error: 'Bulk delete failed',
    })
    await promise
    setSelected(new Set())
    setRefreshToken((t) => t + 1)
  }

  // ─── Render ───────────────────────────────────────────────────────

  return (
    <div className="flex h-full flex-col gap-4 p-4 sm:p-6">
      {/* Header */}
      <div className="flex flex-col gap-1">
        <h1 className="text-foreground text-xl font-medium tracking-tight sm:text-2xl">Posts</h1>
        <p className="text-muted-foreground text-sm">
          {totalAll} total post{totalAll === 1 ? '' : 's'} across all types
        </p>
      </div>

      {/* Type tabs */}
      <TypeTabs types={types} totalAll={totalAll} active={activeType} onChange={setActiveType} />

      {/* Controls row */}
      <ControlsRow
        search={search}
        onSearchChange={setSearch}
        status={status}
        onStatusChange={(s) => {
          setStatus(s)
          setPage(1)
        }}
        author={author}
        onAuthorChange={(a) => {
          setAuthor(a)
          setPage(1)
        }}
        authors={authors}
        resultCount={total}
        onManageTypes={() => onNavigate('/posts/types')}
      />

      {/* Bulk action bar */}
      {selected.size > 0 && (
        <BulkActionBar
          count={selected.size}
          onClear={() => setSelected(new Set())}
          onPublish={() => runBulkUpdate('PUBLISHED')}
          onDraft={() => runBulkUpdate('DRAFT')}
          onDelete={() => setPendingBulk('delete')}
        />
      )}

      {/* Table card */}
      <div className="bg-card border-border overflow-hidden rounded-xl border shadow-sm">
        {/* Horizontal scroll on narrow viewports: the fixed columns keep their
            width and the table scrolls sideways instead of crushing content. */}
        <div className="overflow-x-auto">
          <table className="w-full min-w-[760px] table-fixed" role="table" aria-label="Posts">
            <colgroup>
              <col style={{ width: '40px' }} />
              <col />
              <col style={{ width: '140px' }} />
              <col style={{ width: '180px' }} />
              <col style={{ width: '130px' }} />
              <col style={{ width: '120px' }} />
              <col style={{ width: '90px' }} />
            </colgroup>
            <thead className="border-border bg-muted/40 border-b">
              <tr className="text-muted-foreground text-xs">
                <th scope="col" className="px-3 py-2.5 text-left">
                  <input
                    type="checkbox"
                    aria-label="Select all posts on this page"
                    checked={allOnPageSelected}
                    onChange={(e) => toggleAllOnPage(e.target.checked)}
                    className="border-border accent-primary rounded"
                  />
                </th>
                <th scope="col" className="px-3 py-2.5 text-left">
                  <SortHeader
                    label="Title"
                    field="title"
                    current={sortBy}
                    dir={dir}
                    onSort={handleSort}
                  />
                </th>
                <th scope="col" className="px-3 py-2.5 text-left">
                  <SortHeader
                    label="Type"
                    field="type"
                    current={sortBy}
                    dir={dir}
                    onSort={handleSort}
                  />
                </th>
                <th scope="col" className="px-3 py-2.5 text-left">
                  <SortHeader
                    label="Author"
                    field="author"
                    current={sortBy}
                    dir={dir}
                    onSort={handleSort}
                  />
                </th>
                <th scope="col" className="px-3 py-2.5 text-left">
                  <SortHeader
                    label="Status"
                    field="status"
                    current={sortBy}
                    dir={dir}
                    onSort={handleSort}
                  />
                </th>
                <th scope="col" className="px-3 py-2.5 text-left">
                  <SortHeader
                    label="Date"
                    field="date"
                    current={sortBy}
                    dir={dir}
                    onSort={handleSort}
                  />
                </th>
                <th scope="col" className="px-3 py-2.5 text-left">
                  <SortHeader
                    label="SEO"
                    field="seo"
                    current={sortBy}
                    dir={dir}
                    onSort={handleSort}
                  />
                </th>
              </tr>
            </thead>
            <tbody>
              {loading && posts.length === 0 && <LoadingRows />}
              {!loading && error && (
                <tr>
                  <td colSpan={7} className="px-3 py-12 text-center">
                    <div className="text-foreground mb-2 text-sm font-medium">{error}</div>
                    <button
                      onClick={() => setRefreshToken((t) => t + 1)}
                      className="text-primary text-sm hover:underline"
                    >
                      Try again
                    </button>
                  </td>
                </tr>
              )}
              {!loading && !error && posts.length === 0 && (
                <EmptyRow
                  filtersActive={filtersAreActive}
                  onClear={clearFilters}
                  onNewPost={() => onNavigate('/posts/new')}
                />
              )}
              {!loading &&
                !error &&
                posts.map((p) => (
                  <PostRow
                    key={p.id}
                    post={p}
                    selected={selected.has(p.id)}
                    onSelect={() => toggleOne(p.id)}
                    onOpen={() => onNavigate(`/posts/${p.postTypeId}/${p.id}/edit`)}
                  />
                ))}
            </tbody>
          </table>
        </div>

        {/* Pagination footer */}
        <PaginationFooter
          page={page}
          totalPages={totalPages}
          total={total}
          pageSize={PAGE_SIZE}
          onPage={setPage}
        />
      </div>

      <ConfirmDialog
        open={pendingBulk === 'delete'}
        title={`Delete ${selected.size} post${selected.size === 1 ? '' : 's'}?`}
        description="This moves the selected posts to trash. They can be restored from the per-type list for 30 days."
        confirmLabel="Delete"
        destructive
        onClose={() => setPendingBulk(null)}
        onConfirm={() => {
          void runBulkDelete()
        }}
      />
    </div>
  )
}

// ─── Sub-components ──────────────────────────────────────────────────

function SortHeader({
  label,
  field,
  current,
  dir,
  onSort,
}: {
  label: string
  field: SortBy
  current: SortBy
  dir: Dir
  onSort: (f: SortBy) => void
}) {
  const active = current === field
  const ariaSort = active ? (dir === 'asc' ? 'ascending' : 'descending') : 'none'
  return (
    <button
      type="button"
      onClick={() => onSort(field)}
      aria-sort={ariaSort}
      className={`hover:text-foreground inline-flex items-center gap-1 text-xs font-medium tracking-wider uppercase transition-colors ${
        active ? 'text-foreground' : 'text-muted-foreground'
      }`}
    >
      <span>{label}</span>
      {active &&
        (dir === 'asc' ? (
          <ChevronUp className="h-3 w-3" aria-hidden />
        ) : (
          <ChevronDown className="h-3 w-3" aria-hidden />
        ))}
    </button>
  )
}

function TypeTabs({
  types,
  totalAll,
  active,
  onChange,
}: {
  types: PostType[]
  totalAll: number
  active: string
  onChange: (slug: string) => void
}) {
  return (
    <div
      role="tablist"
      aria-label="Filter posts by type"
      className="border-border flex flex-wrap items-center gap-1 border-b pb-px"
    >
      <TabPill
        label="All Posts"
        count={totalAll}
        active={active === 'all'}
        onClick={() => onChange('all')}
      />
      {types.map((t) => (
        <TabPill
          key={t.slug}
          label={t.name}
          count={t.postCount}
          active={active === t.slug}
          onClick={() => onChange(t.slug)}
        />
      ))}
    </div>
  )
}

function TabPill({
  label,
  count,
  active,
  onClick,
}: {
  label: string
  count: number
  active: boolean
  onClick: () => void
}) {
  return (
    <button
      type="button"
      role="tab"
      aria-selected={active}
      onClick={onClick}
      className={`relative inline-flex items-center gap-2 rounded-md px-3 py-2 text-sm font-medium transition-colors ${
        active
          ? 'bg-primary/10 text-primary'
          : 'text-muted-foreground hover:text-foreground hover:bg-accent'
      }`}
    >
      <span>{label}</span>
      <span
        className={`rounded-full px-1.5 text-xs tabular-nums ${
          active ? 'bg-primary/15 text-primary' : 'bg-muted text-muted-foreground'
        }`}
      >
        {count}
      </span>
    </button>
  )
}

function ControlsRow({
  search,
  onSearchChange,
  status,
  onStatusChange,
  author,
  onAuthorChange,
  authors,
  resultCount,
  onManageTypes,
}: {
  search: string
  onSearchChange: (v: string) => void
  status: PostStatus | 'all'
  onStatusChange: (s: PostStatus | 'all') => void
  author: string
  onAuthorChange: (a: string) => void
  authors: { id: string; name: string }[]
  resultCount: number
  onManageTypes: () => void
}) {
  return (
    <div className="flex flex-wrap items-center gap-2">
      <div className="relative max-w-sm flex-1">
        <Search
          className="text-muted-foreground absolute top-1/2 left-3 h-4 w-4 -translate-y-1/2"
          aria-hidden
        />
        <input
          type="search"
          value={search}
          onChange={(e) => onSearchChange(e.target.value)}
          aria-label="Search posts"
          placeholder="Search posts…"
          className="border-border bg-card text-foreground placeholder:text-muted-foreground focus:border-primary focus:ring-primary/30 w-full rounded-lg border py-2 pr-3 pl-9 text-sm shadow-sm focus:ring-2 focus:outline-none"
        />
      </div>

      <FilterSelect
        label="Status"
        value={status}
        onChange={(v) => onStatusChange(v as PostStatus | 'all')}
        options={[
          { value: 'all', label: 'All' },
          { value: 'PUBLISHED', label: 'Published' },
          { value: 'DRAFT', label: 'Draft' },
          { value: 'SCHEDULED', label: 'Scheduled' },
        ]}
      />

      <FilterSelect
        label="Author"
        value={author}
        onChange={onAuthorChange}
        options={[
          { value: 'all', label: 'All' },
          ...authors.map((a) => ({ value: a.id, label: a.name })),
        ]}
      />

      <div className="ml-auto flex items-center gap-3">
        <button
          type="button"
          onClick={onManageTypes}
          className="border-border bg-background text-foreground hover:bg-accent inline-flex items-center gap-1.5 rounded-lg border px-3 py-1.5 text-sm font-medium transition-colors"
        >
          <Settings className="h-3.5 w-3.5" aria-hidden />
          Manage types
        </button>
        <span className="text-muted-foreground text-sm tabular-nums">
          {resultCount} {resultCount === 1 ? 'result' : 'results'}
        </span>
      </div>
    </div>
  )
}

function FilterSelect({
  label,
  value,
  onChange,
  options,
}: {
  label: string
  value: string
  onChange: (v: string) => void
  options: { value: string; label: string }[]
}) {
  return (
    <label className="text-foreground inline-flex items-center gap-1.5 text-sm">
      <span className="text-muted-foreground">{label}:</span>
      <select
        value={value}
        onChange={(e) => onChange(e.target.value)}
        className="border-border bg-card text-foreground focus:border-primary focus:ring-primary/30 rounded-lg border px-2.5 py-1.5 text-sm shadow-sm focus:ring-2 focus:outline-none"
      >
        {options.map((o) => (
          <option key={o.value} value={o.value}>
            {o.label}
          </option>
        ))}
      </select>
    </label>
  )
}

function BulkActionBar({
  count,
  onClear,
  onPublish,
  onDraft,
  onDelete,
}: {
  count: number
  onClear: () => void
  onPublish: () => void
  onDraft: () => void
  onDelete: () => void
}) {
  return (
    <div className="border-primary/30 bg-primary/5 flex items-center gap-2 rounded-lg border px-4 py-2">
      <span className="text-foreground text-sm font-medium">{count} selected</span>
      <div className="bg-border h-4 w-px" aria-hidden />
      <button
        type="button"
        onClick={onPublish}
        className="text-foreground hover:bg-accent rounded-md px-2 py-1 text-sm transition-colors"
      >
        Publish
      </button>
      <button
        type="button"
        onClick={onDraft}
        className="text-foreground hover:bg-accent rounded-md px-2 py-1 text-sm transition-colors"
      >
        Move to draft
      </button>
      <button
        type="button"
        onClick={onDelete}
        className="text-destructive hover:bg-destructive/10 inline-flex items-center gap-1 rounded-md px-2 py-1 text-sm transition-colors"
      >
        <Trash2 className="h-3.5 w-3.5" aria-hidden />
        Delete
      </button>
      <button
        type="button"
        onClick={onClear}
        className="text-muted-foreground hover:text-foreground ml-auto inline-flex items-center gap-1 rounded-md px-2 py-1 text-sm transition-colors"
      >
        <X className="h-3.5 w-3.5" aria-hidden />
        Clear
      </button>
    </div>
  )
}

function PostRow({
  post,
  selected,
  onSelect,
  onOpen,
}: {
  post: Post
  selected: boolean
  onSelect: () => void
  onOpen: () => void
}) {
  const p = paletteFor(post.postTypeAccentColor)
  return (
    <tr
      className={`border-border hover:bg-muted/30 group border-b transition-colors last:border-b-0 ${
        selected ? 'bg-primary/5' : ''
      }`}
    >
      <td className="px-3 py-3" onClick={(e) => e.stopPropagation()}>
        <input
          type="checkbox"
          aria-label={`Select "${post.title}"`}
          checked={selected}
          onChange={onSelect}
          className="border-border accent-primary rounded"
        />
      </td>
      <td className="px-3 py-3">
        <button type="button" onClick={onOpen} className="flex w-full items-center gap-3 text-left">
          <span aria-hidden className={`block h-9 w-9 shrink-0 rounded-md ${p.bg.split(' ')[0]}`} />
          <span className="min-w-0">
            <span className="text-foreground group-hover:text-primary block truncate text-sm font-medium">
              {post.title}
            </span>
            <span className="text-muted-foreground block truncate text-xs">
              /{post.postTypeId}/{post.slug || '—'}
            </span>
          </span>
        </button>
      </td>
      <td className="px-3 py-3">
        <PostTypeBadge label={post.postTypeName} color={post.postTypeAccentColor} />
      </td>
      <td className="px-3 py-3">
        <div className="flex items-center gap-2">
          <span
            aria-hidden
            className="bg-muted text-muted-foreground inline-flex h-7 w-7 items-center justify-center rounded-full text-[11px] font-medium"
          >
            {post.authorInitials}
          </span>
          <span className="text-foreground truncate text-sm">{post.authorName}</span>
        </div>
      </td>
      <td className="px-3 py-3">
        <PostStatusBadge status={post.status} />
      </td>
      <td className="text-muted-foreground px-3 py-3 text-sm tabular-nums">
        {formatDateShort(post.updatedDate)}
      </td>
      <td className="px-3 py-3">
        <SeoScoreIndicator score={post.seoScore} />
      </td>
    </tr>
  )
}

function LoadingRows() {
  const rows = Array.from({ length: 8 })
  return (
    <>
      {rows.map((_, i) => (
        <tr key={i} className="border-border border-b last:border-b-0">
          {Array.from({ length: 7 }).map((__, j) => (
            <td key={j} className="px-3 py-3">
              <div className="bg-muted h-4 w-full animate-pulse rounded" />
            </td>
          ))}
        </tr>
      ))}
    </>
  )
}

function EmptyRow({
  filtersActive,
  onClear,
  onNewPost,
}: {
  filtersActive: boolean
  onClear: () => void
  onNewPost: () => void
}) {
  return (
    <tr>
      <td colSpan={7} className="px-3 py-16 text-center">
        <div className="text-foreground mb-2 text-sm font-medium">
          {filtersActive ? 'No posts match the current filters.' : 'No posts yet.'}
        </div>
        <div className="text-muted-foreground mb-4 text-sm">
          {filtersActive
            ? 'Try clearing the search or filters above.'
            : 'Create your first post to get started.'}
        </div>
        {filtersActive ? (
          <button type="button" onClick={onClear} className="text-primary text-sm hover:underline">
            Clear filters
          </button>
        ) : (
          <button
            type="button"
            onClick={onNewPost}
            className="bg-primary text-primary-foreground hover:bg-primary/90 inline-flex items-center gap-1.5 rounded-lg px-3.5 py-1.5 text-sm font-medium shadow-sm transition-colors"
          >
            New Post
          </button>
        )}
      </td>
    </tr>
  )
}

function PaginationFooter({
  page,
  totalPages,
  total,
  pageSize,
  onPage,
}: {
  page: number
  totalPages: number
  total: number
  pageSize: number
  onPage: (p: number) => void
}) {
  const start = total === 0 ? 0 : (page - 1) * pageSize + 1
  const end = Math.min(total, page * pageSize)

  return (
    <div className="border-border flex items-center justify-between border-t px-4 py-3 text-sm">
      <span className="text-muted-foreground tabular-nums">
        {start}–{end} of {total} post{total === 1 ? '' : 's'}
      </span>
      <nav className="flex items-center gap-1" aria-label="Posts pagination">
        <button
          type="button"
          onClick={() => onPage(Math.max(1, page - 1))}
          disabled={page === 1}
          aria-label="Previous page"
          className="border-border bg-background text-foreground hover:bg-accent inline-flex h-8 w-8 items-center justify-center rounded-md border transition-colors disabled:cursor-not-allowed disabled:opacity-40"
        >
          <ChevronLeft className="h-4 w-4" aria-hidden />
        </button>
        {pageNumbers(page, totalPages).map((p, idx) =>
          p === '…' ? (
            <span
              key={`gap-${idx}`}
              aria-hidden
              className="text-muted-foreground inline-flex h-8 w-8 items-center justify-center text-sm"
            >
              …
            </span>
          ) : (
            <button
              key={p}
              type="button"
              onClick={() => onPage(p)}
              aria-current={p === page ? 'page' : undefined}
              className={`inline-flex h-8 w-8 items-center justify-center rounded-md text-sm tabular-nums transition-colors ${
                p === page
                  ? 'bg-primary text-primary-foreground'
                  : 'border-border bg-background text-foreground hover:bg-accent border'
              }`}
            >
              {p}
            </button>
          ),
        )}
        <button
          type="button"
          onClick={() => onPage(Math.min(totalPages, page + 1))}
          disabled={page === totalPages}
          aria-label="Next page"
          className="border-border bg-background text-foreground hover:bg-accent inline-flex h-8 w-8 items-center justify-center rounded-md border transition-colors disabled:cursor-not-allowed disabled:opacity-40"
        >
          <ChevronRight className="h-4 w-4" aria-hidden />
        </button>
      </nav>
    </div>
  )
}

// ─── Helpers ─────────────────────────────────────────────────────────

function formatDateShort(input: string): string {
  const d = new Date(input)
  if (Number.isNaN(d.getTime())) return '—'
  return d.toLocaleDateString(undefined, { month: 'short', day: 'numeric', year: 'numeric' })
}

/**
 * Return up to 7 page-control entries: a sliding window around the
 * current page with ellipsis markers. Keeps the footer narrow even on
 * collections with hundreds of pages.
 */
export function pageNumbers(current: number, total: number): Array<number | '…'> {
  if (total <= 7) return Array.from({ length: total }, (_, i) => i + 1)
  const out: Array<number | '…'> = [1]
  const start = Math.max(2, current - 1)
  const end = Math.min(total - 1, current + 1)
  if (start > 2) out.push('…')
  for (let i = start; i <= end; i++) out.push(i)
  if (end < total - 1) out.push('…')
  out.push(total)
  return out
}
