/**
 * lib/sqlcmd — pure-layer tests (arg contract + output parsing; no DB spawn).
 */
import { describe, it, expect } from 'vitest';
import { buildSqlcmdArgs, parsePipeRows, parseForJson } from '../sqlcmd.js';
import type { ParsedConnection } from '../appsettings.js';

function conn(over: Partial<ParsedConnection> = {}): ParsedConnection {
  return {
    server: '(local)',
    database: 'SmartStack',
    useWindowsAuth: true,
    user: null,
    password: null,
    trustServerCertificate: false,
    raw: '',
    ...over,
  };
}

describe('buildSqlcmdArgs', () => {
  it('uses trusted auth (-E) for Windows auth and a default pipe separator', () => {
    const args = buildSqlcmdArgs(conn(), 'SELECT 1');
    expect(args).toContain('-E');
    expect(args).not.toContain('-U');
    expect(args.join(' ')).toContain('-s |');
    expect(args.slice(-2)).toEqual(['-Q', 'SELECT 1']);
    expect(args).toContain('(local)');
    expect(args).toContain('SmartStack');
  });

  it('uses -U/-P when a SQL user is configured', () => {
    const args = buildSqlcmdArgs(conn({ useWindowsAuth: false, user: 'sa', password: 'pw' }), 'SELECT 1');
    expect(args).toContain('-U');
    expect(args).toContain('sa');
    expect(args).toContain('-P');
    expect(args).toContain('pw');
    expect(args).not.toContain('-E');
  });

  it('switches to FOR JSON flags (-y 0, no separator) when forJson', () => {
    const args = buildSqlcmdArgs(conn(), 'SELECT 1 FOR JSON PATH', { forJson: true });
    expect(args.join(' ')).toContain('-y 0');
    expect(args).not.toContain('-s');
  });

  it('adds -C only when the connection trusts the server certificate', () => {
    expect(buildSqlcmdArgs(conn({ trustServerCertificate: true }), 'X')).toContain('-C');
    expect(buildSqlcmdArgs(conn({ trustServerCertificate: false }), 'X')).not.toContain('-C');
  });
});

describe('parsePipeRows', () => {
  it('splits cells, drops blank and pure-dash lines, trims trailing space', () => {
    const raw = 'APP|1|admin|Admin||/admin|0   \n\n------\nMOD|2|users|Users|1|/users|0';
    expect(parsePipeRows(raw)).toEqual([
      ['APP', '1', 'admin', 'Admin', '', '/admin', '0'],
      ['MOD', '2', 'users', 'Users', '1', '/users', '0'],
    ]);
  });
});

describe('parseForJson', () => {
  it('reassembles JSON split across rows', () => {
    const raw = '[{"a":1},\n{"a":2}]';
    expect(parseForJson<{ a: number }>(raw)).toEqual([{ a: 1 }, { a: 2 }]);
  });

  it('returns [] for empty / null output', () => {
    expect(parseForJson('')).toEqual([]);
    expect(parseForJson('NULL')).toEqual([]);
  });

  it('returns [] when the payload is not an array', () => {
    expect(parseForJson('{"a":1}')).toEqual([]);
  });

  it('throws on malformed JSON', () => {
    expect(() => parseForJson('[{not json')).toThrow(/Could not parse sqlcmd JSON/);
  });
});
