import { beforeEach, describe, expect, test } from 'vitest';
import { createDataset } from '@core/dataset';
import { useDatasetStore } from '../stores/DatasetStore';
import { dataInfoAt } from './active-dataset';

/** Seed a library dataset, profiled from inline rows. */
function seed(name: string, rows: Record<string, unknown>[]): void {
  useDatasetStore
    .getState()
    .add(createDataset({ name, data: rows, format: 'json', source: 'inline' }));
}

/** An offset inside the first occurrence of `marker` in `text`. */
function at(text: string, marker: string): number {
  return text.indexOf(marker) + 1;
}

const names = (cols: ReadonlyArray<{ name: string }>) => cols.map((c) => c.name).sort();

beforeEach(() => {
  useDatasetStore.getState().reset();
});

describe('dataInfoAt — view-scoped data context', () => {
  test('resolves a library binding case-insensitively', () => {
    seed('Sales', [{ region: 'W', revenue: 10 }]);
    const text = '{ "data": { "name": "sales" }, "encoding": { "x": { "field": "MK" } } }';
    const info = dataInfoAt(text, at(text, 'MK'));
    expect(info.name).toBe('Sales');
    expect(names(info.columnTypes)).toEqual(['region', 'revenue']);
  });

  test('each layer sees its own dataset, not a sibling view’s', () => {
    seed('Sales', [{ region: 'W', revenue: 10 }]);
    seed('Regions', [{ region: 'W', population: 100 }]);
    const text = JSON.stringify(
      {
        layer: [
          { data: { name: 'Sales' }, encoding: { x: { field: 'sales_mk' } } },
          { data: { name: 'Regions' }, encoding: { y: { field: 'regions_mk' } } },
        ],
      },
      null,
      2,
    );
    const inLayer0 = dataInfoAt(text, at(text, 'sales_mk'));
    const inLayer1 = dataInfoAt(text, at(text, 'regions_mk'));
    expect(inLayer0.name).toBe('Sales');
    expect(names(inLayer0.columnTypes)).toEqual(['region', 'revenue']);
    expect(inLayer1.name).toBe('Regions');
    expect(names(inLayer1.columnTypes)).toEqual(['population', 'region']);
  });

  test('a child without its own data inherits the parent binding', () => {
    seed('Sales', [{ region: 'W', revenue: 10 }]);
    const text = JSON.stringify(
      { data: { name: 'Sales' }, layer: [{ encoding: { x: { field: 'mk' } } }] },
      null,
      2,
    );
    const info = dataInfoAt(text, at(text, '"mk"'));
    expect(info.name).toBe('Sales');
    expect(names(info.columnTypes)).toEqual(['region', 'revenue']);
  });

  test('profiles an inline binding (ghost dataset, no name)', () => {
    const text =
      '{ "data": { "values": [{ "a": 1, "b": 2 }] }, "encoding": { "x": { "field": "MK" } } }';
    const info = dataInfoAt(text, at(text, 'MK'));
    expect(info.name).toBeNull();
    expect(names(info.columnTypes)).toEqual(['a', 'b']);
  });

  test('profiles a self-defined top-level datasets binding', () => {
    const text = JSON.stringify(
      {
        datasets: { local: [{ c: 1, d: 2 }] },
        data: { name: 'local' },
        encoding: { x: { field: 'mk' } },
      },
      null,
      2,
    );
    const info = dataInfoAt(text, at(text, '"mk"'));
    expect(info.name).toBeNull();
    expect(names(info.columnTypes)).toEqual(['c', 'd']);
  });

  test('offers ancestor-derived fields, not a sibling view’s', () => {
    seed('Sales', [{ region: 'W', revenue: 10 }]);
    const text = JSON.stringify(
      {
        data: { name: 'Sales' },
        transform: [{ calculate: 'datum.revenue * 2', as: 'shared' }],
        layer: [
          { transform: [{ calculate: 'x', as: 'inner' }], encoding: { x: { field: 'mk0' } } },
          { transform: [{ calculate: 'y', as: 'sibling' }], encoding: { y: { field: 'mk1' } } },
        ],
      },
      null,
      2,
    );
    const fields = dataInfoAt(text, at(text, 'mk0')).fields.map((f) => f.name);
    expect(fields).toContain('shared'); // top-level (inherited)
    expect(fields).toContain('inner'); // this view
    expect(fields).not.toContain('sibling'); // the other layer
  });

  test('a url/generator binding yields no static columns', () => {
    const url = '{ "data": { "url": "x.csv" }, "encoding": { "x": { "field": "MK" } } }';
    expect(dataInfoAt(url, at(url, 'MK')).columnTypes).toEqual([]);
  });
});
