// extension.test.js — end-to-end tests for the VuurRaaf VS Code extension.
//
// These launch a real VS Code instance with the extension loaded, open a
// demo .vr file, and verify the full editing experience against the live
// `vr lsp` server: language registration, diagnostics, go-to-definition,
// and hover.

const assert = require('assert');
const path = require('path');
const vscode = require('vscode');

const DEMO_URI = vscode.Uri.file(
	path.join(__dirname, '..', 'fixtures', 'workspace', 'demo.vr')
);

async function openDemo() {
	const doc = await vscode.workspace.openTextDocument(DEMO_URI);
	await vscode.window.showTextDocument(doc);
	return doc;
}

async function waitForDiagnostics(timeoutMs) {
	const deadline = Date.now() + timeoutMs;
	while (Date.now() < deadline) {
		const diags = vscode.languages.getDiagnostics(DEMO_URI);
		if (diags.length > 0) {
			return diags;
		}
		await new Promise((r) => setTimeout(r, 500));
	}
	return vscode.languages.getDiagnostics(DEMO_URI);
}

suite('VuurRaaf extension', function () {
	this.timeout(90000);

	test('vuurraaf language (grammar) is registered', async function () {
		const langs = await vscode.languages.getLanguages();
		assert.ok(langs.includes('vuurraaf'), 'vuurraaf language id not registered');
	});

	test('language server publishes compiler diagnostics', async function () {
		await openDemo();
		const diags = await waitForDiagnostics(30000);
		assert.ok(diags.length > 0, 'no diagnostics were published');
		const joined = diags.map((d) => d.message).join('; ');
		assert.ok(joined.includes('unknown_var'), 'expected unknown_var error, got: ' + joined);
		assert.strictEqual(diags[0].severity, vscode.DiagnosticSeverity.Error);
	});

	test('go-to-definition jumps to the function declaration', async function () {
		await openDemo();
		// "helper" in `let y = helper(21)` — line 6, char 12 (0-based)
		const pos = new vscode.Position(5, 12);
		const locs = await vscode.commands.executeCommand(
			'vscode.executeDefinitionProvider',
			DEMO_URI,
			pos
		);
		assert.ok(locs && locs.length > 0, 'no definition returned for helper');
		assert.strictEqual(locs[0].range.start.line, 0, 'helper should resolve to line 1');
	});

	test('hover reports the symbol kind', async function () {
		await openDemo();
		// on the local `y` — line 6, char 5
		const pos = new vscode.Position(5, 5);
		const hovers = await vscode.commands.executeCommand('vscode.executeHoverProvider', DEMO_URI, pos);
		assert.ok(hovers && hovers.length > 0, 'no hover returned for y');
		const text = hovers
			.map((h) => h.contents.map((c) => (typeof c === 'string' ? c : c.value)).join(' '))
			.join(' ');
		assert.ok(text.includes('variable'), 'expected a variable hover, got: ' + text);
	});

	test('go-to-definition resolves stdlib module functions', async function () {
		await openDemo();
		// switch to a file that calls os.exists so the import resolves
		const modUri = vscode.Uri.file(
			path.join(__dirname, '..', 'fixtures', 'workspace', 'module.vr')
		);
		const doc = await vscode.workspace.openTextDocument(modUri);
		await vscode.window.showTextDocument(doc);
		// "exists" in `let a = os.exists("x")` — line 4, char 12 (0-based)
		const pos = new vscode.Position(3, 12);
		// the definition request may race the didOpen notification, so retry
		let locs = [];
		for (let i = 0; i < 20 && locs.length === 0; i++) {
			locs = await vscode.commands.executeCommand(
				'vscode.executeDefinitionProvider',
				modUri,
				pos
			);
			if (locs.length === 0) {
				await new Promise((r) => setTimeout(r, 500));
			}
		}
		assert.ok(locs && locs.length > 0, 'no definition returned for os.exists');
		assert.ok(locs[0].uri.fsPath.endsWith('os.vr'), 'expected lib/os.vr, got ' + locs[0].uri.fsPath);
	});
});
