// DocsLayout.jsx — v0.20.0 (was v0.19.0 / roadmap item #30).
//
// Left-side flyout nav + content area for the `/docs/*` route tree:
//
//   /docs                       → redirect to /docs/user-guide
//   /docs/user-guide            → DOCS.md (existing v0.14.0 content)
//   /docs/specs                 → client-side redirect to first configured spec
//   /docs/specs/:name           → a single framework spec (v0.20.0)
//   /docs/sessions              → redirect to /docs/sessions/about
//   /docs/sessions/about        → README.md from the sessions repo
//   /docs/sessions/:nnnn        → per-session overview (nav-only navigation)
//   /docs/sessions/:nnnn/:file  → per-transcript view
//
// v0.20.0 changes (Session 0018.0):
//   - Adds a "Specs" section between User Guide and Sessions, driven
//     by `/api/docs/specs/manifest`.
//   - Sessions render a nested tree: each session row has the
//     session's transcripts nested under it as their own nav rows
//     (labeled by `.N` ordinal). The transcript list is fetched per
//     session via `/api/docs/sessions/:nnnn/index` (cached server-
//     side, so the manifest+index fan-out is cheap on subsequent
//     loads). Always-expanded; no collapse toggle (current scale is
//     under twenty sessions — well under the threshold where lazy
//     expansion would pay).
//
// The flyout is a persistent left sidebar on desktop and a slide-out
// drawer on mobile (toggled by the icon button in the docs header).
//
// Amplitude analytics (per SPEC §21):
//   - track('Doc Viewed', { section: '...' }) on each sub-route mount;
//     the sub-route component owns the fire.
//   - flyout buttons + links carry `aria-label` + `data-amp-track-name`
//     so autocapture rows are readable rather than ":nth-child(7)".

import { useEffect, useState, useCallback } from 'react'
import { Link, useNavigate, useLocation, Outlet } from 'react-router-dom'
import { getSessionsManifest, getSessionIndex, getSpecsManifest } from '../api.js'

// Extract the `.N` ordinal from a transcript filename:
//   "SESSION-0014.1-TRANSCRIPT-...md" → "0014.1"
//   "SESSION-0013.1.1-TRANSCRIPT-...md" → "0013.1.1" (nested subagent)
// Returns the bare filename as fallback if the expected shape isn't
// matched (which shouldn't happen — the backend index endpoint
// filters by the same regex).
function transcriptOrdinal(filename) {
  const m = /^SESSION-(\d{4}\.\d+(?:\.\d+)*)-TRANSCRIPT/.exec(filename)
  return m ? m[1] : filename
}

export default function DocsLayout({ authenticated }) {
  const [manifest, setManifest] = useState(null)
  const [manifestState, setManifestState] = useState('loading') // loading | ok | error
  const [sessionFiles, setSessionFiles] = useState({})          // { nnnn: [filename, ...] }
  const [specs, setSpecs] = useState([])
  const [specsState, setSpecsState] = useState('loading')       // loading | ok | error
  const [drawerOpen, setDrawerOpen] = useState(false)
  const [reloadTick, setReloadTick] = useState(0)
  const navigate = useNavigate()
  const location = useLocation()

  // Manifest fetch — drives the Sessions section.
  useEffect(() => {
    let active = true
    setManifestState('loading')
    getSessionsManifest()
      .then(payload => {
        if (!active) return
        setManifest(payload || {})
        setManifestState('ok')
      })
      .catch(() => {
        if (!active) return
        setManifest({})
        setManifestState('error')
      })
    return () => { active = false }
  }, [reloadTick])

  // Per-session transcript lists — fan out from the manifest. Always-
  // expanded means we pre-fetch every session's index alongside the
  // manifest, gated on the manifest having loaded successfully. The
  // backend's 5-minute content TTL makes the repeat cost negligible.
  useEffect(() => {
    if (manifestState !== 'ok' || !manifest) return
    let active = true
    const nnnnList = Object.keys(manifest).sort()
    Promise.all(
      nnnnList.map(nnnn =>
        getSessionIndex(nnnn)
          .then(payload => [nnnn, (payload && payload.files) || []])
          .catch(() => [nnnn, []])
      )
    ).then(pairs => {
      if (!active) return
      setSessionFiles(Object.fromEntries(pairs))
    })
    return () => { active = false }
  }, [manifest, manifestState])

  // Specs fetch — drives the Specs section. Independent of sessions.
  useEffect(() => {
    let active = true
    setSpecsState('loading')
    getSpecsManifest()
      .then(payload => {
        if (!active) return
        setSpecs((payload && payload.specs) || [])
        setSpecsState('ok')
      })
      .catch(() => {
        if (!active) return
        setSpecs([])
        setSpecsState('error')
      })
    return () => { active = false }
  }, [reloadTick])

  // Close the mobile drawer on every navigation so a click in the nav
  // doesn't strand the user on a drawer-open view.
  useEffect(() => {
    setDrawerOpen(false)
  }, [location.pathname])

  const retryManifest = useCallback(() => {
    setReloadTick(t => t + 1)
  }, [])

  return (
    <div className="docs-layout">
      <header className="docs-header">
        <button
          className="docs-back"
          onClick={() => (history.length > 1 ? navigate(-1) : navigate('/'))}
          aria-label="Back to previous page"
          data-amp-track-name="Docs Back"
        >
          ← Back
        </button>
        <button
          className="docs-drawer-toggle"
          onClick={() => setDrawerOpen(o => !o)}
          aria-label="Toggle docs navigation"
          aria-expanded={drawerOpen}
          data-amp-track-name="Docs Drawer Toggle"
        >
          <span aria-hidden>☰</span>
        </button>
        <span className="docs-title">Docs</span>
        {!authenticated && (
          <Link
            className="docs-signin"
            to="/"
            aria-label="Home"
            data-amp-track-name="Docs Home"
          >
            Home
          </Link>
        )}
      </header>
      <div className={'docs-body' + (drawerOpen ? ' docs-body--drawer-open' : '')}>
        <aside className="docs-nav" aria-label="Docs navigation">
          <DocsNav
            manifest={manifest}
            manifestState={manifestState}
            sessionFiles={sessionFiles}
            specs={specs}
            specsState={specsState}
            onRetry={retryManifest}
            currentPath={location.pathname}
          />
        </aside>
        <main className="docs-content">
          <Outlet />
        </main>
      </div>
      {drawerOpen && (
        <button
          className="docs-drawer-scrim"
          aria-label="Close drawer"
          onClick={() => setDrawerOpen(false)}
          data-amp-track-name="Docs Drawer Close"
        />
      )}
    </div>
  )
}

function DocsNav({
  manifest,
  manifestState,
  sessionFiles,
  specs,
  specsState,
  onRetry,
  currentPath,
}) {
  const isActive = (path) => currentPath === path || currentPath.startsWith(path + '/')
  const isExactly = (path) => currentPath === path

  const sessionKeys = Object.keys(manifest || {}).sort()

  return (
    <nav className="docs-nav-inner">
      <div className="docs-nav-section">
        <div className="docs-nav-section-label">Docs</div>
        <ul className="docs-nav-list">
          <li>
            <Link
              to="/docs/user-guide"
              className={isActive('/docs/user-guide') ? 'active' : ''}
              aria-label="User Guide"
              data-amp-track-name="Docs Nav User Guide"
            >
              User Guide
            </Link>
          </li>
        </ul>
      </div>

      <div className="docs-nav-section">
        <div className="docs-nav-section-label">Specs</div>
        {specsState === 'loading' && (
          <ul className="docs-nav-list docs-nav-skeleton" aria-hidden>
            <li><span className="skeleton-row" /></li>
            <li><span className="skeleton-row" /></li>
          </ul>
        )}
        {specsState === 'error' && (
          <div className="docs-nav-error" role="alert">
            <span>Couldn't load specs.</span>
          </div>
        )}
        {specsState === 'ok' && specs.length > 0 && (
          <ul className="docs-nav-list">
            {specs.map(spec => {
              const to = `/docs/specs/${spec.name}`
              return (
                <li key={spec.name}>
                  <Link
                    to={to}
                    className={isExactly(to) ? 'active' : ''}
                    aria-label={`Spec: ${spec.title}`}
                    data-amp-track-name="Docs Nav Spec"
                    data-amp-track-spec={spec.name}
                  >
                    {spec.title}
                  </Link>
                </li>
              )
            })}
          </ul>
        )}
      </div>

      <div className="docs-nav-section">
        <div className="docs-nav-section-label">Sessions</div>
        <ul className="docs-nav-list">
          <li>
            <Link
              to="/docs/sessions/about"
              className={isExactly('/docs/sessions/about') ? 'active' : ''}
              aria-label="About sessions"
              data-amp-track-name="Docs Nav Sessions About"
            >
              About
            </Link>
          </li>
        </ul>

        {manifestState === 'loading' && (
          <ul className="docs-nav-list docs-nav-skeleton" aria-hidden>
            <li><span className="skeleton-row" /></li>
            <li><span className="skeleton-row" /></li>
            <li><span className="skeleton-row" /></li>
          </ul>
        )}

        {manifestState === 'error' && (
          <div className="docs-nav-error" role="alert">
            <span>Couldn't load session list.</span>
            <button
              type="button"
              onClick={onRetry}
              aria-label="Retry session list"
              data-amp-track-name="Docs Nav Sessions Retry"
            >
              Try again
            </button>
          </div>
        )}

        {manifestState === 'ok' && sessionKeys.length > 0 && (
          <ul className="docs-nav-list docs-nav-list--tree">
            {sessionKeys.map(nnnn => {
              const entry = manifest[nnnn] || {}
              const title = entry.title || ''
              const label = title ? `${nnnn} — ${title}` : nnnn
              const to = `/docs/sessions/${nnnn}`
              const files = sessionFiles[nnnn] || []
              return (
                <li key={nnnn}>
                  <Link
                    to={to}
                    className={isExactly(to) ? 'active' : ''}
                    aria-label={`Session ${nnnn}${title ? ': ' + title : ''}`}
                    data-amp-track-name="Docs Nav Session"
                    data-amp-track-session={nnnn}
                  >
                    {label}
                  </Link>
                  {files.length > 0 && (
                    <ul className="docs-nav-list docs-nav-list--children">
                      {files.map(f => {
                        const tTo = `/docs/sessions/${nnnn}/${f}`
                        return (
                          <li key={f}>
                            <Link
                              to={tTo}
                              className={isExactly(tTo) ? 'active' : ''}
                              aria-label={`Transcript ${transcriptOrdinal(f)}`}
                              data-amp-track-name="Docs Nav Transcript"
                              data-amp-track-session={nnnn}
                              data-amp-track-filename={f}
                            >
                              {transcriptOrdinal(f)}
                            </Link>
                          </li>
                        )
                      })}
                    </ul>
                  )}
                </li>
              )
            })}
          </ul>
        )}
      </div>
    </nav>
  )
}
