import { containsCodeSpan, isOffsetInCodeBlock, tokenizeContent } from '$lib/utils';
import { describe, expect, it } from 'vitest';

describe('tokenizeContent', () => {
	it('tokenizes a plain text buffer with no badges', () => {
		expect(tokenizeContent('hello world')).toEqual([{ kind: 'text', text: 'hello world' }]);
	});

	it('tokenizes a single badge', () => {
		expect(tokenizeContent('[docs](file:///a/b)')).toEqual([
			{ kind: 'badge', name: 'docs', path: '/a/b' }
		]);
	});

	it('tokenizes text around a single badge', () => {
		expect(tokenizeContent('hello [docs](file:///a/b) world')).toEqual([
			{ kind: 'text', text: 'hello ' },
			{ kind: 'badge', name: 'docs', path: '/a/b' },
			{ kind: 'text', text: ' world' }
		]);
	});

	it('tokenizes adjacent badges as separate tokens', () => {
		expect(tokenizeContent('[a](file:///x)[b](file:///y)')).toEqual([
			{ kind: 'badge', name: 'a', path: '/x' },
			{ kind: 'badge', name: 'b', path: '/y' }
		]);
	});

	it('leaves non-file links untouched in the stream', () => {
		expect(tokenizeContent('see [foo](https://example.com) for details')).toEqual([
			{ kind: 'text', text: 'see [foo](https://example.com) for details' }
		]);
	});

	it('recognizes badges whose path contains spaces (macOS screenshots)', () => {
		const path = '/Users/allozaur/Desktop/Screenshot 2026-07-28 at 17.21.50.png';
		const source = `[Screenshot 2026-07-28 at 17.21.50.png](file://${path}) `;

		expect(tokenizeContent(source)).toEqual([
			{ kind: 'badge', name: 'Screenshot 2026-07-28 at 17.21.50.png', path },
			{ kind: 'text', text: ' ' }
		]);
	});

	it('recognizes badges whose path lives in the macOS temp folder', () => {
		const path =
			'/var/folders/78/j28m7pn57wb34bfjwlskh62h0000gn/T/TemporaryItems/NSIRD_screencaptureui_GD0A2R/Screenshot 2026-07-28 at 17.23.28.png';
		const source = `[Screenshot 2026-07-28 at 17.23.28.png](file://${path}) `;

		expect(tokenizeContent(source)).toEqual([
			{ kind: 'badge', name: 'Screenshot 2026-07-28 at 17.23.28.png', path },
			{ kind: 'text', text: ' ' }
		]);
	});

	it('keeps text around a badge with spaces in the path', () => {
		const path = '/Users/allozaur/Desktop/Screenshot 2026-07-28 at 17.21.50.png';
		const source = `see [Screenshot 2026-07-28 at 17.21.50.png](file://${path}) done`;

		expect(tokenizeContent(source)).toEqual([
			{ kind: 'text', text: 'see ' },
			{ kind: 'badge', name: 'Screenshot 2026-07-28 at 17.21.50.png', path },
			{ kind: 'text', text: ' done' }
		]);
	});

	it('recognizes badges whose path contains a close parenthesis (macOS duplicate files)', () => {
		const path = '/Users/foo/Screenshot (1).png';
		const source = `[Screenshot (1).png](file://${path}) `;

		expect(tokenizeContent(source)).toEqual([
			{ kind: 'badge', name: 'Screenshot (1).png', path },
			{ kind: 'text', text: ' ' }
		]);
	});

	it('recognizes badges whose folder name is wrapped in parentheses', () => {
		const path = '/Users/foo/Project (Stuff)/main.rs';
		const source = `[main.rs](file://${path}) `;

		expect(tokenizeContent(source)).toEqual([
			{ kind: 'badge', name: 'main.rs', path },
			{ kind: 'text', text: ' ' }
		]);
	});

	it('recognizes adjacent badges back-to-back with no separator', () => {
		const source = '[a](file:///p)[b](file:///q)';

		expect(tokenizeContent(source)).toEqual([
			{ kind: 'badge', name: 'a', path: '/p' },
			{ kind: 'badge', name: 'b', path: '/q' }
		]);
	});

	it('tokenizes inline code with the backticks included', () => {
		expect(tokenizeContent('run `npm test` now')).toEqual([
			{ kind: 'text', text: 'run ' },
			{ kind: 'code_inline', text: '`npm test`' },
			{ kind: 'text', text: ' now' }
		]);
	});

	it('tokenizes a fenced code block without a language', () => {
		const source = 'before\n```\nconst a = 1;\n```\nafter';

		expect(tokenizeContent(source)).toEqual([
			{ kind: 'text', text: 'before\n' },
			{ kind: 'code_block', text: '```\nconst a = 1;\n```' },
			{ kind: 'text', text: '\nafter' }
		]);
	});

	it('tokenizes a fenced code block with a language', () => {
		const source = '```js\nconst a = 1;\n```';

		expect(tokenizeContent(source)).toEqual([
			{ kind: 'code_block', text: '```js\nconst a = 1;\n```' }
		]);
	});

	it('prefers the fenced block over inline spans at triple backticks', () => {
		expect(tokenizeContent('```a``` ```b```')).toEqual([
			{ kind: 'code_block', text: '```a```' },
			{ kind: 'text', text: ' ' },
			{ kind: 'code_block', text: '```b```' }
		]);
	});

	it('leaves an unclosed fence as plain text', () => {
		expect(tokenizeContent('```js\nconst a = 1;')).toEqual([
			{ kind: 'text', text: '```js\nconst a = 1;' }
		]);
	});

	it('leaves an unclosed inline backtick as plain text', () => {
		expect(tokenizeContent('run `npm test')).toEqual([{ kind: 'text', text: 'run `npm test' }]);
	});

	it('does not recognize badges inside code spans', () => {
		expect(tokenizeContent('`[a](file:///p)`')).toEqual([
			{ kind: 'code_inline', text: '`[a](file:///p)`' }
		]);
	});

	it('tokenizes badges and code spans side by side', () => {
		expect(tokenizeContent('[a](file:///p) `x`')).toEqual([
			{ kind: 'badge', name: 'a', path: '/p' },
			{ kind: 'text', text: ' ' },
			{ kind: 'code_inline', text: '`x`' }
		]);
	});
});

describe('containsCodeSpan', () => {
	it('detects inline code', () => {
		expect(containsCodeSpan('run `npm test` now')).toBe(true);
	});

	it('detects a fenced block with a language', () => {
		expect(containsCodeSpan('```js\nconst a = 1;\n```')).toBe(true);
	});

	it('detects a fenced block without a language', () => {
		expect(containsCodeSpan('```\ncode\n```')).toBe(true);
	});

	it('ignores unclosed fences and lone backticks', () => {
		expect(containsCodeSpan('```js\nconst a = 1;')).toBe(false);
		expect(containsCodeSpan('run `npm test')).toBe(false);
		expect(containsCodeSpan('``')).toBe(false);
	});

	it('ignores plain text and mention links', () => {
		expect(containsCodeSpan('hello world')).toBe(false);
		expect(containsCodeSpan('[a](file:///p)')).toBe(false);
	});
});

describe('isOffsetInCodeBlock', () => {
	const BLOCK = '```js\nconst a = 1;\n```';

	it('is false with no fences in the buffer', () => {
		expect(isOffsetInCodeBlock('hello world', 5)).toBe(false);
		expect(isOffsetInCodeBlock('run `npm test` now', 10)).toBe(false);
	});

	it('is true right after the opening fence, before any content', () => {
		expect(isOffsetInCodeBlock('```', 3)).toBe(true);
		expect(isOffsetInCodeBlock('```js', 5)).toBe(true);
	});

	it('is true inside a still-open block while it is being typed', () => {
		const open = '```js\nconst a = 1;';

		expect(isOffsetInCodeBlock(open, open.length)).toBe(true);
	});

	it('is true inside a closed block and false outside it', () => {
		expect(isOffsetInCodeBlock(BLOCK, 6)).toBe(true);
		expect(isOffsetInCodeBlock(BLOCK, 0)).toBe(false);
		expect(isOffsetInCodeBlock(BLOCK, BLOCK.length)).toBe(false);
		expect(isOffsetInCodeBlock(BLOCK + '\nafter', BLOCK.length + 5)).toBe(false);
	});

	it('toggles per fence across multiple blocks', () => {
		const two = BLOCK + '\ntext\n' + BLOCK;
		const secondBlock = two.lastIndexOf(BLOCK);

		expect(isOffsetInCodeBlock(two, secondBlock - 2)).toBe(false);
		expect(isOffsetInCodeBlock(two, secondBlock + 6)).toBe(true);
		expect(isOffsetInCodeBlock(two, two.length)).toBe(false);
	});
});
