/**
 * Accessibility plumbing shared by every chart component per
 * ADR-0023's six commitments. Single source of truth so a sloppy
 * contributor can't drop one silently.
 *
 * The helpers here either *return* DOM nodes (so the consumer can
 * attach them to a `<figure>` it controls) or *augment* an existing
 * SVG produced by Plot / D3 with the missing a11y bits (Plot does
 * not emit `<title>` or `<desc>` by default).
 */

let idCounter = 0;

/**
 * Stable per-chart id used to wire `aria-labelledby` /
 * `aria-describedby`. Generated client-side at component
 * construction; not stable across SSR (we don't SSR the SPAs per
 * ADR-0004).
 */
export function chartId(): string {
  idCounter += 1;
  return `chart-${idCounter}`;
}

/**
 * Find the first `<svg>` inside a node Plot returned. Plot returns
 * either the SVG itself (no legend) or an HTML `<figure>` wrapping
 * a legend `<div>` + the SVG when `color.legend: true`. Charts care
 * about the SVG specifically — that's where `<title>` + `<desc>`
 * live for assistive tech.
 */
export function findChartSvg(plotOutput: SVGSVGElement | HTMLElement): SVGSVGElement | null {
  if (plotOutput instanceof SVGSVGElement) {
    return plotOutput;
  }
  return plotOutput.querySelector('svg');
}

/**
 * Prepend `<title>` and `<desc>` elements to a Plot-generated SVG
 * so screen readers expose them as the chart's accessible name and
 * long description. SVG2 + ARIA require these elements to be the
 * first children of the `<svg>` for the AT to pick them up reliably.
 *
 * Idempotent: if a `<title>` is already present, no-op.
 */
export function injectSvgTitleDesc(svg: SVGSVGElement, caption: string, description: string): void {
  const doc = svg.ownerDocument;
  // Strip any pre-existing title/desc the Plot helper might have
  // injected so we don't accumulate on re-renders.
  Array.from(svg.children)
    .filter((c) => c.tagName === 'title' || c.tagName === 'desc')
    .forEach((c) => c.remove());

  const titleEl = doc.createElementNS('http://www.w3.org/2000/svg', 'title');
  titleEl.textContent = caption;

  const descEl = doc.createElementNS('http://www.w3.org/2000/svg', 'desc');
  descEl.textContent = description;

  // The first child is the title, second is the description.
  svg.insertBefore(descEl, svg.firstChild);
  svg.insertBefore(titleEl, svg.firstChild);
}

/**
 * Test whether the active user has requested reduced motion. Used
 * by the chart components to skip Plot's default transitions per
 * ADR-0016. The check is read once per render — Plot doesn't expose
 * an animation toggle, so this drives whether we apply the
 * `[no-transitions]` CSS reset shipped with the lib's stylesheet.
 */
export function prefersReducedMotion(): boolean {
  if (typeof window === 'undefined' || !window.matchMedia) {
    return false;
  }
  return window.matchMedia('(prefers-reduced-motion: reduce)').matches;
}

/**
 * Resolve the active theme from `<html class="dark">` (toggled by
 * `LayoutStateService.themeMode` upstream — see
 * `libs/shared/state/src/lib/layout-state.service.ts`). Charts swap
 * their colour ramp + axis text colour based on this.
 *
 * Safe on the server: returns `'light'` if `document` is absent
 * (irrelevant for v1 since both SPAs are CSR-only, but defensive).
 */
export function resolveTheme(): 'light' | 'dark' {
  if (typeof document === 'undefined') {
    return 'light';
  }
  return document.documentElement.classList.contains('dark') ? 'dark' : 'light';
}
