// SPDX-License-Identifier: LGPL-3.0-or-later
import { join } from '@zenfs/core/path';
import type { Exception } from 'kerium';
import assert from 'node:assert/strict';
import { suite, test } from 'node:test';
import { fs } from '../common.js';

suite('Links', () => {
	const target = '/a1.js',
		symlink = 'symlink1.js',
		hardlink = 'link1.js';

	test('symlink', async () => {
		await fs.promises.symlink(target, symlink);
	});

	test('lstat', async () => {
		const stats = await fs.promises.lstat(symlink);
		assert(stats.isSymbolicLink());
	});

	test('lstat file inside symlinked directory #241', async () => {
		await fs.promises.mkdir('/a');
		await fs.promises.writeFile('/a/hello.txt', 'hello world');
		await fs.promises.symlink('/a', '/b');

		const stat = await fs.promises.lstat('/b/hello.txt');
		assert(stat.isFile());
	});

	test('readlink', async () => {
		const destination = await fs.promises.readlink(symlink);
		assert.equal(destination, target);
		assert.throws(() => fs.readlinkSync(destination));
	});

	test('read target contents', async () => {
		assert.equal(await fs.promises.readFile(target, 'utf-8'), await fs.promises.readFile(symlink, 'utf-8'));
	});

	test('nested symlinks', async () => {
		// Create the real directory structure
		const realDir = '/real-dir';
		const realFile = '/real-dir/realfile.txt';
		const fileContent = 'hello world';
		await fs.promises.mkdir(realDir);
		await fs.promises.writeFile(realFile, fileContent);
		// Create first symlink (symlink-dir -> real-dir)
		const symlinkDir = '/symlink-dir';
		await fs.promises.symlink(realDir, symlinkDir);
		const symfile = 'symfile.txt';
		const symlinkFile = join(realDir, symfile);
		// Create second symlink (symlink-dir -> real-dir)
		await fs.promises.symlink(realFile, symlinkFile);
		// Now access file through nested symlinks
		const nestedPath = join(symlinkDir, symfile);
		// Verify realpath resolution
		const resolvedPath = await fs.promises.realpath(nestedPath);
		assert.equal(resolvedPath, realFile);
		// Verify content can be read through nested symlinks
		const content = await fs.promises.readFile(nestedPath, 'utf8');
		assert.notEqual(content, '/real-dir/realfile.txt');
		assert.equal(content, fileContent);
	});

	test('unlink', async () => {
		await fs.promises.unlink(symlink);
		assert(!(await fs.promises.exists(symlink)));
		assert(await fs.promises.exists(target));
	});

	test('link', async t => {
		const _ = await fs.promises.link(target, hardlink).catch((e: Exception) => {
			if (e.code == 'ENOSYS') return e;
			throw e;
		});
		if (_) {
			return t.skip('Backend does not support hard links');
		}
		const targetContent = await fs.promises.readFile(target, 'utf8');
		const linkContent = await fs.promises.readFile(hardlink, 'utf8');
		assert.equal(targetContent, linkContent);
	});

	test('file inside symlinked directory', async () => {
		await fs.promises.symlink('.', 'link');
		const targetContent = await fs.promises.readFile(target, 'utf8');
		const link = join('link', target);
		assert((await fs.promises.realpath(link)) === target);
		const linkContent = await fs.promises.readFile(link, 'utf8');
		assert.equal(targetContent, linkContent);
	});
});
