import assert from "node:assert/strict";
import {
	existsSync,
	mkdirSync,
	mkdtempSync,
	rmSync,
	writeFileSync,
} from "node:fs";
import { tmpdir } from "node:os";
import { join } from "node:path";
import { afterEach, describe, test } from "vitest";
import {
	applyEnvironmentFixes,
	environmentResultsToDoctorIssues,
	runEnvironmentChecks,
} from "../doctor-environment.js";

const tmpDirs = [];

function makeProject() {
	const dir = mkdtempSync(join(tmpdir(), "sf-env-fix-"));
	tmpDirs.push(dir);
	writeFileSync(
		join(dir, "package.json"),
		JSON.stringify({ scripts: {} }, null, 2),
	);
	writeFileSync(
		join(dir, "package-lock.json"),
		JSON.stringify({ lockfileVersion: 3 }, null, 2),
	);
	return dir;
}

afterEach(() => {
	while (tmpDirs.length > 0) {
		const dir = tmpDirs.pop();
		if (dir) rmSync(dir, { recursive: true, force: true });
	}
});

describe("doctor environment dependency fixes", () => {
	test("environmentResultsToDoctorIssues_when_node_modules_missing_marks_dependencies_fixable", () => {
		const project = makeProject();
		const issues = environmentResultsToDoctorIssues(
			runEnvironmentChecks(project),
		);
		const deps = issues.find((issue) => issue.code === "env_dependencies");

		assert.equal(deps?.fixable, true);
		assert.match(deps?.message ?? "", /node_modules missing/);
	});

	test("applyEnvironmentFixes_when_fix_enabled_runs_detected_package_manager", () => {
		const project = makeProject();
		const fakeBin = mkdtempSync(join(tmpdir(), "sf-env-bin-"));
		tmpDirs.push(fakeBin);
		writeFileSync(
			join(fakeBin, "npm"),
			"#!/usr/bin/env sh\nmkdir -p node_modules\nprintf '{}\\n' > node_modules/.package-lock.json\n",
			{ mode: 0o755 },
		);

		const results = runEnvironmentChecks(project);
		const fixesApplied = [];
		const originalPath = process.env.PATH;
		process.env.PATH = `${fakeBin}:${originalPath ?? ""}`;
		try {
			const fixed = applyEnvironmentFixes(project, results, {
				shouldFix: (code) => code === "env_dependencies",
				fixesApplied,
			});

			assert.equal(fixed, true);
			assert.equal(
				existsSync(join(project, "node_modules", ".package-lock.json")),
				true,
			);
			assert.deepEqual(fixesApplied, [
				"dependencies: ran npm install --package-lock=false",
			]);
		} finally {
			process.env.PATH = originalPath;
		}
	});

	test("applyEnvironmentFixes_when_fix_disabled_does_not_install", () => {
		const project = makeProject();
		mkdirSync(join(project, "node_modules"), { recursive: true });
		rmSync(join(project, "node_modules"), { recursive: true, force: true });
		const results = runEnvironmentChecks(project);
		const fixesApplied = [];

		const fixed = applyEnvironmentFixes(project, results, {
			shouldFix: () => false,
			fixesApplied,
		});

		assert.equal(fixed, false);
		assert.deepEqual(fixesApplied, []);
		assert.equal(existsSync(join(project, "node_modules")), false);
	});
});
