import { describe, expect, it } from 'vitest';
import {
	splitPathQuery,
	buildCaseInsensitiveGlob,
	rankEntries,
	joinPath,
	highlightMatch
} from '$lib/utils';

describe('splitPathQuery', () => {
	it('treats a plain query as a home-relative glob (not navigation)', () => {
		expect(splitPathQuery('docs')).toBeNull();
	});

	it('navigates the root for `/`', () => {
		expect(splitPathQuery('/')).toEqual({ parent: '/', last: '' });
	});

	it('navigates home for `~`', () => {
		expect(splitPathQuery('~')).toEqual({ parent: '~', last: '' });
	});

	it('splits an absolute path into parent and last segment', () => {
		expect(splitPathQuery('/Users/al/proj')).toEqual({ parent: '/Users/al', last: 'proj' });
	});

	it('navigates a Windows drive path written with backslashes', () => {
		expect(splitPathQuery('C:\\repos\\llama.cpp')).toEqual({
			parent: 'C:/repos',
			last: 'llama.cpp'
		});
	});

	it('navigates a Windows drive path written with forward slashes', () => {
		expect(splitPathQuery('D:/repos')).toEqual({ parent: 'D:/', last: 'repos' });
	});

	it('treats a bare drive as its root', () => {
		expect(splitPathQuery('D:')).toEqual({ parent: 'D:/', last: '' });
		expect(splitPathQuery('D:\\')).toEqual({ parent: 'D:/', last: '' });
	});

	it('navigates a UNC share', () => {
		expect(splitPathQuery('\\\\host\\share\\proj')).toEqual({
			parent: '//host/share/',
			last: 'proj'
		});
	});

	it('keeps a backslash as a POSIX filename character', () => {
		expect(splitPathQuery('/tmp/a\\b')).toEqual({ parent: '/tmp', last: 'a\\b' });
	});

	it('splits a home-relative path into parent and last segment', () => {
		expect(splitPathQuery('~/Documents')).toEqual({ parent: '~', last: 'Documents' });
	});

	it('strips trailing slashes before splitting', () => {
		expect(splitPathQuery('/Users/al/')).toEqual({ parent: '/Users', last: 'al' });
	});

	it('handles a single-segment absolute path', () => {
		expect(splitPathQuery('/opt')).toEqual({ parent: '/', last: 'opt' });
	});
});

describe('buildCaseInsensitiveGlob', () => {
	it('wraps letters in case-insensitive character classes', () => {
		expect(buildCaseInsensitiveGlob('ab')).toBe('*[aA][bB]*');
	});

	it('escapes glob metacharacters into literal fragments', () => {
		expect(buildCaseInsensitiveGlob('a*b')).toBe('*[aA][*][bB]*');
	});
});

describe('rankEntries', () => {
	const entries = [
		{ path: '/h/README', type: 'dir' },
		{ path: '/h/read', type: 'dir' },
		{ path: '/h/readme.txt', type: 'dir' }
	];

	it('ranks exact basename match first', () => {
		const ranked = rankEntries(entries, 'read');
		expect(ranked[0].path).toBe('/h/read');
	});

	it('breaks ties by shorter path, then alphabetically', () => {
		const ranked = rankEntries(entries, 'read');
		expect(ranked[ranked.length - 1].path).toBe('/h/readme.txt');
	});

	it('does not mutate the input', () => {
		const snapshot = [...entries];
		rankEntries(entries, 'read');
		expect(entries).toEqual(snapshot);
	});
});

describe('joinPath', () => {
	it('joins base and relative avoiding a double slash', () => {
		expect(joinPath('/home/al/', 'docs')).toBe('/home/al/docs');
	});

	it('returns the relative path when base is empty', () => {
		expect(joinPath('', 'docs')).toBe('docs');
	});
});

describe('highlightMatch', () => {
	it('returns a single non-matching segment when query is empty', () => {
		expect(highlightMatch('abc', '')).toEqual([{ text: 'abc', match: false }]);
	});

	it('marks every case-insensitive occurrence of the query', () => {
		expect(highlightMatch('aXa', 'ax')).toEqual([
			{ text: 'aX', match: true },
			{ text: 'a', match: false }
		]);
	});

	it('returns non-matching text when the query is absent', () => {
		expect(highlightMatch('abc', 'z')).toEqual([{ text: 'abc', match: false }]);
	});
});
