import { badgeAwareWordJump, leadingBadgeEdgeOffset } from '$lib/utils';
import { describe, expect, it } from 'vitest';

// Layout of `hello [docs](file:///a/b) world foo`:
//   "hello" 0-4, " " 5, badge 6-24 (length 19), " " 25, "world" 26-30, " " 31, "foo" 32-34
const BADGE = '[docs](file:///a/b)';
const SOURCE = `hello ${BADGE} world foo`;
const BADGE_START = 6;
const BADGE_END = 25;

describe('badgeAwareWordJump', () => {
	it('returns null when the buffer has no badge', () => {
		expect(badgeAwareWordJump('hello world', 0, 'forward')).toBeNull();
		expect(badgeAwareWordJump('hello world', 11, 'backward')).toBeNull();
	});

	it('jumps forward onto a badge landing at its end, not the next word', () => {
		expect(badgeAwareWordJump(SOURCE, BADGE_START, 'forward')).toBe(BADGE_END);
	});

	it('jumps forward from the space before a badge landing at its end', () => {
		expect(badgeAwareWordJump(SOURCE, BADGE_START - 1, 'forward')).toBe(BADGE_END);
	});

	it('jumps backward over a badge landing at its start', () => {
		expect(badgeAwareWordJump(SOURCE, BADGE_END, 'backward')).toBe(BADGE_START);
	});

	it('jumps backward from the next word onto the badge start', () => {
		// caret at the start of "world"
		expect(badgeAwareWordJump(SOURCE, BADGE_END + 1, 'backward')).toBe(BADGE_START);
	});

	it('returns null for jumps that cross no badge', () => {
		// forward over "hello" only
		expect(badgeAwareWordJump(SOURCE, 0, 'forward')).toBeNull();
		// backward over "foo" only
		expect(badgeAwareWordJump(SOURCE, SOURCE.length, 'backward')).toBeNull();
		// backward away from the badge (over "hello")
		expect(badgeAwareWordJump(SOURCE, BADGE_START, 'backward')).toBeNull();
	});

	it('treats a leading badge as one word in both directions', () => {
		const source = `${BADGE} rest`;

		expect(badgeAwareWordJump(source, 0, 'forward')).toBe(BADGE.length);
		expect(badgeAwareWordJump(source, BADGE.length, 'backward')).toBe(0);
	});

	it('treats adjacent badges as separate words', () => {
		// each badge is 14 chars: "[a](file:///x)" / "[b](file:///y)"
		const source = '[a](file:///x)[b](file:///y)';

		expect(badgeAwareWordJump(source, 0, 'forward')).toBe(14);
		expect(badgeAwareWordJump(source, 14, 'forward')).toBe(28);
		expect(badgeAwareWordJump(source, 28, 'backward')).toBe(14);
		expect(badgeAwareWordJump(source, 14, 'backward')).toBe(0);
	});

	it('jumps over a badge following punctuation', () => {
		// "foo," 0-3, " " 4, badge 5-23 (end 24), " bar" 24-27
		const source = `foo, ${BADGE} bar`;

		expect(badgeAwareWordJump(source, 0, 'forward')).toBeNull();
		expect(badgeAwareWordJump(source, 3, 'forward')).toBe(24);
	});
});

describe('leadingBadgeEdgeOffset', () => {
	it('returns 0 when the caret sits exactly at a leading badge end', () => {
		expect(leadingBadgeEdgeOffset(`${BADGE} rest`, BADGE.length)).toBe(0);
	});

	it('returns null when the caret is anywhere else', () => {
		expect(leadingBadgeEdgeOffset(`${BADGE} rest`, 0)).toBeNull();
		expect(leadingBadgeEdgeOffset(`${BADGE} rest`, BADGE.length + 2)).toBeNull();
	});

	it('returns null when the buffer does not start with a badge', () => {
		expect(leadingBadgeEdgeOffset(SOURCE, BADGE_END)).toBeNull();
		expect(leadingBadgeEdgeOffset('', 0)).toBeNull();
	});
});
