'use strict';

const {describe, test} = require('node:test');
const assert = require('node:assert');

const {hashPassword, hashBasicAuthUsers} = require('../../utils/basicauth');
const {
	parseBasicAuthLines,
	sanitizeBasicAuthObject,
	sanitizeRealm,
	parseAllowList,
	normalizeHostFeatures,
} = require('../../utils/host_features');

/**
 * Per-host basic auth (#57). The hash must match what OpenResty computes in
 * ops/nginx_conf/hostfeatures.lua: base64(sha1(password)) (htpasswd "{SHA}").
 */
describe('basicauth hashing', () => {
	test('base64(sha1(password)) matches the known htpasswd {SHA} vector', () => {
		assert.strictEqual(hashPassword('secret'), '5en6G6MezRroT3XKqkdPOmY/BfQ=');
	});
	test('hashBasicAuthUsers hashes each password, skips empties', () => {
		assert.deepStrictEqual(
			hashBasicAuthUsers({alice: 'secret', bob: '', carol: null}),
			{alice: '5en6G6MezRroT3XKqkdPOmY/BfQ='}
		);
	});
});

describe('parseBasicAuthLines', () => {
	test('parses user:password lines; passwords may contain colons', () => {
		assert.deepStrictEqual(
			parseBasicAuthLines('alice:secret\nbob:pw:with:colons'),
			{alice: 'secret', bob: 'pw:with:colons'}
		);
	});
	test('drops blank lines, lines without a colon, and empty passwords', () => {
		assert.deepStrictEqual(
			parseBasicAuthLines('\nalice:secret\nnopassword\nbob:\n   \n'),
			{alice: 'secret'}
		);
	});
	test('rejects usernames with spaces/control chars', () => {
		assert.deepStrictEqual(parseBasicAuthLines('a b:secret'), {});
	});
});

describe('sanitizeRealm', () => {
	test('strips CR/LF and quotes and trims', () => {
		assert.strictEqual(sanitizeRealm('My "Realm"\r\n'), 'My Realm');
		assert.strictEqual(sanitizeRealm(undefined), '');
	});
});

describe('normalizeHostFeatures (basic auth)', () => {
	test('coerces enabled, parses users to plaintext object, sanitizes realm', () => {
		let body = {
			basicauth_enabled: 'true',
			basicauth_realm: 'Admins\n',
			basicauth_users: 'alice:secret\nbob:pw',
		};
		normalizeHostFeatures(body);
		assert.strictEqual(body.basicauth_enabled, true);
		assert.strictEqual(body.basicauth_realm, 'Admins');
		assert.deepStrictEqual(body.basicauth_users, {alice: 'secret', bob: 'pw'});
	});
	test('empty users input is dropped so a blank edit keeps existing users', () => {
		let body = {basicauth_enabled: 'true', basicauth_users: '   \n'};
		normalizeHostFeatures(body);
		assert.ok(!('basicauth_users' in body));
	});
	test('object input is sanitized like text input', () => {
		let body = {basicauth_users: {alice: 'secret', 'bad user': 'x', bob: ''}};
		normalizeHostFeatures(body);
		assert.deepStrictEqual(body.basicauth_users, {alice: 'secret'});
	});
});

describe('SSO allow-lists (#57)', () => {
	test('parseAllowList splits on commas/whitespace/newlines and dedupes', () => {
		assert.deepStrictEqual(
			parseAllowList('alice@x.com, bob@x.com\ncarol@x.com alice@x.com'),
			['alice@x.com', 'bob@x.com', 'carol@x.com']
		);
		assert.deepStrictEqual(parseAllowList(['a', 'a', ' b ', '']), ['a', 'b']);
		assert.deepStrictEqual(parseAllowList(''), []);
	});
	test('normalizeHostFeatures coerces sso_enabled and parses allow-lists', () => {
		let body = {
			sso_enabled: 'true',
			sso_allow_users: 'alice@x.com\nbob@x.com',
			sso_allow_groups: 'dns-team, admins',
		};
		normalizeHostFeatures(body);
		assert.strictEqual(body.sso_enabled, true);
		assert.deepStrictEqual(body.sso_allow_users, ['alice@x.com', 'bob@x.com']);
		assert.deepStrictEqual(body.sso_allow_groups, ['dns-team', 'admins']);
	});
});
