// Catalog.jsx — the §7 left pane.
//
// One scrollable flat list of every super-draft and active entry,
// state-styled rather than grouped. Filter chips are AND-combined per
// §7.1; search is fuzzy over title + slug + id. The "Pending ideas"
// disclosure per §7.3 lives at the bottom, above the "+ Propose New RFC"
// button.

import { useEffect, useMemo, useState } from 'react'
import { useParams, Link } from 'react-router-dom'
import { listRFCs, listProposals, getCollection } from '../api'
import { entryPath, proposalPath, useProjectId, useCollectionId } from '../lib/entryPaths'
import JoinRequestModal from './JoinRequestModal.jsx'

const STATE_CHIPS = [
  { id: 'super-draft', label: 'Super-draft' },
  { id: 'active',      label: 'Active' },
]

const SORT_OPTIONS = [
  { id: 'recent',  label: 'Recently active' },
  { id: 'title',   label: 'Title' },
  { id: 'id',      label: 'ID' },
  { id: 'state',   label: 'State' },
]

export default function Catalog({ viewer, onProposeRFC, version }) {
  const [rfcs, setRfcs] = useState([])
  const [proposals, setProposals] = useState([])
  // §22 S4: the viewer's contribute capability in this collection, driving the
  // propose-first empty state (C3.5) and the propose control. `null` until the
  // collection's caps load; we fall back to "any authenticated viewer" so the
  // common case doesn't flicker, then refine.
  const [canContribute, setCanContribute] = useState(null)
  // §22.8: when the viewer can't contribute here but holds no role reaching the
  // collection, offer "Request to join" in the footer.
  const [canRequestJoin, setCanRequestJoin] = useState(false)
  const [joinOpen, setJoinOpen] = useState(false)
  // §22.4a: the type-driven entry noun ("RFC" | "Spec" | "Feature") for this
  // collection, read from the API. Defaults to the generic "RFC" until loaded.
  const [entryNoun, setEntryNoun] = useState('RFC')
  const [search, setSearch] = useState('')
  const [sort, setSort] = useState('recent')
  const [activeChips, setActiveChips] = useState(new Set())
  const [pendingOpen, setPendingOpen] = useState(true)
  const { slug, prNumber } = useParams()
  const pid = useProjectId()
  // §22 S2: the catalog is scoped to the active collection (the `/c/:cid/`
  // route segment, else the project's default collection).
  const cid = useCollectionId()

  useEffect(() => {
    listRFCs(pid, cid).then(d => setRfcs(d.items)).catch(() => setRfcs([]))
    listProposals(pid).then(d => setProposals(d.items)).catch(() => setProposals([]))
    setCanContribute(null)
    setCanRequestJoin(false)
    getCollection(pid, cid)
      .then(c => {
        setCanContribute(!!c?.viewer?.can_contribute)
        setCanRequestJoin(!!c?.viewer?.can_request_join)
        setEntryNoun(c?.entry_noun || 'RFC')
      })
      .catch(() => setCanContribute(false))
  }, [version, pid, cid])

  // While caps load, fall back to "any authenticated viewer" so the propose
  // affordance on the common (default-collection) case doesn't flash off.
  const mayPropose = canContribute === null ? !!viewer : canContribute

  const filtered = useMemo(() => {
    const needle = search.trim().toLowerCase()
    let items = rfcs.filter(r => {
      if (activeChips.size > 0 && !activeChips.has(r.state)) return false
      if (!needle) return true
      const hay = [r.title, r.slug, r.id || ''].join(' ').toLowerCase()
      return hay.includes(needle)
    })
    items = [...items].sort((a, b) => {
      // Starred items pin to the top of the current sort per §7.2.
      if (a.starred_by_me !== b.starred_by_me) return a.starred_by_me ? -1 : 1
      if (sort === 'title') return a.title.localeCompare(b.title)
      if (sort === 'id') return (a.id || 'zzz').localeCompare(b.id || 'zzz')
      if (sort === 'state') return a.state.localeCompare(b.state)
      // recent
      return (b.last_active_at || '').localeCompare(a.last_active_at || '')
    })
    return items
  }, [rfcs, search, sort, activeChips])

  function toggleChip(id) {
    const next = new Set(activeChips)
    next.has(id) ? next.delete(id) : next.add(id)
    setActiveChips(next)
  }

  return (
    <aside className="catalog">
      <div className="catalog-search">
        <input
          placeholder="Search title, slug, ID…"
          value={search}
          onChange={e => setSearch(e.target.value)}
        />
      </div>
      <div className="catalog-controls">
        <label htmlFor="catalog-sort">Sort:</label>
        <select id="catalog-sort" value={sort} onChange={e => setSort(e.target.value)}>
          {SORT_OPTIONS.map(o => <option key={o.id} value={o.id}>{o.label}</option>)}
        </select>
      </div>
      <div className="catalog-chips">
        {STATE_CHIPS.map(chip => (
          <button
            key={chip.id}
            className={`chip ${activeChips.has(chip.id) ? 'active' : ''}`}
            onClick={() => toggleChip(chip.id)}
          >
            {chip.label}
          </button>
        ))}
      </div>

      <div className="catalog-list">
        {filtered.length === 0 ? (
          <div style={{ padding: '24px 14px', color: '#999', fontSize: 13 }}>
            {rfcs.length === 0
              // C3.5: a contributor sees a propose-first call to action; a
              // granted viewer without contribute rights sees a bare empty
              // state; an anonymous reader sees the read-only note (the footer
              // carries the sign-in prompt).
              ? (mayPropose
                  ? 'No entries yet. Propose the first entry below.'
                  : 'No entries in the catalog yet.')
              : 'No matches.'}
          </div>
        ) : (
          filtered.map(r => {
            const isActive = slug === r.slug
            const isSuper = r.state === 'super-draft'
            return (
              <Link
                key={r.slug}
                to={entryPath(pid, r.slug, cid)}
                className={`catalog-row ${isActive ? 'active' : ''} ${isSuper ? 'is-super' : ''}`}
              >
                <div className="row-top">
                  <span className={`row-id ${isSuper ? 'super' : ''}`}>
                    {isSuper ? 'super-draft' : (r.id || '—')}
                  </span>
                </div>
                <span className="row-title">{r.title}</span>
                {r.tags.length > 0 && (
                  <span className="row-tags">{r.tags.join(' · ')}</span>
                )}
              </Link>
            )
          })
        )}
      </div>

      <div className="catalog-pending">
        <button className="pending-header" onClick={() => setPendingOpen(o => !o)}>
          <span>{pendingOpen ? '▾' : '▸'} Pending ideas</span>
          <span className="pending-count">{proposals.length}</span>
        </button>
        {pendingOpen && proposals.length > 0 && (
          <div className="pending-list">
            {proposals.map(p => (
              <Link
                key={p.pr_number}
                to={proposalPath(pid, p.pr_number, cid)}
                className={`pending-row ${String(prNumber) === String(p.pr_number) ? 'active' : ''}`}
              >
                <div>{p.title.replace(/^Propose:\s*/, '')}</div>
                <div className="pending-by">by @{p.opened_by || '—'} · PR #{p.pr_number}</div>
              </Link>
            ))}
          </div>
        )}
        {pendingOpen && proposals.length === 0 && (
          <div style={{ padding: '0 14px 12px', color: '#aaa', fontSize: 12 }}>
            No pending proposals.
          </div>
        )}
      </div>

      <div className="catalog-footer">
        {viewer ? (
          // §22 S4: the propose control is offered only when the viewer may
          // contribute to *this* collection (the scope-role gate); a granted
          // viewer with no contribute right in this collection sees nothing.
          mayPropose ? (
            <button className="btn-propose" onClick={onProposeRFC}>+ Propose New {entryNoun}</button>
          ) : (
            // §22.8: signed in but no contribute right here — offer to join.
            canRequestJoin && (
              <button className="btn-propose" onClick={() => setJoinOpen(true)}>Request to join</button>
            )
          )
        ) : (
          <a className="btn-propose" href="/auth/login" title="Private beta — only invited emails can propose">
            Sign in to propose <span className="beta-chip">Beta</span>
          </a>
        )}
      </div>
      {joinOpen && (
        <JoinRequestModal
          scopeType="collection"
          scopeId={cid}
          onClose={() => setJoinOpen(false)}
        />
      )}
    </aside>
  )
}
