#!/usr/bin/env node
/**
 * Migrate test files from node:test to vitest.
 *
 * 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. auto-loop.test.ts: replace mock.timers with vi fake timers API
 */

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

const ROOT = join(process.cwd(), "src");

const FILES_WITH_MOCK_FN = new Set([
  "src/resources/extensions/sf/tests/pre-execution-fail-closed.test.ts",
  "src/resources/extensions/sf/tests/pre-execution-pause-wiring.test.ts",
  "src/resources/extensions/sf/tests/post-exec-retry-bypass.test.ts",
  "src/resources/extensions/sf/tests/validate-milestone-stuck-guard.test.ts",
  "src/resources/extensions/sf/tests/claude-import-tui.test.ts",
]);

  const AUTO_LOOP_FILE =
    "/home/mhugo/code/singularity-forge/src/resources/extensions/sf/tests/auto-loop.test.ts";

function collectTestFiles(dir) {
  const results = [];
  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);
    }
  }
  return results;
}

function migrateImport(content, { isAutoLoop, isMockFn }) {
  // Case: import test from "node:test";
  if (isAutoLoop || isMockFn) {
    // These files don't have plain default imports (they have named imports too)
    // but keep the check for safety
  }
  if (!content.includes("from 'node:test'")) {
    // Replace: import test from "node:test";
    content = content.replace(
      /^import test from "node:test";$/gm,
      "import { test } from 'vitest';",
    );
  }

  if (isAutoLoop) {
    // import test, { mock } from "node:test"
    // → import { test, vi } from 'vitest'
    return content.replace(
      /import\s+(test,)?\s*\{\s*([^}]+)\}\s+from\s+"node:test";?/,
      (match, hasDefault, named) => {
        const namedList = named.split(",").map((s) => s.trim()).filter((s) => s !== "mock");
        const vitestNamed = ["vi", ...namedList.filter((s) => s !== "test")].join(", ");
        const defaultImport = hasDefault ? "test, " : "";
        return `import { ${defaultImport}${vitestNamed} } from 'vitest';`;
      },
    );
  } else if (isMockFn) {
    // import { ..., mock, ... } from "node:test"
    // → import { ..., vi, ... } from 'vitest'
    return content.replace(
      /import\s+(test,)?\s*\{\s*([^}]+)\}\s+from\s+"node:test";?/,
      (match, hasDefault, named) => {
        const namedList = named.split(",").map((s) => s.trim()).filter((s) => s !== "mock");
        const vitestNamed = ["vi", ...namedList.filter((s) => s !== "test")].join(", ");
        const defaultImport = hasDefault ? "test, " : "";
        return `import { ${defaultImport}${vitestNamed} } from 'vitest';`;
      },
    );
  } else {
    // Simple case: just swap the source
    return content.replace(
      /import\s+(test,)?\s*\{\s*([^}]+)\}\s+from\s+"node:test";?/,
      (match, hasDefault, named) => {
        const defaultImport = hasDefault ? "test, " : "";
        return `import { ${defaultImport}${named.trim()} } from 'vitest';`;
      },
    );
  }
}

const files = collectTestFiles(ROOT);
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"')) continue;

    let newContent = content;
    const isAutoLoop = file === AUTO_LOOP_FILE;
    const relPath = file.replace(process.cwd() + "/", "");
    const isMockFn = FILES_WITH_MOCK_FN.has(relPath);

    // Step 1: migrate the import
    newContent = migrateImport(newContent, { isAutoLoop, isMockFn });

    // Step 2: migrate mock.fn → vi.fn (for mock-fn files and auto-loop)
    if (isAutoLoop || isMockFn) {
      newContent = newContent.replace(/\bmock\.fn\b/g, "vi.fn");
      newContent = newContent.replace(/ReturnType<typeof mock\.fn>/g, "ReturnType<typeof vi.fn>");
    }

    // Step 3: migrate mock.timers (auto-loop only)
    if (isAutoLoop) {
      // mock.timers.enable() → vi.useFakeTimers()
      newContent = newContent.replace(/\bmock\.timers\.enable\(\)/g, "vi.useFakeTimers()");
      // mock.timers.tick(ms) → vi.advanceTimersByTime(ms)
      newContent = newContent.replace(/\bmock\.timers\.tick\(([^)]+)\)/g, "vi.advanceTimersByTime($1)");
      // mock.timers.reset() → vi.useRealTimers()
      newContent = newContent.replace(/\bmock\.timers\.reset\(\)/g, "vi.useRealTimers()");
    }

    if (newContent !== content) {
      writeFileSync(file, newContent, "utf-8");
      updated++;
      const rel = file.replace(process.cwd() + "/", "");
      if (isAutoLoop || isMockFn) {
        console.log(`  Migrated (mock): ${rel}`);
      }
    }
  } catch (err) {
    console.error(`Error: ${file}: ${err.message}`);
    errors++;
  }
}

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