const parser = require('../src/bruToJson');
const jsonToBru = require('../src/jsonToBru');
const envParser = require('../src/envToJson');
const jsonToEnv = require('../src/jsonToEnv');
const collectionParser = require('../src/collectionBruToJson');
const jsonToCollectionBru = require('../src/jsonToCollectionBru');
const fs = require('fs');
const path = require('path');

describe('pair annotations', () => {
  it('above-line annotations in asserts', () => {
    const input = `
assert {
  @description('hello')
  res.status: eq 200
}
`;
    const output = parser(input);
    expect(output.assertions).toEqual([
      { name: 'res.status', value: 'eq 200', enabled: true, annotations: [{ name: 'description', value: 'hello' }] }
    ]);
  });

  it('above-line annotation', () => {
    const input = `
headers {
  @description('hello')
  key: value
}
`;
    const output = parser(input);
    expect(output.headers).toEqual([
      { name: 'key', value: 'value', enabled: true, annotations: [{ name: 'description', value: 'hello' }] }
    ]);
  });

  it('annotation without args', () => {
    const input = `
headers {
  @string
  key: value
}
`;
    const output = parser(input);
    expect(output.headers).toEqual([
      { name: 'key', value: 'value', enabled: true, annotations: [{ name: 'string' }] }
    ]);
  });

  it('multiple above-line annotations on same pair', () => {
    const input = `
headers {
  @string
  @description('x')
  key: value
}
`;
    const output = parser(input);
    expect(output.headers).toEqual([
      {
        name: 'key',
        value: 'value',
        enabled: true,
        annotations: [{ name: 'string' }, { name: 'description', value: 'x' }]
      }
    ]);
  });

  it('multiple above-line annotations', () => {
    const input = `
headers {
  @string
  @description('hello')
  key: value
}
`;
    const output = parser(input);
    expect(output.headers).toEqual([
      {
        name: 'key',
        value: 'value',
        enabled: true,
        annotations: [{ name: 'string' }, { name: 'description', value: 'hello' }]
      }
    ]);
  });

  it('no annotation — output unchanged (backward compat)', () => {
    const input = `
headers {
  key: value
}
`;
    const output = parser(input);
    expect(output.headers).toEqual([{ name: 'key', value: 'value', enabled: true }]);
    expect(output.headers[0]).not.toHaveProperty('annotations');
  });

  it('disabled pair with annotation', () => {
    const input = `
headers {
  @string
  ~key: value
}
`;
    const output = parser(input);
    expect(output.headers).toEqual([
      { name: 'key', value: 'value', enabled: false, annotations: [{ name: 'string' }] }
    ]);
  });

  it('double-quoted annotation arg', () => {
    const input = `
headers {
  @description("hello")
  key: value
}
`;
    const output = parser(input);
    expect(output.headers[0].annotations).toEqual([{ name: 'description', value: 'hello' }]);
  });

  it('single quote inside double-quoted annotation arg (e.g. O\'Reilly)', () => {
    const input = `
headers {
  @description("O'Reilly")
  key: value
}
`;
    const output = parser(input);
    expect(output.headers[0].annotations).toEqual([{ name: 'description', value: 'O\'Reilly' }]);
  });

  it('double quote inside single-quoted annotation arg (e.g. say "hello")', () => {
    const input = `
headers {
  @description('say "hello"')
  key: value
}
`;
    const output = parser(input);
    expect(output.headers[0].annotations).toEqual([{ name: 'description', value: 'say "hello"' }]);
  });

  it('smoke test escaping special characters', () => {
    const input = fs.readFileSync(path.join(__dirname, './fixtures/annotations.bru'));
    const output = parser(input);
    expect(output.vars.req[0].annotations).toEqual([{ name: 'description', value: 'found in C:\\Users\\File\\Path' }]);
    expect(output.vars.req[1].annotations).toEqual([{ name: 'description', value: 'height of 2\' ' }]);
  });

  it('unquoted annotation arg', () => {
    const input = `
headers {
  @version(2)
  key: value
}
`;
    const output = parser(input);
    expect(output.headers[0].annotations).toEqual([{ name: 'version', value: '2' }]);
  });

  it('float (decimal) unquoted annotation arg', () => {
    const input = `
headers {
  @version(3.14)
  key: value
}
`;
    const output = parser(input);
    expect(output.headers[0].annotations).toEqual([{ name: 'version', value: '3.14' }]);
  });

  it('empty string arg', () => {
    const input = `
headers {
  @description('')
  key: value
}
`;
    const output = parser(input);
    expect(output.headers[0].annotations).toEqual([{ name: 'description', value: '' }]);
  });

  it('whitespace-only string arg preserves spaces', () => {
    const input = `
headers {
  @description('   ')
  key: value
}
`;
    const output = parser(input);
    expect(output.headers[0].annotations).toEqual([{ name: 'description', value: '   ' }]);
  });

  it('leading and trailing whitespace in string arg is preserved', () => {
    const input = `
headers {
  @description('  hello  ')
  key: value
}
`;
    const output = parser(input);
    expect(output.headers[0].annotations).toEqual([{ name: 'description', value: '  hello  ' }]);
  });

  it('unicode characters in annotation arg', () => {
    const input = `
headers {
  @description('日本語')
  key: value
}
`;
    const output = parser(input);
    expect(output.headers[0].annotations).toEqual([{ name: 'description', value: '日本語' }]);
  });

  it('URL with query string in annotation arg', () => {
    const input = `
headers {
  @description('https://example.com/path?q=1&r=2#anchor')
  key: value
}
`;
    const output = parser(input);
    expect(output.headers[0].annotations).toEqual([{ name: 'description', value: 'https://example.com/path?q=1&r=2#anchor' }]);
  });

  it('colon inside annotation arg value', () => {
    const input = `
headers {
  @description('Content-Type: application/json')
  key: value
}
`;
    const output = parser(input);
    expect(output.headers[0].annotations).toEqual([{ name: 'description', value: 'Content-Type: application/json' }]);
  });

  it('email address (@ symbol) inside annotation arg value', () => {
    const input = `
headers {
  @description('user@example.com')
  key: value
}
`;
    const output = parser(input);
    expect(output.headers[0].annotations).toEqual([{ name: 'description', value: 'user@example.com' }]);
  });

  it('template variable syntax inside annotation arg value', () => {
    const input = `
headers {
  @description('{{baseUrl}}/endpoint')
  key: value
}
`;
    const output = parser(input);
    expect(output.headers[0].annotations).toEqual([{ name: 'description', value: '{{baseUrl}}/endpoint' }]);
  });

  it('tab character inside annotation arg value', () => {
    const input = `headers {\n  @description('col1\tcol2')\n  key: value\n}\n`;
    const output = parser(input);
    expect(output.headers[0].annotations).toEqual([{ name: 'description', value: 'col1\tcol2' }]);
  });

  it('multiline string values', () => {
    const input = `headers { 
  @description('''
    make it rain
    make it rain2
  ''') 
  key: value
}`;
    const output = parser(input);
    expect(output.headers[0].annotations).toEqual([{ name: 'description', value: 'make it rain\nmake it rain2' }]);
  });

  it('serializeAnnotations — multiline value uses triple-quote delimiters and roundtrips correctly', () => {
    const json = {
      meta: { name: 'test', type: 'http', seq: 1 },
      http: { method: 'get', url: 'https://example.com' },
      headers: [{ name: 'x-key', value: 'val', enabled: true, annotations: [{ name: 'description', value: 'line one\nline two' }] }]
    };
    const bru = jsonToBru(json);
    expect(bru).toContain('@description(\'\'\'\n    line one\n    line two\n  \'\'\')\n  x-key: val'); const parsed = parser(bru);
    expect(parsed.headers[0].annotations).toEqual([{ name: 'description', value: 'line one\nline two' }]);
  });

  it('serializeAnnotations — empty string value roundtrips correctly', () => {
    const json = {
      meta: { name: 'test', type: 'http', seq: 1 },
      http: { method: 'get', url: 'https://example.com' },
      headers: [{ name: 'x-key', value: 'val', enabled: true, annotations: [{ name: 'description', value: '' }] }]
    };
    const bru = jsonToBru(json);
    const parsed = parser(bru);
    expect(parsed.headers[0].annotations).toEqual([{ name: 'description', value: '' }]);
  });

  it('serializeAnnotations — URL with special chars uses single-quote delimiters', () => {
    const json = {
      meta: { name: 'test', type: 'http', seq: 1 },
      http: { method: 'get', url: 'https://example.com' },
      headers: [{ name: 'x-key', value: 'val', enabled: true, annotations: [{ name: 'description', value: 'https://example.com?q=1&r=2' }] }]
    };
    const bru = jsonToBru(json);
    expect(bru).toContain('@description(\'https://example.com?q=1&r=2\')\n  x-key: val');
  });

  it('serializeAnnotations — template variable in value roundtrips correctly', () => {
    const json = {
      meta: { name: 'test', type: 'http', seq: 1 },
      http: { method: 'get', url: 'https://example.com' },
      headers: [{ name: 'x-key', value: 'val', enabled: true, annotations: [{ name: 'description', value: '{{baseUrl}}/path' }] }]
    };
    const bru = jsonToBru(json);
    const parsed = parser(bru);
    expect(parsed.headers[0].annotations).toEqual([{ name: 'description', value: '{{baseUrl}}/path' }]);
  });

  it('serializeAnnotations — annotation on params:path', () => {
    const json = {
      params: [{ name: 'userId', value: '123', enabled: true, type: 'path', annotations: [{ name: 'description', value: 'user id' }] }]
    };
    const bru = jsonToBru(json);
    expect(bru).toContain('params:path {');
    expect(bru).toContain('@description(\'user id\')\n  userId: 123');
  });

  it('serializeAnnotations — annotation on metadata', () => {
    const json = {
      metadata: [{ name: 'trace-id', value: 'abc123', enabled: true, annotations: [{ name: 'description', value: 'trace id' }] }]
    };
    const bru = jsonToBru(json);
    expect(bru).toContain('metadata {');
    expect(bru).toContain('@description(\'trace id\')\n  trace-id: abc123');
  });

  it('serializeAnnotations — annotation on body:form-urlencoded', () => {
    const json = {
      body: {
        formUrlEncoded: [{ name: 'username', value: 'alice', enabled: true, annotations: [{ name: 'description', value: 'username field' }] }]
      }
    };
    const bru = jsonToBru(json);
    expect(bru).toContain('body:form-urlencoded {');
    expect(bru).toContain('@description(\'username field\')\n  username: alice');
  });

  it('annotation on params:query block', () => {
    const input = `
params:query {
  @string
  q: search
}
`;
    const output = parser(input);
    expect(output.params).toEqual([
      { name: 'q', value: 'search', enabled: true, type: 'query', annotations: [{ name: 'string' }] }
    ]);
  });

  it('annotation on params:path block', () => {
    const input = `
params:path {
  @description('user id')
  userId: 123
}
`;
    const output = parser(input);
    expect(output.params).toEqual([
      { name: 'userId', value: '123', enabled: true, type: 'path', annotations: [{ name: 'description', value: 'user id' }] }
    ]);
  });

  it('annotation on metadata block', () => {
    const input = `
metadata {
  @description('trace id')
  trace-id: abc123
}
`;
    const output = parser(input);
    expect(output.metadata).toEqual([
      { name: 'trace-id', value: 'abc123', enabled: true, annotations: [{ name: 'description', value: 'trace id' }] }
    ]);
  });

  it('annotation on body:form-urlencoded block', () => {
    const input = `
body:form-urlencoded {
  @description('username field')
  username: alice
}
`;
    const output = parser(input);
    expect(output.body.formUrlEncoded).toEqual([
      { name: 'username', value: 'alice', enabled: true, annotations: [{ name: 'description', value: 'username field' }] }
    ]);
  });

  it('annotation on vars:pre-request block', () => {
    const input = `
vars:pre-request {
  @description('base url')
  myVar: http://localhost
}
`;
    const output = parser(input);
    expect(output.vars.req).toEqual([
      {
        name: 'myVar',
        value: 'http://localhost',
        enabled: true,
        local: false,
        annotations: [{ name: 'description', value: 'base url' }]
      }
    ]);
  });

  it('annotation on vars:post-response block', () => {
    const input = `
vars:post-response {
  @description('auth token')
  token: abc123
}
`;
    const output = parser(input);
    expect(output.vars.res).toEqual([
      {
        name: 'token',
        value: 'abc123',
        enabled: true,
        local: false,
        annotations: [{ name: 'description', value: 'auth token' }]
      }
    ]);
  });

  it('annotation on local vars:pre-request pair', () => {
    const input = `
vars:pre-request {
  @description('local base url')
  @BASE_URL: http://localhost
}
`;
    const output = parser(input);
    expect(output.vars.req).toEqual([
      {
        name: 'BASE_URL',
        value: 'http://localhost',
        enabled: true,
        local: true,
        annotations: [{ name: 'description', value: 'local base url' }]
      }
    ]);
  });

  it('annotation on local vars:post-response pair', () => {
    const input = `
vars:post-response {
  @description('local token')
  @token: abc123
}
`;
    const output = parser(input);
    expect(output.vars.res).toEqual([
      {
        name: 'token',
        value: 'abc123',
        enabled: true,
        local: true,
        annotations: [{ name: 'description', value: 'local token' }]
      }
    ]);
  });

  it('annotation on body:multipart-form text field', () => {
    const input = `
body:multipart-form {
  @description('plain field')
  field: value @contentType(text/plain)
}
`;
    const output = parser(input);
    expect(output.body.multipartForm).toEqual([
      {
        name: 'field',
        value: 'value',
        enabled: true,
        type: 'text',
        contentType: 'text/plain',
        annotations: [{ name: 'description', value: 'plain field' }]
      }
    ]);
  });

  it('annotation on body:multipart-form file field', () => {
    const input = `
body:multipart-form {
  @description('upload image')
  upload: @file(/tmp/a.png|/tmp/b.png) @contentType(image/png)
}
`;
    const output = parser(input);
    expect(output.body.multipartForm).toEqual([
      {
        name: 'upload',
        value: ['/tmp/a.png', '/tmp/b.png'],
        enabled: true,
        type: 'file',
        contentType: 'image/png',
        annotations: [{ name: 'description', value: 'upload image' }]
      }
    ]);
  });

  it('annotation on body:file', () => {
    const input = `
body:file {
  @description('upload doc')
  file: @file(/tmp/readme.pdf) @contentType(application/pdf)
}
`;
    const output = parser(input);
    expect(output.body.file).toEqual([
      {
        filePath: '/tmp/readme.pdf',
        selected: true,
        contentType: 'application/pdf',
        annotations: [{ name: 'description', value: 'upload doc' }]
      }
    ]);
  });

  it('serializeAnnotations — multipart text field with contentType', () => {
    const json = {
      body: {
        multipartForm: [
          {
            name: 'field',
            value: 'value',
            enabled: true,
            type: 'text',
            contentType: 'text/plain',
            annotations: [{ name: 'description', value: 'plain field' }]
          }
        ]
      }
    };
    const bru = jsonToBru(json);
    expect(bru).toContain('@description(\'plain field\')\n  field: value @contentType(text/plain)');
  });

  it('serializeAnnotations — multipart file field with contentType', () => {
    const json = {
      body: {
        multipartForm: [
          {
            name: 'upload',
            value: ['/tmp/a.png', '/tmp/b.png'],
            enabled: true,
            type: 'file',
            contentType: 'image/png',
            annotations: [{ name: 'description', value: 'upload image' }]
          }
        ]
      }
    };
    const bru = jsonToBru(json);
    expect(bru).toContain('@description(\'upload image\')\n  upload: @file(/tmp/a.png|/tmp/b.png) @contentType(image/png)');
  });

  it('serializeAnnotations — annotation on vars:post-response', () => {
    const json = {
      vars: {
        res: [{ name: 'token', value: 'abc123', enabled: true, local: false, annotations: [{ name: 'description', value: 'auth token' }] }]
      }
    };
    const bru = jsonToBru(json);
    expect(bru).toContain('vars:post-response {');
    expect(bru).toContain('@description(\'auth token\')\n  token: abc123');
  });

  it('serializeAnnotations — annotation on local vars:pre-request', () => {
    const json = {
      vars: {
        req: [{ name: 'BASE_URL', value: 'http://localhost', enabled: true, local: true, annotations: [{ name: 'description', value: 'local base url' }] }]
      }
    };
    const bru = jsonToBru(json);
    expect(bru).toContain('vars:pre-request {');
    expect(bru).toContain('@description(\'local base url\')\n  @BASE_URL: http://localhost');
  });

  it('serializeAnnotations — annotation on disabled local vars:post-response', () => {
    const json = {
      vars: {
        res: [{ name: 'token', value: 'abc123', enabled: false, local: true, annotations: [{ name: 'description', value: 'local token' }] }]
      }
    };
    const bru = jsonToBru(json);
    expect(bru).toContain('vars:post-response {');
    expect(bru).toContain('@description(\'local token\')\n  ~@token: abc123');
  });

  it('serializeAnnotations — body:file with annotations', () => {
    const json = {
      body: {
        file: [{ filePath: '/tmp/readme.pdf', selected: true, contentType: 'application/pdf', annotations: [{ name: 'description', value: 'upload doc' }] }]
      }
    };
    const bru = jsonToBru(json);
    expect(bru).toContain('body:file {');
    expect(bru).toContain('@description(\'upload doc\')\n  file: @file(/tmp/readme.pdf) @contentType(application/pdf)');
    const parsed = parser(bru);
    expect(parsed.body.file).toEqual(json.body.file);
  });

  it('roundtrip — multipart annotation survives json→bru→json', () => {
    const json = {
      body: {
        multipartForm: [
          {
            name: 'upload',
            value: ['/tmp/a.png'],
            enabled: true,
            type: 'file',
            contentType: 'image/png',
            annotations: [{ name: 'description', value: 'upload image' }]
          }
        ]
      }
    };
    const bru = jsonToBru(json);
    const parsed = parser(bru);
    expect(parsed.body.multipartForm).toEqual(json.body.multipartForm);
  });

  it('roundtrip: bru → json → bru → json equal', () => {
    const input = `get {
  url: https://example.com
}

headers {
  @description('Content type')
  content-type: application/json
  @string
  ~accept: */*
}
`;
    const json1 = parser(input);
    const bru = jsonToBru(json1);
    const json2 = parser(bru);
    expect(json2.headers).toEqual(json1.headers);
  });

  it('serializeAnnotations — annotation without value', () => {
    const json = {
      meta: { name: 'test', type: 'http', seq: 1 },
      http: { method: 'get', url: 'https://example.com' },
      headers: [{ name: 'x-key', value: 'val', enabled: true, annotations: [{ name: 'string' }] }]
    };
    const bru = jsonToBru(json);
    expect(bru).toContain('@string\n  x-key: val');
  });

  it('serializeAnnotations — annotation with value', () => {
    const json = {
      meta: { name: 'test', type: 'http', seq: 1 },
      http: { method: 'get', url: 'https://example.com' },
      headers: [
        { name: 'x-key', value: 'val', enabled: true, annotations: [{ name: 'description', value: 'my header' }] }
      ]
    };
    const bru = jsonToBru(json);
    expect(bru).toContain('@description(\'my header\')\n  x-key: val');
  });

  it('serializeAnnotations — disabled pair with annotation', () => {
    const json = {
      meta: { name: 'test', type: 'http', seq: 1 },
      http: { method: 'get', url: 'https://example.com' },
      headers: [{ name: 'x-key', value: 'val', enabled: false, annotations: [{ name: 'string' }] }]
    };
    const bru = jsonToBru(json);
    expect(bru).toContain('@string\n  ~x-key: val');
  });

  it('serializeAnnotations — value with single quote uses double-quote delimiters (e.g. O\'Reilly)', () => {
    const json = {
      meta: { name: 'test', type: 'http', seq: 1 },
      http: { method: 'get', url: 'https://example.com' },
      headers: [{ name: 'x-key', value: 'val', enabled: true, annotations: [{ name: 'description', value: 'O\'Reilly' }] }]
    };
    const bru = jsonToBru(json);
    expect(bru).toContain('@description("O\'Reilly")\n  x-key: val');
  });

  it('serializeAnnotations — value with double quote uses single-quote delimiters (e.g. say "hello")', () => {
    const json = {
      meta: { name: 'test', type: 'http', seq: 1 },
      http: { method: 'get', url: 'https://example.com' },
      headers: [{ name: 'x-key', value: 'val', enabled: true, annotations: [{ name: 'description', value: 'say "hello"' }] }]
    };
    const bru = jsonToBru(json);
    expect(bru).toContain('@description(\'say "hello"\')\n  x-key: val');
  });

  it('parseAndSerialise - bru sourced roundtrip check - headers', () => {
    const input = `headers {
  @description('hello')
  key: value
}
`;
    const parsed = parser(input);
    const output = jsonToBru(parsed);

    expect(input).toEqual(output);
  });

  it('parseAndSerialise - json sourced roundtrip check - headers', () => {
    const input = {
      headers: [{ name: 'x-key', value: 'val', enabled: true, annotations: [{ name: 'description', value: 'say "hello"' }] }]
    };
    const stringified = jsonToBru(input);
    const output = parser(stringified);

    expect(input).toEqual(output);
  });

  it('parseAndSerialise - bru sourced roundtrip check - asserts', () => {
    const input = `assert {
  @description('make it rain')
  res.status: eq 200
}
`;

    const parsed = parser(input);
    const output = jsonToBru(parsed);

    expect(input).toEqual(output);
  });

  it('parseAndSerialise - json sourced roundtrip check - asserts', () => {
    const input = {
      assertions: [
        {
          annotations: [{ name: 'description', value: 'hello' }],
          name: 'res.status', value: 'eq 200', enabled: true }
      ]
    };

    const parsed = jsonToBru(input);
    const output = parser(parsed);

    expect(input).toEqual(output);
  });

  it('paren inside single-quoted annotation arg — Token (JWT)', () => {
    const input = `headers {
  @description('Token (JWT)')
  key: value
}
`;
    const output = parser(input);
    expect(output.headers[0].annotations).toEqual([{ name: 'description', value: 'Token (JWT)' }]);
  });

  it('paren inside double-quoted annotation arg — Result (OK)', () => {
    const input = `headers {
  @description("Result (OK)")
  key: value
}
`;
    const output = parser(input);
    expect(output.headers[0].annotations).toEqual([{ name: 'description', value: 'Result (OK)' }]);
  });

  it('multiple parens inside single-quoted annotation arg', () => {
    const input = `headers {
  @description('func(a, b) returns (c)')
  key: value
}
`;
    const output = parser(input);
    expect(output.headers[0].annotations).toEqual([{ name: 'description', value: 'func(a, b) returns (c)' }]);
  });

  it('roundtrip — value containing parens survives json→bru→json — Token (JWT)', () => {
    const json = {
      meta: { name: 'test', type: 'http', seq: 1 },
      http: { method: 'get', url: 'https://example.com' },
      headers: [
        { name: 'Authorization', value: 'Bearer token', enabled: true, annotations: [{ name: 'description', value: 'Token (JWT)' }] }
      ]
    };
    const bru = jsonToBru(json);
    const parsed = parser(bru);
    expect(parsed.headers[0].annotations).toEqual([{ name: 'description', value: 'Token (JWT)' }]);
  });

  it('inline annotation on a header is rejected', () => {
    const input = `
headers {
  @string key: value
}
`;
    expect(() => parser(input)).toThrow();
  });
});

describe('env pair annotations', () => {
  it('above-line annotation with string arg on a var', () => {
    const input = `vars {
  @description('my api key')
  API_KEY: abc123
}
`;
    const output = envParser(input);
    expect(output.variables).toEqual([
      { name: 'API_KEY', value: 'abc123', enabled: true, secret: false, annotations: [{ name: 'description', value: 'my api key' }] }
    ]);
  });

  it('above-line annotation on a var', () => {
    const input = `vars {
  @deprecated
  OLD_KEY: old_value
}
`;
    const output = envParser(input);
    expect(output.variables).toEqual([
      { name: 'OLD_KEY', value: 'old_value', enabled: true, secret: false, annotations: [{ name: 'deprecated' }] }
    ]);
  });

  it('annotation without args on a var', () => {
    const input = `vars {
  @string
  API_KEY: abc
}
`;
    const output = envParser(input);
    expect(output.variables[0].annotations).toEqual([{ name: 'string' }]);
  });

  it('multiple annotations on a var', () => {
    const input = `vars {
  @string
  @description('base url')
  BASE_URL: http://localhost
}
`;
    const output = envParser(input);
    expect(output.variables[0].annotations).toEqual([{ name: 'string' }, { name: 'description', value: 'base url' }]);
  });

  it('disabled var with annotation', () => {
    const input = `vars {
  @deprecated
  ~OLD_KEY: old_value
}
`;
    const output = envParser(input);
    expect(output.variables).toEqual([
      { name: 'OLD_KEY', value: 'old_value', enabled: false, secret: false, annotations: [{ name: 'deprecated' }] }
    ]);
  });

  it('no annotation — output unchanged (backward compat)', () => {
    const input = `vars {
  API_KEY: abc123
}
`;
    const output = envParser(input);
    expect(output.variables[0]).not.toHaveProperty('annotations');
    expect(output.variables[0]).toEqual({ name: 'API_KEY', value: 'abc123', enabled: true, secret: false });
  });

  it('secret vars are unaffected by annotation support', () => {
    const input = `vars:secret [
  SECRET_KEY
]
`;
    const output = envParser(input);
    expect(output.variables).toEqual([{ name: 'SECRET_KEY', value: '', enabled: true, secret: true }]);
  });

  it('serializeAnnotations in jsonToEnv — annotation without value', () => {
    const json = {
      variables: [{ name: 'API_KEY', value: 'abc', enabled: true, secret: false, annotations: [{ name: 'deprecated' }] }]
    };
    const bru = jsonToEnv(json);
    expect(bru).toContain('@deprecated\n  API_KEY: abc');
  });

  it('serializeAnnotations in jsonToEnv — annotation with value', () => {
    const json = {
      variables: [{ name: 'BASE_URL', value: 'http://localhost', enabled: true, secret: false, annotations: [{ name: 'description', value: 'base url' }] }]
    };
    const bru = jsonToEnv(json);
    expect(bru).toContain('@description(\'base url\')\n  BASE_URL: http://localhost');
  });

  it('serializeAnnotations in jsonToEnv — disabled var with annotation', () => {
    const json = {
      variables: [{ name: 'OLD_KEY', value: 'old', enabled: false, secret: false, annotations: [{ name: 'deprecated' }] }]
    };
    const bru = jsonToEnv(json);
    expect(bru).toContain('@deprecated\n  ~OLD_KEY: old');
  });

  it('parseAndSerialise - bru sourced roundtrip check - env vars', () => {
    const input = `vars {
  @description('api key')
  API_KEY: abc123
}
`;
    const parsed = envParser(input);
    const output = jsonToEnv(parsed);
    expect(output).toEqual(input);
  });

  it('parseAndSerialise - json sourced roundtrip check - env vars', () => {
    const input = {
      variables: [{ name: 'API_KEY', value: 'abc123', enabled: true, secret: false, annotations: [{ name: 'description', value: 'api key' }] }]
    };
    const bru = jsonToEnv(input);
    const output = envParser(bru);
    expect(output).toEqual(input);
  });

  it('inline annotation on an env var is rejected', () => {
    const input = `vars {
  @deprecated API_KEY: abc
}
`;
    expect(() => envParser(input)).toThrow();
  });
});

describe('collection pair annotations', () => {
  it('above-line annotation on a header (collection)', () => {
    const input = `headers {
  @description('content type')
  content-type: application/json
}
`;
    const output = collectionParser(input);
    expect(output.headers).toEqual([
      { name: 'content-type', value: 'application/json', enabled: true, annotations: [{ name: 'description', value: 'content type' }] }
    ]);
  });

  it('above-line annotation on a header', () => {
    const input = `headers {
  @deprecated
  old-header: old-value
}
`;
    const output = collectionParser(input);
    expect(output.headers).toEqual([
      { name: 'old-header', value: 'old-value', enabled: true, annotations: [{ name: 'deprecated' }] }
    ]);
  });

  it('annotation on a query param', () => {
    const input = `query {
  @string
  q: search
}
`;
    const output = collectionParser(input);
    expect(output.query).toEqual([
      { name: 'q', value: 'search', enabled: true, annotations: [{ name: 'string' }] }
    ]);
  });

  it('disabled header with annotation', () => {
    const input = `headers {
  @deprecated
  ~x-old: value
}
`;
    const output = collectionParser(input);
    expect(output.headers).toEqual([
      { name: 'x-old', value: 'value', enabled: false, annotations: [{ name: 'deprecated' }] }
    ]);
  });

  it('annotation on vars:pre-request', () => {
    const input = `vars:pre-request {
  @description('base url')
  BASE_URL: http://localhost
}
`;
    const output = collectionParser(input);
    expect(output.vars.req).toEqual([
      { name: 'BASE_URL', value: 'http://localhost', enabled: true, local: false, annotations: [{ name: 'description', value: 'base url' }] }
    ]);
  });

  it('annotation on vars:post-response', () => {
    const input = `vars:post-response {
  @string
  token: abc
}
`;
    const output = collectionParser(input);
    expect(output.vars.res).toEqual([
      { name: 'token', value: 'abc', enabled: true, local: false, annotations: [{ name: 'string' }] }
    ]);
  });

  it('local var (@-prefixed) is not misidentified as annotation', () => {
    const input = `vars:pre-request {
  @localVar: http://localhost
}
`;
    const output = collectionParser(input);
    expect(output.vars.req).toEqual([
      { name: 'localVar', value: 'http://localhost', enabled: true, local: true }
    ]);
    expect(output.vars.req[0]).not.toHaveProperty('annotations');
  });

  it('no annotation — output unchanged (backward compat)', () => {
    const input = `headers {
  content-type: application/json
}
`;
    const output = collectionParser(input);
    expect(output.headers[0]).not.toHaveProperty('annotations');
    expect(output.headers[0]).toEqual({ name: 'content-type', value: 'application/json', enabled: true });
  });

  it('serializeAnnotations in jsonToCollectionBru — header without value', () => {
    const json = {
      headers: [{ name: 'x-key', value: 'val', enabled: true, annotations: [{ name: 'string' }] }]
    };
    const bru = jsonToCollectionBru(json);
    expect(bru).toContain('@string\n  x-key: val');
  });

  it('serializeAnnotations in jsonToCollectionBru — header with annotation value', () => {
    const json = {
      headers: [{ name: 'content-type', value: 'application/json', enabled: true, annotations: [{ name: 'description', value: 'content type' }] }]
    };
    const bru = jsonToCollectionBru(json);
    expect(bru).toContain('@description(\'content type\')\n  content-type: application/json');
  });

  it('serializeAnnotations in jsonToCollectionBru — disabled header with annotation', () => {
    const json = {
      headers: [{ name: 'x-old', value: 'val', enabled: false, annotations: [{ name: 'deprecated' }] }]
    };
    const bru = jsonToCollectionBru(json);
    expect(bru).toContain('@deprecated\n  ~x-old: val');
  });

  it('serializeAnnotations in jsonToCollectionBru — query param with annotation', () => {
    const json = {
      query: [{ name: 'q', value: 'search', enabled: true, annotations: [{ name: 'string' }] }]
    };
    const bru = jsonToCollectionBru(json);
    expect(bru).toContain('@string\n  q: search');
  });

  it('serializeAnnotations in jsonToCollectionBru — vars:pre-request with annotation', () => {
    const json = {
      vars: {
        req: [{ name: 'BASE_URL', value: 'http://localhost', enabled: true, local: false, annotations: [{ name: 'description', value: 'base url' }] }]
      }
    };
    const bru = jsonToCollectionBru(json);
    expect(bru).toContain('@description(\'base url\')\n  BASE_URL: http://localhost');
  });

  it('serializeAnnotations in jsonToCollectionBru — vars:post-response with annotation', () => {
    const json = {
      vars: {
        res: [{ name: 'token', value: 'abc123', enabled: true, local: false, annotations: [{ name: 'description', value: 'auth token' }] }]
      }
    };
    const bru = jsonToCollectionBru(json);
    expect(bru).toContain('vars:post-response {');
    expect(bru).toContain('@description(\'auth token\')\n  token: abc123');
  });

  it('serializeAnnotations in jsonToCollectionBru — local vars:pre-request with annotation', () => {
    const json = {
      vars: {
        req: [{ name: 'BASE_URL', value: 'http://localhost', enabled: true, local: true, annotations: [{ name: 'description', value: 'local base url' }] }]
      }
    };
    const bru = jsonToCollectionBru(json);
    expect(bru).toContain('vars:pre-request {');
    expect(bru).toContain('@description(\'local base url\')\n  @BASE_URL: http://localhost');
  });

  it('serializeAnnotations in jsonToCollectionBru — disabled local vars:post-response with annotation', () => {
    const json = {
      vars: {
        res: [{ name: 'token', value: 'abc123', enabled: false, local: true, annotations: [{ name: 'description', value: 'local token' }] }]
      }
    };
    const bru = jsonToCollectionBru(json);
    expect(bru).toContain('vars:post-response {');
    expect(bru).toContain('@description(\'local token\')\n  ~@token: abc123');
  });

  it('parseAndSerialise - bru sourced roundtrip check - collection headers', () => {
    const input = `headers {
  @description('content type')
  content-type: application/json
}
`;
    const parsed = collectionParser(input);
    const output = jsonToCollectionBru(parsed);
    expect(output).toEqual(input);
  });

  it('parseAndSerialise - json sourced roundtrip check - collection headers', () => {
    const input = {
      headers: [{ name: 'content-type', value: 'application/json', enabled: true, annotations: [{ name: 'description', value: 'content type' }] }]
    };
    const bru = jsonToCollectionBru(input);
    const output = collectionParser(bru);
    expect(output).toEqual(input);
  });

  it('parseAndSerialise - bru sourced roundtrip check - collection vars:pre-request', () => {
    const input = `vars:pre-request {
  @description('base url')
  BASE_URL: http://localhost
}
`;
    const parsed = collectionParser(input);
    const output = jsonToCollectionBru(parsed);
    expect(output).toEqual(input);
  });

  it('inline annotation on a collection header is rejected', () => {
    const input = `headers {
  @string x-key: val
}
`;
    expect(() => collectionParser(input)).toThrow();
  });
});
