// §22.10 (M3) — the per-project layout mounted at /p/:projectId/*.
//
// Responsibilities:
//   - fetch GET /api/projects/:id (404 → "not found / no access")
//   - apply the project's theme as :root CSS custom-property overrides,
//     reset on unmount/switch so one project's accent never bleeds into
//     deployment chrome or another project (§22.9)
//   - set the tab title to the project name
//   - provide ProjectContext { project, projectId, served, noun }
//   - the §4 guard: render the served corpus (children) only for the
//     corpus-served (default) project; any other id gets a "content not yet
//     served" placeholder (the per-project RFC serving is Plan B, a later
//     slice). This decouples M3-frontend from Plan B without ever showing
//     default-project content under the wrong project's chrome.
import { createContext, useContext, useEffect, useState } from 'react'
import { useParams } from 'react-router-dom'
import { getProject } from '../api'
import { brandTitle } from '../lib/brand'

const ProjectContext = createContext(null)

export function useProject() {
  return useContext(ProjectContext)
}

// §22.4a — the entry noun the chrome shows around a slug, driven by project
// type. The route segment stays the generic /e/; only the label varies.
export function entryNoun(type) {
  if (type === 'specification') return 'Spec'
  if (type === 'bdd') return 'Feature'
  return 'RFC'
}

// Which theme keys map onto which tokens.css custom properties. Kept small and
// explicit (§22.9 ships `accent`); grows as the per-project theme surface does.
const THEME_TOKENS = {
  accent: '--c-accent',
  accentStrong: '--c-accent-strong',
}

export default function ProjectLayout({ children }) {
  const { projectId } = useParams()
  const [project, setProject] = useState(null)
  const [status, setStatus] = useState('loading') // loading | ready | notfound

  useEffect(() => {
    let cancelled = false
    setStatus('loading')
    getProject(projectId)
      .then(p => { if (!cancelled) { setProject(p); setStatus('ready') } })
      .catch(() => { if (!cancelled) { setProject(null); setStatus('notfound') } })
    return () => { cancelled = true }
  }, [projectId])

  // Theme overlay — apply then reset (cleanup runs on project change/unmount).
  useEffect(() => {
    const theme = project?.theme
    if (!theme) return undefined
    const root = document.documentElement
    const applied = []
    for (const [key, value] of Object.entries(theme)) {
      const prop = THEME_TOKENS[key]
      if (prop && value) {
        root.style.setProperty(prop, value)
        applied.push(prop)
      }
    }
    return () => { applied.forEach(prop => root.style.removeProperty(prop)) }
  }, [project])

  // Tab title — project name (deployment chrome owns the deployment name).
  useEffect(() => {
    if (project?.name) document.title = brandTitle(project.name)
  }, [project])

  if (status === 'loading') {
    return <main className="chrome-pane"><div className="boot">Loading…</div></main>
  }
  if (status === 'notfound') {
    return (
      <main className="chrome-pane">
        <div className="welcome">
          <h1>Not found.</h1>
          <p>There is no such project here, or you don&apos;t have access to it.</p>
        </div>
      </main>
    )
  }

  // §22.4 (Plan B): every registry project now serves its own corpus, so
  // there is no "not served" guard — a readable project renders its children.
  return (
    <ProjectContext.Provider value={{ project, projectId, noun: entryNoun(project?.type) }}>
      {children}
    </ProjectContext.Provider>
  )
}
