/**
 * Onboarding canvas — the empty-workspace actions (spec §02).
 *
 * The live chart rendering is integration (vega-embed) and is mocked away here;
 * what these tests own is the wiring: each affordance creates the right snippets
 * in the store. Card previews are exercised only insofar as they don't crash.
 */

import { afterEach, beforeEach, describe, expect, test, vi } from 'vitest';
import { act } from 'react';
import { createRoot, type Root } from 'react-dom/client';
import { CHART_EXAMPLES, exampleSpecText } from '@core/examples';
import { sampleSpecText } from '@core/snippet';
import { useSnippetStore } from '../stores/SnippetStore';
import { Onboarding } from './Onboarding';

// The gallery renders one chart per card; stub the shared renderer so the test
// never touches vega-embed. A resolved no-op handle is enough — Onboarding only
// finalizes it on unmount.
vi.mock('../services/chart-renderer', () => ({
  renderSpec: () =>
    Promise.resolve({
      destroy() {},
      resize() {},
      inspectData: () => null,
      onDataChange: () => () => {},
    }),
}));

(globalThis as { IS_REACT_ACT_ENVIRONMENT?: boolean }).IS_REACT_ACT_ENVIRONMENT = true;

let container: HTMLDivElement;
let root: Root;

beforeEach(() => {
  useSnippetStore.getState().reset();
  container = document.createElement('div');
  document.body.appendChild(container);
  root = createRoot(container);
  act(() => root.render(<Onboarding />));
});

afterEach(() => {
  act(() => root.unmount());
  container.remove();
  useSnippetStore.getState().reset();
  vi.clearAllMocks();
});

/** Click the button whose accessible name (aria-label, else text) matches. */
function click(name: string) {
  const button = Array.from(container.querySelectorAll('button')).find((b) =>
    (b.getAttribute('aria-label') ?? b.textContent ?? '').includes(name),
  );
  if (!button) throw new Error(`button not found: ${name}`);
  act(() => button.click());
}

describe('Onboarding', () => {
  test('"Create your first snippet" creates one snippet from the sample template', () => {
    click('Create your first snippet');
    const { snippets, activeSnippetId } = useSnippetStore.getState();
    expect(snippets).toHaveLength(1);
    expect(snippets[0].spec).toBe(sampleSpecText());
    expect(activeSnippetId).toBe(snippets[0].id);
  });

  test('an example’s Add creates that snippet, named and active', () => {
    const scatter = CHART_EXAMPLES.find((e) => e.id === 'scatter')!;
    click(`Add ${scatter.name}`);
    const { snippets, activeSnippetId } = useSnippetStore.getState();
    expect(snippets).toHaveLength(1);
    expect(snippets[0].name).toBe(scatter.name);
    expect(snippets[0].spec).toBe(exampleSpecText(scatter));
    expect(activeSnippetId).toBe(snippets[0].id);
  });

  test('"Add all" adds every example and makes the bar chart active', () => {
    click('Add all');
    const { snippets, activeSnippetId } = useSnippetStore.getState();
    expect(snippets).toHaveLength(CHART_EXAMPLES.length);
    // Every example name is present.
    const names = new Set(snippets.map((s) => s.name));
    for (const example of CHART_EXAMPLES) expect(names.has(example.name)).toBe(true);
    // The first example (bar) is the active one (staggered newest).
    const active = snippets.find((s) => s.id === activeSnippetId);
    expect(active?.name).toBe(CHART_EXAMPLES[0].name);
  });
});
