import { describe, expect, test } from 'vitest';
import { inspectViewLabel, inspectableViews } from './inspect-views';

// Fixtures mirror the shapes Vega-Lite 6 actually compiles to (verified by
// compiling each composition and dumping `vgSpec.data` + `vgSpec.marks`).

describe('inspectableViews', () => {
  test('a single unit: one drawn table, resolved + input ends of its pipeline', () => {
    const vg = {
      data: [
        { name: 'source_0', values: [] },
        { name: 'data_0', source: 'source_0' },
      ],
      marks: [{ type: 'rect', name: 'marks', from: { data: 'data_0' } }],
    };
    expect(inspectableViews(vg)).toEqual([{ resolved: 'data_0', input: 'source_0' }]);
  });

  test('vconcat sharing one source: two tables, same input, different resolved', () => {
    const vg = {
      data: [
        { name: 'source_0', values: [] },
        { name: 'data_1', source: 'source_0' },
        { name: 'data_2', source: 'source_0' },
      ],
      marks: [
        {
          type: 'group',
          name: 'concat_0_group',
          marks: [{ type: 'rect', name: 'concat_0_marks', from: { data: 'data_1' } }],
        },
        {
          type: 'group',
          name: 'concat_1_group',
          marks: [{ type: 'rect', name: 'concat_1_marks', from: { data: 'data_2' } }],
        },
      ],
    };
    expect(inspectableViews(vg)).toEqual([
      { resolved: 'data_1', input: 'source_0' },
      { resolved: 'data_2', input: 'source_0' },
    ]);
  });

  test('layers binding different data: each table traces to its own named source', () => {
    const vg = {
      data: [
        { name: 'a', values: [] },
        { name: 'b', values: [] },
        { name: 'data_0', source: 'a' },
        { name: 'data_1', source: 'b' },
      ],
      marks: [
        { type: 'line', name: 'layer_0_marks', from: { data: 'data_0' } },
        { type: 'symbol', name: 'layer_1_marks', from: { data: 'data_1' } },
      ],
    };
    expect(inspectableViews(vg)).toEqual([
      { resolved: 'data_0', input: 'a' },
      { resolved: 'data_1', input: 'b' },
    ]);
  });

  test('repeat: one drawn table per repeated child', () => {
    const vg = {
      data: [
        { name: 'source_0', values: [] },
        { name: 'data_1', source: 'source_0' },
        { name: 'data_2', source: 'source_0' },
      ],
      marks: [
        {
          type: 'group',
          name: 'child__a_group',
          marks: [{ type: 'rect', name: 'child__a_marks', from: { data: 'data_1' } }],
        },
        {
          type: 'group',
          name: 'child__b_group',
          marks: [{ type: 'rect', name: 'child__b_marks', from: { data: 'data_2' } }],
        },
      ],
    };
    expect(inspectableViews(vg).map((v) => v.resolved)).toEqual(['data_1', 'data_2']);
  });

  test('facet: the cell data via from.facet.data; layout-helper tables excluded', () => {
    const vg = {
      data: [
        { name: 'source_0', values: [] },
        { name: 'data_0', source: 'source_0' },
        { name: 'facet_domain', source: 'data_0' },
        { name: 'facet_domain_row' },
        { name: 'facet_domain_column' },
      ],
      marks: [
        { type: 'group', name: 'facet-title' },
        { type: 'group', name: 'row_header', from: { data: 'facet_domain_row' } },
        { type: 'group', name: 'column_footer', from: { data: 'facet_domain_column' } },
        {
          type: 'group',
          name: 'cell',
          from: { facet: { data: 'data_0' } },
          marks: [{ type: 'rect', name: 'child_marks', from: { data: 'facet' } }],
        },
      ],
    };
    // Only the faceted cell data is inspectable; facet_domain* are layout, and the
    // child's `from: { data: 'facet' }` names no real table.
    expect(inspectableViews(vg)).toEqual([{ resolved: 'data_0', input: 'source_0' }]);
  });

  test('input equals resolved when the drawn table is itself the source (no transforms)', () => {
    const vg = {
      data: [{ name: 'source_0', values: [] }],
      marks: [{ type: 'rect', name: 'marks', from: { data: 'source_0' } }],
    };
    expect(inspectableViews(vg)).toEqual([{ resolved: 'source_0', input: 'source_0' }]);
  });

  test('a line+point unit shows two tables (Vega-Lite desugars point into a layer)', () => {
    // Documented consequence of enumerating drawn tables: point overlay → two
    // near-identical tables (data_1 derives from data_0), both tracing to source_0.
    const vg = {
      data: [
        { name: 'source_0', values: [] },
        { name: 'data_0', source: 'source_0' },
        { name: 'data_1', source: 'data_0' },
      ],
      marks: [
        { type: 'line', name: 'layer_0_marks', from: { data: 'data_0' } },
        { type: 'symbol', name: 'layer_1_marks', from: { data: 'data_1' } },
      ],
    };
    expect(inspectableViews(vg)).toEqual([
      { resolved: 'data_0', input: 'source_0' },
      { resolved: 'data_1', input: 'source_0' },
    ]);
  });

  test('deduplicates a table drawn by more than one mark', () => {
    const vg = {
      data: [
        { name: 'source_0', values: [] },
        { name: 'data_0', source: 'source_0' },
      ],
      marks: [
        { type: 'rect', name: 'm1', from: { data: 'data_0' } },
        { type: 'text', name: 'm2', from: { data: 'data_0' } },
      ],
    };
    expect(inspectableViews(vg)).toEqual([{ resolved: 'data_0', input: 'source_0' }]);
  });

  test('returns [] for malformed or data-less specs', () => {
    expect(inspectableViews(null)).toEqual([]);
    expect(inspectableViews({})).toEqual([]);
    expect(inspectableViews({ data: [], marks: [] })).toEqual([]);
    expect(inspectableViews({ marks: [{ type: 'rect', from: { data: 'ghost' } }] })).toEqual([]);
  });
});

describe('inspectViewLabel', () => {
  test('compiler-generated names become an ordinal "View N"', () => {
    expect(inspectViewLabel('source_0', 0)).toBe('View 1');
    expect(inspectViewLabel('data_2', 1)).toBe('View 2');
  });

  test('a user-authored dataset name is shown verbatim', () => {
    expect(inspectViewLabel('sales', 0)).toBe('sales');
    expect(inspectViewLabel('data_foo', 2)).toBe('data_foo'); // no trailing digits → not compiler
  });
});
