import assert from "node:assert/strict";
import * as fs from "node:fs";
import { createRequire } from "node:module";
import * as os from "node:os";
import * as path from "node:path";
import { fileURLToPath } from "node:url";
import { describe, test } from "vitest";

const __dirname = path.dirname(fileURLToPath(import.meta.url));
const require = createRequire(import.meta.url);

const addonDir = path.resolve(
	__dirname,
	"..",
	"..",
	"..",
	"..",
	"rust-engine",
	"addon",
);
const platformTag = `${process.platform}-${process.arch}`;
const candidates = [
	path.join(addonDir, "forge_engine.dev.node"),
	path.join(addonDir, `forge_engine.${platformTag}.node`),
];

let native;
for (const candidate of candidates) {
	try {
		native = require(candidate);
		break;
	} catch {
		// try next
	}
}

if (!native) {
	console.error(
		"Native addon not found. Run `npm run build:native -w @singularity-forge/native` first.",
	);
	process.exit(1);
}

function tempFile(contents) {
	const dir = fs.mkdtempSync(path.join(os.tmpdir(), "sf-edit-test-"));
	const file = path.join(dir, "sample.txt");
	fs.writeFileSync(file, contents, "utf8");
	return { dir, file };
}

const range = (startLine, startCharacter, endLine, endCharacter) => ({
	start: { line: startLine, character: startCharacter },
	end: { line: endLine, character: endCharacter },
});

describe("native edit: applyEdits()", () => {
	test("applies LSP-style edits atomically", ({ onTestFinished }) => {
		const { dir, file } = tempFile("hello world\n");
		onTestFinished(() => fs.rmSync(dir, { recursive: true, force: true }));

		const result = native.applyEdits(
			file,
			[{ range: range(0, 6, 0, 11), newText: "forge" }],
			{ fsync: false },
		);

		assert.deepEqual(result, {
			editsApplied: 1,
			bytesWritten: Buffer.byteLength("hello forge\n"),
		});
		assert.equal(fs.readFileSync(file, "utf8"), "hello forge\n");
	});

	test("uses UTF-16 character offsets", ({ onTestFinished }) => {
		const { dir, file } = tempFile("a😀b\n");
		onTestFinished(() => fs.rmSync(dir, { recursive: true, force: true }));

		native.applyEdits(file, [{ range: range(0, 1, 0, 3), newText: "X" }], {
			fsync: false,
		});

		assert.equal(fs.readFileSync(file, "utf8"), "aXb\n");
	});

	test("rejects overlapping edits without changing the file", ({
		onTestFinished,
	}) => {
		const { dir, file } = tempFile("abcdef\n");
		onTestFinished(() => fs.rmSync(dir, { recursive: true, force: true }));

		assert.throws(
			() =>
				native.applyEdits(
					file,
					[
						{ range: range(0, 0, 0, 4), newText: "x" },
						{ range: range(0, 2, 0, 6), newText: "y" },
					],
					{ fsync: false },
				),
			/overlapping edits/,
		);
		assert.equal(fs.readFileSync(file, "utf8"), "abcdef\n");
	});
});
