// DocsSessionTranscript.jsx — v0.21.0 (was v0.19.0 / roadmap item #30).
//
// Per-transcript view at `/docs/sessions/:nnnn/:filename`. Fetches the
// transcript body via the backend mediator and renders it through the
// shared MarkdownPreview.
//
// v0.21.0 / roadmap item #32:
//   - A compact metadata header now sits above the rendered body
//     (title, started/ended, duration, optional TL;DR, and a
//     "View source on git.wiggleverse.org" external link). The parse
//     + render helpers (`parseTranscriptMeta`, `TranscriptMetaHeader`,
//     `gitSourceUrl`) are exported here so the session-root inline-
//     collapse view (DocsSessionIndex.jsx) reuses the exact same
//     rendering for the transcript(s) it inlines.
//
// Empty-state contract:
//   404 → "This transcript isn't published yet" with a link back to
//         the parent session index
//   502 → "Couldn't reach the session-history repo" + retry button

import { useEffect, useState, useCallback } from 'react'
import { Link, useParams } from 'react-router-dom'
import MarkdownPreview from './MarkdownPreview.jsx'
import { getSessionTranscript, getSessionsManifest } from '../api.js'
import { EVENTS, track } from '../lib/analytics'
import './Docs.css'

// The canonical published-repo source URL for a transcript file, per
// SESSION-PROTOCOL.md §1's folder layout (one folder per session).
export function gitSourceUrl(nnnn, filename) {
  return (
    'https://git.wiggleverse.org/wiggleverse/ohm-session-history/src/branch/main/' +
    `${encodeURIComponent(nnnn)}/${encodeURIComponent(filename)}`
  )
}

// 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)
export function transcriptOrdinal(filename) {
  const m = /^SESSION-(\d{4}\.\d+(?:\.\d+)*)-TRANSCRIPT/.exec(filename || '')
  return m ? m[1] : filename || ''
}

// Parse the `<start>--<end>` ISO segment out of a transcript filename.
// Per the protocol the segment is `YYYY-MM-DDTHH-MM--YYYY-MM-DDTHH-MM`
// (colons replaced by dashes for filesystem portability, minute
// precision, PST implied). Legacy renamed-letter transcripts omit the
// segment entirely; in that case every derived field comes back null
// and the header degrades gracefully.
//
// Returns { ordinal, start: Date|null, end: Date|null, durationMs: number|null }.
export function parseTranscriptMeta(filename) {
  const ordinal = transcriptOrdinal(filename)
  const m = /-TRANSCRIPT-(\d{4}-\d{2}-\d{2})T(\d{2})-(\d{2})--(\d{4}-\d{2}-\d{2})T(\d{2})-(\d{2})\.md$/.exec(
    filename || ''
  )
  if (!m) {
    return { ordinal, start: null, end: null, durationMs: null }
  }
  const [, sDate, sH, sM, eDate, eH, eM] = m
  // Parse as local wall-clock time. The filename carries no timezone
  // (PST is implied per the protocol); we render the wall-clock value
  // verbatim rather than shifting it, so we build a local Date and read
  // it back with the same calendar fields. Duration is a difference of
  // two local Dates, so the implied-timezone ambiguity cancels out.
  const start = new Date(`${sDate}T${sH}:${sM}:00`)
  const end = new Date(`${eDate}T${eH}:${eM}:00`)
  const startOk = !Number.isNaN(start.getTime())
  const endOk = !Number.isNaN(end.getTime())
  const durationMs =
    startOk && endOk && end.getTime() >= start.getTime()
      ? end.getTime() - start.getTime()
      : null
  return {
    ordinal,
    start: startOk ? start : null,
    end: endOk ? end : null,
    durationMs,
  }
}

function fmtDateTime(d) {
  if (!d) return null
  // e.g. "May 28, 2026, 11:11 AM" — human-readable, wall-clock.
  try {
    return d.toLocaleString(undefined, {
      year: 'numeric',
      month: 'short',
      day: 'numeric',
      hour: 'numeric',
      minute: '2-digit',
    })
  } catch {
    return d.toISOString()
  }
}

function fmtDuration(ms) {
  if (ms == null || ms <= 0) return null
  const totalMin = Math.round(ms / 60000)
  const h = Math.floor(totalMin / 60)
  const m = totalMin % 60
  if (h > 0 && m > 0) return `${h}h ${m}m`
  if (h > 0) return `${h}h`
  return `${m}m`
}

// The compact metadata block rendered above every transcript body.
// Shared between the standalone transcript route and the session-root
// inline-collapse view. `tldr` is optional — absent ⇒ rendered nothing
// (graceful degrade, per the manifest schema where `tldr` may be unset).
export function TranscriptMetaHeader({ nnnn, filename, title, tldr }) {
  const { ordinal, start, end, durationMs } = parseTranscriptMeta(filename)
  const started = fmtDateTime(start)
  const ended = fmtDateTime(end)
  const duration = fmtDuration(durationMs)
  const heading = title ? `${ordinal} — ${title}` : `Session ${ordinal}`

  return (
    <header className="docs-transcript-meta">
      <h2 className="docs-transcript-meta-title">{heading}</h2>
      {(started || ended || duration) && (
        <dl className="docs-transcript-meta-grid">
          {started && (
            <div className="docs-transcript-meta-row">
              <dt>Started</dt>
              <dd>{started}</dd>
            </div>
          )}
          {ended && (
            <div className="docs-transcript-meta-row">
              <dt>Ended</dt>
              <dd>{ended}</dd>
            </div>
          )}
          {duration && (
            <div className="docs-transcript-meta-row">
              <dt>Duration</dt>
              <dd>{duration}</dd>
            </div>
          )}
        </dl>
      )}
      {tldr && <p className="docs-transcript-meta-tldr">{tldr}</p>}
      <a
        className="docs-source-link docs-transcript-meta-source"
        href={gitSourceUrl(nnnn, filename)}
        target="_blank"
        rel="noopener noreferrer"
        aria-label={`View transcript ${ordinal} source on git.wiggleverse.org`}
        data-amp-track-name="Docs Transcript Source Link"
        data-amp-track-session={nnnn}
        data-amp-track-filename={filename}
      >
        View source on git.wiggleverse.org ↗
      </a>
    </header>
  )
}

export default function DocsSessionTranscript() {
  const { nnnn, filename } = useParams()
  const [body, setBody] = useState('')
  const [title, setTitle] = useState('')
  const [tldr, setTldr] = useState('')
  const [status, setStatus] = useState('loading') // loading | ok | notfound | error
  const [reloadTick, setReloadTick] = useState(0)

  useEffect(() => {
    track(EVENTS.DOC_VIEWED, { section: `sessions/${nnnn}/${filename}` })
  }, [nnnn, filename])

  // Title + optional tl;dr from the manifest — decorative metadata that
  // feeds the header. Failure leaves the header showing the bare NNNN
  // and no TL;DR; the body fetch below is the load-bearing one.
  useEffect(() => {
    let active = true
    getSessionsManifest()
      .then(payload => {
        if (!active) return
        const entry = (payload && payload[nnnn]) || {}
        setTitle(entry.title || '')
        setTldr(typeof entry.tldr === 'string' ? entry.tldr : '')
      })
      .catch(() => {})
    return () => { active = false }
  }, [nnnn])

  useEffect(() => {
    let active = true
    setStatus('loading')
    getSessionTranscript(nnnn, filename)
      .then(text => {
        if (!active) return
        setBody(text || '')
        setStatus('ok')
      })
      .catch(e => {
        if (!active) return
        if (e.status === 404) {
          setStatus('notfound')
        } else {
          setStatus('error')
        }
      })
    return () => { active = false }
  }, [nnnn, filename, reloadTick])

  const retry = useCallback(() => setReloadTick(t => t + 1), [])

  return (
    <article className="docs-article">
      <div className="docs-breadcrumbs">
        <Link
          to={`/docs/sessions/${nnnn}`}
          aria-label={`Back to session ${nnnn} index`}
          data-amp-track-name="Docs Transcript Back To Index"
        >
          ← Session {nnnn}
        </Link>
      </div>
      {status === 'loading' && <p className="muted">Loading…</p>}
      {status === 'notfound' && (
        <div className="docs-empty">
          <p>This transcript isn't published yet.</p>
          <p>
            <Link
              to={`/docs/sessions/${nnnn}`}
              aria-label={`Back to session ${nnnn}`}
              data-amp-track-name="Docs Transcript Back To Session"
            >
              ← Back to session {nnnn}
            </Link>
          </p>
        </div>
      )}
      {status === 'error' && (
        <div className="docs-error" role="alert">
          <p>Couldn't reach the session-history repo.</p>
          <button
            type="button"
            onClick={retry}
            aria-label="Retry"
            data-amp-track-name="Docs Transcript Retry"
          >
            Try again
          </button>
        </div>
      )}
      {status === 'ok' && (
        <>
          <TranscriptMetaHeader
            nnnn={nnnn}
            filename={filename}
            title={title}
            tldr={tldr}
          />
          <div className="philosophy-body">
            <MarkdownPreview content={body} />
          </div>
        </>
      )}
    </article>
  )
}
