#!/usr/bin/env node
/**
 * Migrate ALL test files from node:test to vitest.
 *
 * Scans src/, packages/, web/, and scripts/.
 * Changes:
 * 1. Replace `from "node:test"` → `from 'vitest'` in all imports
 * 2. Files using mock.fn(): replace `mock.fn` → `vi.fn` and add `vi` to imports
 * 3. Files using mock.timers: migrate to vi fake timers API
 */

import { readdirSync, readFileSync, writeFileSync } from "node:fs";
import { join } from "node:path";

const ROOTS = [
	join(process.cwd(), "src"),
	join(process.cwd(), "packages"),
	join(process.cwd(), "web"),
	join(process.cwd(), "scripts"),
];

function collectTestFiles(dirs) {
	const results = [];
	for (const dir of dirs) {
		try {
			const entries = readdirSync(dir, { withFileTypes: true });
			for (const entry of entries) {
				const full = join(dir, entry.name);
				if (entry.isDirectory()) {
					results.push(...collectTestFiles([full]));
				} else if (
					(entry.name.endsWith(".test.ts") ||
						entry.name.endsWith(".test.mjs")) &&
					!entry.name.endsWith(".d.ts")
				) {
					results.push(full);
				}
			}
		} catch {
			// Directory may not exist
		}
	}
	return results;
}

function migrateImport(content, { hasMockFn, hasMockTimers }) {
	// Case 1: import test from "node:test"; (or single quotes, optional semicolon)
	content = content.replace(
		/^import test from ["']node:test["'];?$/gm,
		"import { test } from 'vitest';",
	);

	// Case 2: import { ... } from "node:test" (and variants with default import)
	content = content.replace(
		/import\s+(test,)?\s*\{\s*([^}]+)\}\s+from\s+["']node:test["'];?/g,
		(_match, hasDefault, named) => {
			const namedList = named
				.split(",")
				.map((s) => s.trim())
				.filter((s) => s !== "mock" && s !== "");

			const extra = [];
			if (hasMockFn || hasMockTimers) {
				extra.push("vi");
			}

			const allNamed = [...extra, ...namedList.filter((s) => s !== "test")];
			const defaultImport = hasDefault ? "test, " : "";
			const namedStr = allNamed.join(", ");
			return `import { ${defaultImport}${namedStr} } from 'vitest';`;
		},
	);

	return content;
}

const files = collectTestFiles(ROOTS);
console.log(`Found ${files.length} test files`);

let updated = 0;
let errors = 0;

for (const file of files) {
	try {
		const content = readFileSync(file, "utf-8");
		if (
			!content.includes('from "node:test"') &&
			!content.includes("from 'node:test'")
		)
			continue;

		const hasMockFn = content.includes("mock.fn");
		const hasMockTimers = content.includes("mock.timers");

		let newContent = migrateImport(content, { hasMockFn, hasMockTimers });

		// Migrate mock.fn → vi.fn
		if (hasMockFn) {
			newContent = newContent.replace(/\bmock\.fn\b/g, "vi.fn");
			newContent = newContent.replace(
				/ReturnType<typeof mock\.fn>/g,
				"ReturnType<typeof vi.fn>",
			);
		}

		// Migrate mock.timers → vi fake timers
		if (hasMockTimers) {
			newContent = newContent.replace(
				/\bmock\.timers\.enable\(\)/g,
				"vi.useFakeTimers()",
			);
			newContent = newContent.replace(
				/\bmock\.timers\.tick\(([^)]+)\)/g,
				"vi.advanceTimersByTime($1)",
			);
			newContent = newContent.replace(
				/\bmock\.timers\.reset\(\)/g,
				"vi.useRealTimers()",
			);
		}

		if (newContent !== content) {
			writeFileSync(file, newContent, "utf-8");
			updated++;
			const rel = file.replace(process.cwd() + "/", "");
			const tags = [];
			if (hasMockFn) tags.push("mock");
			if (hasMockTimers) tags.push("timers");
			const tagStr = tags.length ? ` (${tags.join(", ")})` : "";
			console.log(`  Migrated${tagStr}: ${rel}`);
		}
	} catch (err) {
		console.error(`Error: ${file}: ${err.message}`);
		errors++;
	}
}

console.log(`Updated ${updated} files, ${errors} errors`);
