import * as assert from 'uvu/assert';
import { suite, type Test } from 'uvu';
import { join, resolve } from 'node:path';

import * as walk from 'empathic/walk';

type Builder = (it: Test) => unknown;
function describe(name: string, builder: Builder) {
	let it = suite(name);
	builder(it);
	it.run();
}

describe('walk.up', (it) => {
	const fixtures = resolve('fixtures');

	it('should be a function', () => {
		assert.type(walk.up, 'function');
	});

	let parents = walk.up(
		join(fixtures, 'a/b/c'),
	);

	it('should return an Array of parent directories', () => {
		assert.instance(parents, Array);
	});

	it('should resolve input from CWD if not absolute', () => {
		let output = walk.up('fixtures/a/b/c');
		assert.equal(output, parents);
	});

	it('should start with the initial directory', () => {
		assert.is(parents[0], resolve('fixtures/a/b/c'));
	});

	it('should return all parents until "/" root (default)', () => {
		assert.is(parents[0], resolve('fixtures/a/b/c'));
		assert.is(parents[1], resolve('fixtures/a/b'));
		assert.is(parents[2], resolve('fixtures/a'));
		assert.is(parents[3], resolve('fixtures'));
		assert.is(parents[4], resolve('.'));
		// total chain length unknown
		assert.is(parents[parents.length - 1], resolve('/'));
	});

	it('should resolve from `options.cwd` if input is not absolute', () => {
		let output = walk.up('a/b/c', {
			cwd: fixtures,
		});

		assert.equal(output, parents);
	});

	it('should stop after `options.last` directory', () => {
		let output = walk.up('fixtures/a/b/c', {
			last: fixtures,
		});

		assert.ok(parents.length > output.length);
	});

	it('should include `options.last` directory', () => {
		let output = walk.up('fixtures/a/b/c', {
			last: fixtures,
		});

		assert.is(output[output.length - 1], fixtures);
	});

	it('should only have 1 entry if started at `options.last` directory', () => {
		let start = resolve('fixtures/a/b/c');
		let output = walk.up(start, { last: start });
		assert.is(output.length, 1);
	});

	// find-up/locate-paths cycle in infinite loop
	it('should still exit at root if `options.last` is a subdir of start', () => {
		let start = resolve('fixtures/a/b/c');
		let last = join(start, 'd/e/f');

		let output = walk.up(start, { last });

		assert.is(output[0], resolve('fixtures/a/b/c'));
		assert.is(output[1], resolve('fixtures/a/b'));
		assert.is(output[2], resolve('fixtures/a'));
		assert.is(output[output.length - 1], resolve('/'));

		assert.equal(output, parents);
	});
});
