import assert from 'node:assert';
import { describe, test } from 'vitest';
import { fixJson } from './fix-json';

test('should handle empty input', () => {
  assert.strictEqual(fixJson(''), '');
});

describe('literals', () => {
  test('should handle incomplete null', () => {
    assert.strictEqual(fixJson('nul'), 'null');
  });

  test('should handle incomplete true', () => {
    assert.strictEqual(fixJson('t'), 'true');
  });

  test('should handle incomplete false', () => {
    assert.strictEqual(fixJson('fals'), 'false');
  });
});

describe('number', () => {
  test('should handle incomplete numbers', () => {
    assert.strictEqual(fixJson('12.'), '12');
  });

  test('should handle numbers with dot', () => {
    assert.strictEqual(fixJson('12.2'), '12.2');
  });

  test('should handle negative numbers', () => {
    assert.strictEqual(fixJson('-12'), '-12');
  });

  test('should handle incomplete negative numbers', () => {
    assert.strictEqual(fixJson('-'), '');
  });

  test('should handle e-notation numbers', () => {
    assert.strictEqual(fixJson('2.5e'), '2.5');
    assert.strictEqual(fixJson('2.5e-'), '2.5');
    assert.strictEqual(fixJson('2.5e3'), '2.5e3');
    assert.strictEqual(fixJson('-2.5e3'), '-2.5e3');
  });

  test('should handle uppercase e-notation numbers', () => {
    assert.strictEqual(fixJson('2.5E'), '2.5');
    assert.strictEqual(fixJson('2.5E-'), '2.5');
    assert.strictEqual(fixJson('2.5E3'), '2.5E3');
    assert.strictEqual(fixJson('-2.5E3'), '-2.5E3');
  });

  test('should handle incomplete numbers', () => {
    assert.strictEqual(fixJson('12.e'), '12');
    assert.strictEqual(fixJson('12.34e'), '12.34');
    assert.strictEqual(fixJson('5e'), '5');
  });
});

describe('string', () => {
  test('should handle incomplete strings', () => {
    assert.strictEqual(fixJson('"abc'), '"abc"');
  });

  test('should handle escape sequences', () => {
    assert.strictEqual(
      fixJson('"value with \\"quoted\\" text and \\\\ escape'),
      '"value with \\"quoted\\" text and \\\\ escape"',
    );
  });

  test('should handle incomplete escape sequences', () => {
    assert.strictEqual(fixJson('"value with \\'), '"value with "');
  });

  test('should handle unicode characters', () => {
    assert.strictEqual(
      fixJson('"value with unicode \u003C"'),
      '"value with unicode \u003C"',
    );
  });
});

describe('array', () => {
  test('should handle incomplete array', () => {
    assert.strictEqual(fixJson('['), '[]');
  });

  test('should handle closing bracket after number in array', () => {
    assert.strictEqual(fixJson('[[1], [2'), '[[1], [2]]');
  });

  test('should handle closing bracket after string in array', () => {
    assert.strictEqual(fixJson(`[["1"], ["2`), `[["1"], ["2"]]`);
  });

  test('should handle closing bracket after literal in array', () => {
    assert.strictEqual(fixJson('[[false], [nu'), '[[false], [null]]');
  });

  test('should handle closing bracket after array in array', () => {
    assert.strictEqual(fixJson('[[[]], [[]'), '[[[]], [[]]]');
  });

  test('should handle closing bracket after object in array', () => {
    assert.strictEqual(fixJson('[[{}], [{'), '[[{}], [{}]]');
  });

  test('should handle trailing comma', () => {
    assert.strictEqual(fixJson('[1, '), '[1]');
  });

  test('should handle closing array', () => {
    assert.strictEqual(fixJson('[[], 123'), '[[], 123]');
  });
});

describe('object', () => {
  test('should handle keys without values', () => {
    assert.strictEqual(fixJson('{"key":'), '{}');
  });

  test('should handle closing brace after number in object', () => {
    assert.strictEqual(
      fixJson('{"a": {"b": 1}, "c": {"d": 2'),
      '{"a": {"b": 1}, "c": {"d": 2}}',
    );
  });

  test('should handle closing brace after string in object', () => {
    assert.strictEqual(
      fixJson('{"a": {"b": "1"}, "c": {"d": 2'),
      '{"a": {"b": "1"}, "c": {"d": 2}}',
    );
  });

  test('should handle closing brace after literal in object', () => {
    assert.strictEqual(
      fixJson('{"a": {"b": false}, "c": {"d": 2'),
      '{"a": {"b": false}, "c": {"d": 2}}',
    );
  });

  test('should handle closing brace after array in object', () => {
    assert.strictEqual(
      fixJson('{"a": {"b": []}, "c": {"d": 2'),
      '{"a": {"b": []}, "c": {"d": 2}}',
    );
  });

  test('should handle closing brace after object in object', () => {
    assert.strictEqual(
      fixJson('{"a": {"b": {}}, "c": {"d": 2'),
      '{"a": {"b": {}}, "c": {"d": 2}}',
    );
  });

  test('should handle partial keys (first key)', () => {
    assert.strictEqual(fixJson('{"ke'), '{}');
  });

  test('should handle partial keys (second key)', () => {
    assert.strictEqual(fixJson('{"k1": 1, "k2'), '{"k1": 1}');
  });

  test('should handle partial keys with colon (second key)', () => {
    assert.strictEqual(fixJson('{"k1": 1, "k2":'), '{"k1": 1}');
  });

  test('should handle trailing whitespaces', () => {
    assert.strictEqual(fixJson('{"key": "value"  '), '{"key": "value"}');
  });
});

describe('nesting', () => {
  test('should handle nested arrays with numbers', () => {
    assert.strictEqual(fixJson('[1, [2, 3, ['), '[1, [2, 3, []]]');
  });

  test('should handle nested arrays with literals', () => {
    assert.strictEqual(fixJson('[false, [true, ['), '[false, [true, []]]');
  });

  test('should handle nested objects', () => {
    assert.strictEqual(fixJson('{"key": {"subKey":'), '{"key": {}}');
  });

  test('should handle nested objects with numbers', () => {
    assert.strictEqual(
      fixJson('{"key": 123, "key2": {"subKey":'),
      '{"key": 123, "key2": {}}',
    );
  });

  test('should handle nested objects with literals', () => {
    assert.strictEqual(
      fixJson('{"key": null, "key2": {"subKey":'),
      '{"key": null, "key2": {}}',
    );
  });

  test('should handle arrays within objects', () => {
    assert.strictEqual(fixJson('{"key": [1, 2, {'), '{"key": [1, 2, {}]}');
  });

  test('should handle objects within arrays', () => {
    assert.strictEqual(
      fixJson('[1, 2, {"key": "value",'),
      '[1, 2, {"key": "value"}]',
    );
  });

  test('should handle nested arrays and objects', () => {
    assert.strictEqual(
      fixJson('{"a": {"b": ["c", {"d": "e",'),
      '{"a": {"b": ["c", {"d": "e"}]}}',
    );
  });

  test('should handle deeply nested objects', () => {
    assert.strictEqual(
      fixJson('{"a": {"b": {"c": {"d":'),
      '{"a": {"b": {"c": {}}}}',
    );
  });

  test('should handle potential nested arrays or objects', () => {
    assert.strictEqual(fixJson('{"a": 1, "b": ['), '{"a": 1, "b": []}');
    assert.strictEqual(fixJson('{"a": 1, "b": {'), '{"a": 1, "b": {}}');
    assert.strictEqual(fixJson('{"a": 1, "b": "'), '{"a": 1, "b": ""}');
  });
});

describe('regression', () => {
  test('should handle complex nesting 1', () => {
    assert.strictEqual(
      fixJson(
        [
          '{',
          '  "a": [',
          '    {',
          '      "a1": "v1",',
          '      "a2": "v2",',
          `      "a3": "v3"`,
          '    }',
          '  ],',
          '  "b": [',
          '    {',
          '      "b1": "n',
        ].join('\n'),
      ),
      [
        '{',
        '  "a": [',
        '    {',
        '      "a1": "v1",',
        '      "a2": "v2",',
        `      "a3": "v3"`,
        '    }',
        '  ],',
        '  "b": [',
        '    {',
        '      "b1": "n"}]}',
      ].join('\n'),
    );
  });
});
