import assert from "node:assert/strict";
import { execSync } from "node:child_process";
import {
	existsSync,
	lstatSync,
	mkdirSync,
	mkdtempSync,
	realpathSync,
	rmSync,
	writeFileSync,
} from "node:fs";
import { tmpdir } from "node:os";
import { join } from "node:path";
import { afterEach, beforeEach, describe, test } from 'vitest';

import { repairMissingSfSymlinkForHeadless } from "../../../../headless.ts";
import { externalSfRoot } from "../repo-identity.ts";

function run(command: string, cwd: string): string {
	return execSync(command, {
		cwd,
		stdio: ["ignore", "pipe", "pipe"],
		encoding: "utf-8",
	}).trim();
}

describe("headless project repair", () => {
	let base: string;
	let stateDir: string;
	let previousStateDir: string | undefined;

	beforeEach(() => {
		base = realpathSync(mkdtempSync(join(tmpdir(), "sf-headless-repair-")));
		stateDir = realpathSync(mkdtempSync(join(tmpdir(), "sf-state-")));
		previousStateDir = process.env.SF_STATE_DIR;
		process.env.SF_STATE_DIR = stateDir;

		run("git init -b main", base);
		run('git config user.name "SF Test"', base);
		run('git config user.email "sf@example.com"', base);
		run("git remote add origin git@github.com:example/repaired.git", base);
		writeFileSync(join(base, "README.md"), "# Test Repo\n", "utf-8");
		run("git add README.md", base);
		run('git commit -m "chore: init"', base);
	});

	afterEach(() => {
		if (previousStateDir === undefined) delete process.env.SF_STATE_DIR;
		else process.env.SF_STATE_DIR = previousStateDir;
		rmSync(base, { recursive: true, force: true });
		rmSync(stateDir, { recursive: true, force: true });
	});

	test("re-links .sf when matching external project state already exists", () => {
		const externalPath = externalSfRoot(base);
		mkdirSync(join(externalPath, "milestones"), { recursive: true });

		const repairedPath = repairMissingSfSymlinkForHeadless(base);

		assert.equal(repairedPath, externalPath);
		assert.ok(existsSync(join(base, ".sf")), ".sf exists after repair");
		assert.ok(lstatSync(join(base, ".sf")).isSymbolicLink(), ".sf is a symlink");
		assert.equal(realpathSync(join(base, ".sf")), realpathSync(externalPath));
	});

	test("does not initialize a blank project for read-only headless commands", () => {
		const repairedPath = repairMissingSfSymlinkForHeadless(base);

		assert.equal(repairedPath, null);
		assert.equal(existsSync(join(base, ".sf")), false);
	});
});
