import { describe, expect, it } from 'vitest';
import { formatJson, formatSpec } from './json-format';

describe('formatJson', () => {
  it('pretty-prints a valid object with the default indent', () => {
    expect(formatJson('{"a":1,"b":2}')).toBe('{"a": 1, "b": 2}');
  });

  it('keeps short arrays compact (Vega house style)', () => {
    expect(formatJson('{"values":[1,2,3]}')).toBe('{"values": [1, 2, 3]}');
  });

  it('wraps content that exceeds the line-length budget', () => {
    expect(formatJson('{"a":1,"b":2,"c":3}', { maxLength: 5 })).toBe(
      '{\n  "a": 1,\n  "b": 2,\n  "c": 3\n}',
    );
  });

  it('respects a custom indent width', () => {
    const out = formatJson('{"a":1,"b":2}', { indent: 4, maxLength: 5 });
    expect(out).toBe('{\n    "a": 1,\n    "b": 2\n}');
  });

  it('returns null for invalid JSON so callers can skip', () => {
    expect(formatJson('{"a":')).toBeNull();
    expect(formatJson('not json')).toBeNull();
  });

  it('returns null for empty or whitespace-only input', () => {
    expect(formatJson('')).toBeNull();
    expect(formatJson('   \n  ')).toBeNull();
  });

  it('is idempotent — formatting formatted text changes nothing', () => {
    const once = formatJson('{"a":1,"b":[1,2,3],"c":{"d":4}}');
    expect(once).not.toBeNull();
    expect(formatJson(once!)).toBe(once);
  });

  it('handles top-level arrays and primitives', () => {
    expect(formatJson('[1,2,3]')).toBe('[1, 2, 3]');
    expect(formatJson('42')).toBe('42');
    expect(formatJson('true')).toBe('true');
  });
});

describe('formatSpec', () => {
  it('pretty-prints an object in the same house style as formatJson', () => {
    expect(formatSpec({ a: 1, b: 2 })).toBe('{"a": 1, "b": 2}');
  });

  it('agrees with formatJson for the equivalent text', () => {
    const obj = { mark: 'bar', encoding: { x: { field: 'a', type: 'nominal' } } };
    expect(formatSpec(obj)).toBe(formatJson(JSON.stringify(obj)));
  });

  it('preserves the object’s own key order (no canonical sort)', () => {
    // Insertion order is the contract — a lesson author orders keys for teaching.
    expect(formatSpec({ z: 1, a: 2 })).toBe('{"z": 1, "a": 2}');
  });

  it('respects indent and line-length options', () => {
    expect(formatSpec({ a: 1, b: 2 }, { maxLength: 5 })).toBe('{\n  "a": 1,\n  "b": 2\n}');
  });
});
