---
/**
 * /convergence — dashboard of agentic-coding patterns across tools.
 *
 * For each category, renders every pattern in that category as a
 * PatternTimeline card. Categories with no patterns render an honest
 * placeholder so readers see the coverage gaps rather than inferring
 * the registry is exhaustive.
 *
 * Driven from changelog/patterns.yaml + changelog/tools/*.yaml in a
 * single-book app, or changelog/<book>/ in corpus mode. No code change is
 * needed to grow the dashboard — add manifest rows.
 */
import Base from '../layouts/Base.astro';
import PatternTimeline from '../components/PatternTimeline.astro';
import {
  getPatternsByCategory,
  emptyPatternsByCategory,
  CATEGORY_LABELS,
} from '../src/lib/patterns';
import { patternCategories } from '@brandon_m_behring/book-scaffold-astro';

interface Props { bookId?: string; }
const { bookId } = Astro.props;

// A tools-profile book may use the inline <Convergence> component yet define
// no `changelog/patterns.yaml` — then the `patterns` collection is never
// registered and getCollection('patterns') errors with "The collection
// 'patterns' does not exist or is empty" (#86). Gate on the manifest's
// presence and never touch the collection when it is absent — the same
// presence-gate references.astro uses for the optional `sources` collection.
// When absent, render the dashboard's honest empty-state from a pure
// all-empty map (no content-collection access).
const rootPatternsManifest = import.meta.glob('/changelog/patterns.yaml', {
  query: '?raw',
  import: 'default',
  eager: true,
});
const corpusPatternManifests = import.meta.glob('/changelog/*/patterns.yaml', {
  query: '?raw',
  import: 'default',
  eager: true,
});
const rootToolFiles = import.meta.glob('/changelog/tools/*.yaml', {
  query: '?raw',
  import: 'default',
  eager: true,
});
const corpusToolFiles = import.meta.glob('/changelog/*/tools/*.yaml', {
  query: '?raw',
  import: 'default',
  eager: true,
});
const patternsPath = bookId
  ? `/changelog/${bookId}/patterns.yaml`
  : '/changelog/patterns.yaml';
const hasPatterns = bookId
  ? patternsPath in corpusPatternManifests
  : patternsPath in rootPatternsManifest;
const hasChangelog = bookId
  ? Object.keys(corpusToolFiles).some((path) => path.startsWith(`/changelog/${bookId}/tools/`))
  : Object.keys(rootToolFiles).length > 0;
const grouped = hasPatterns
  ? await getPatternsByCategory(bookId)
  : emptyPatternsByCategory();
const totalPatterns = Object.values(grouped).reduce(
  (n, arr) => n + arr.length,
  0,
);
// Distinguish a missing/misnamed manifest from a legitimately empty registry:
// a tools book that uses <Convergence> but never created changelog/patterns.yaml
// would otherwise see a blank dashboard indistinguishable from "no patterns yet".
// Surface an actionable hint instead — matching tips.astro / references.astro.
const noManifestHint = hasPatterns
  ? null
  : `No ${patternsPath.slice(1)} found — create it to populate this dashboard, or remove convergence from this book's apparatus.`;
---
<Base
  title="Convergence — Agentic Coding"
  description="Which agentic-coding patterns have converged across Claude Code, Gemini CLI, and Codex CLI — and when? A live timeline driven from the changelog manifest."
  bookId={bookId}
>
  <article class="prose convergence-dashboard">
    <header class="convergence-header">
      <h1>Convergence dashboard</h1>
      <p class="convergence-lede">
        Each card below tracks one agentic-coding pattern across the
        three primary tools. A pattern is <em>converged</em> when all
        three tools have shipped it. The timeline shows the sequence of
        adoptions — who first, who followed, who has not yet.
      </p>
      <p class="convergence-metric">
        <strong>{totalPatterns}</strong> pattern{totalPatterns === 1 ? '' : 's'}
        {' '}currently tracked. The registry grows as new patterns
        converge or as historical patterns get backfilled; expect the
        count to drift upward, not the existing entries.
      </p>
      {noManifestHint && (
        <p class="convergence-no-manifest" role="note">{noManifestHint}</p>
      )}
    </header>

    {patternCategories.map((cat) => {
      const patterns = grouped[cat];
      return (
        <section class="convergence-category" data-category={cat} id={`category-${cat}`}>
          <h2 class="convergence-category-heading">
            <span class="convergence-category-label">{CATEGORY_LABELS[cat]}</span>
            <span class="convergence-category-count">
              {patterns.length === 0
                ? 'no patterns'
                : `${patterns.length} pattern${patterns.length === 1 ? '' : 's'}`}
            </span>
          </h2>
          {patterns.length === 0 ? (
            <p class="convergence-category-empty">
              No tracked patterns in this category yet. This is a
              coverage gap, not an assertion that nothing belongs here.
            </p>
          ) : (
            <div class="convergence-card-list">
              {patterns.map((p) => (
                <PatternTimeline pattern={p} bookId={bookId} hasChangelog={hasChangelog} />
              ))}
            </div>
          )}
        </section>
      );
    })}

    <section class="convergence-method">
      <h2>How to read this</h2>
      <p>
        The registry lives at <code>{patternsPath.slice(1)}</code>.
        Each tool's adoption timeline lives beside it under
        <code>{bookId ? `changelog/${bookId}/tools/` : 'changelog/tools/'}</code>. The dashboard
        joins them at build time — no code edits needed to add new
        patterns or new adoption events.
      </p>
      <p>
        A pattern with <em>convergence_date: null</em> is either a
        partial convergence (1 or 2 of 3 tools) or an open design
        space where the right shape is still being argued. Either
        way, it is information worth surfacing, not hiding.
      </p>
      <p>
        Coverage is intentionally sparse in the early book — the
        registry grows organically through quarterly audit cycles.
        Pattern nominations are welcome via Issues.
      </p>
    </section>
  </article>
</Base>
