// SPDX-License-Identifier: LGPL-3.0-or-later
import { Buffer } from 'buffer';
import assert from 'node:assert/strict';
import { suite, test } from 'node:test';
import { fs } from '../common.js';

suite('Extended Attributes', () => {
	const testFile = 'xattr-test.txt';
	const testValue = 'test value';
	const testName = 'user.test';

	test.before(() => fs.promises.writeFile(testFile, 'test content'));
	test.after(() => fs.promises.unlink(testFile));

	test('Non-user attribute set fails', async () => {
		await assert.rejects(fs.xattr.set(testFile, 'system.test', 'value'), { code: 'ENOTSUP' });
	});

	test('set and get attributes', async () => {
		await fs.xattr.set(testFile, testName, testValue);
		const value = await fs.xattr.get(testFile, testName, { encoding: 'utf8' });
		assert.equal(value, testValue);
	});

	test('get attributes with buffer encoding', async () => {
		await fs.xattr.set(testFile, 'user.buffer', 'buffer value');
		const buffer = await fs.xattr.get(testFile, 'user.buffer', { encoding: 'buffer' });
		assert(buffer instanceof Uint8Array);
		assert.equal(Buffer.from(buffer).toString(), 'buffer value');
	});

	test('remove attributes', async () => {
		await fs.xattr.set(testFile, 'user.to-remove', testValue);
		await fs.xattr.remove(testFile, 'user.to-remove');

		await assert.rejects(fs.xattr.get(testFile, 'user.to-remove', { encoding: 'utf8' }), { code: 'ENODATA' });
	});

	test('list attributes', async () => {
		await fs.xattr.set(testFile, 'user.list1', 'value1');
		await fs.xattr.set(testFile, 'user.list2', 'value2');

		const attrs = await fs.xattr.list(testFile);
		assert(attrs.includes('user.list1'));
		assert(attrs.includes('user.list2'));
	});

	test('handle create and replace options', async () => {
		const flagTestName = 'user.flag-test';

		await fs.xattr.set(testFile, flagTestName, 'original', { create: true });

		await assert.rejects(fs.xattr.set(testFile, flagTestName, 'new value', { create: true }), { code: 'EEXIST' });

		await fs.xattr.set(testFile, flagTestName, 'updated', { replace: true });
		const value = await fs.xattr.get(testFile, flagTestName, { encoding: 'utf8' });
		assert.equal(value, 'updated');

		await assert.rejects(fs.xattr.set(testFile, 'user.nonexistent', 'value', { replace: true }), { code: 'ENODATA' });
	});

	test('file must exist', async () => {
		await assert.rejects(fs.xattr.set('nonexistent-file.txt', testName, 'value'), { code: 'ENOENT' });
	});

	test('synchronous operations', () => {
		const syncAttrName = 'user.sync-test';

		fs.xattr.setSync(testFile, syncAttrName, testValue);
		const value = fs.xattr.getSync(testFile, syncAttrName, { encoding: 'utf8' });
		assert.equal(value, testValue);

		fs.xattr.removeSync(testFile, syncAttrName);

		assert.throws(() => fs.xattr.getSync(testFile, syncAttrName, { encoding: 'utf8' }), { code: 'ENODATA' });

		const syncList1 = 'user.sync-list1';
		const syncList2 = 'user.sync-list2';
		fs.xattr.setSync(testFile, syncList1, 'value1');
		fs.xattr.setSync(testFile, syncList2, 'value2');

		const attrs = fs.xattr.listSync(testFile);
		assert(attrs.includes(syncList1));
		assert(attrs.includes(syncList2));
	});
});
