import crypto from "crypto";
import fs from "fs";
import path from "path";
import { build } from "esbuild";
import { fileURLToPath, pathToFileURL } from "url";

const PACKAGE_ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..", "..");
const ROOT_ENTRY = path.join(PACKAGE_ROOT, "lib", "index.mjs");
const SETUP_ENTRY = path.join(PACKAGE_ROOT, "lib", "setup", "index.mjs");
const RUNTIME_ENTRY = path.join(PACKAGE_ROOT, "lib", "runtime", "index.mjs");
const SETUP_FILES = [
  "testkit.setup.ts",
  "testkit.setup.mts",
  "testkit.setup.mjs",
  "testkit.setup.js",
];

export function findSetupFile(productDir) {
  for (const candidate of SETUP_FILES) {
    const absolute = path.join(productDir, candidate);
    if (fs.existsSync(absolute)) {
      return absolute;
    }
  }
  return null;
}

export async function loadTestkitSetup(productDir) {
  const setupFile = findSetupFile(productDir);
  if (!setupFile) {
    return {
      setup: {},
      setupFile: null,
    };
  }

  const bundleDir = path.join(productDir, ".testkit", "_setup");
  fs.mkdirSync(bundleDir, { recursive: true });

  const cacheKey = buildSetupCacheKey(setupFile);
  const outputFile = path.join(bundleDir, `setup-${cacheKey.slice(0, 12)}.mjs`);

  await build({
    absWorkingDir: productDir,
    bundle: true,
    entryPoints: [setupFile],
    format: "esm",
    legalComments: "none",
    outfile: outputFile,
    platform: "node",
    sourcemap: "inline",
    target: "es2020",
    plugins: [testkitSetupAliasPlugin()],
  });

  const imported = await import(`${pathToFileURL(outputFile).href}?v=${cacheKey}`);
  const setup = imported.default || imported.setup || {};
  if (!setup || typeof setup !== "object") {
    throw new Error(`testkit setup file must export an object: ${path.basename(setupFile)}`);
  }

  return {
    setup,
    setupFile,
  };
}

function buildSetupCacheKey(setupFile) {
  const content = fs.readFileSync(setupFile, "utf8");
  return crypto.createHash("sha256").update(setupFile).update("\0").update(content).digest("hex");
}

function testkitSetupAliasPlugin() {
  return {
    name: "testkit-setup-alias",
    setup(buildApi) {
      buildApi.onResolve({ filter: /^@elench\/testkit(?:\/.*)?$/ }, (args) => ({
        namespace: "file",
        path: resolvePackageSubpath(args.path),
      }));
    },
  };
}

function resolvePackageSubpath(specifier) {
  const subpath = specifier.slice("@elench/testkit".length);
  if (!subpath) return ROOT_ENTRY;
  if (subpath === "/setup") return SETUP_ENTRY;
  if (subpath === "/runtime") {
    throw new Error(
      "testkit.setup.ts may not import @elench/testkit/runtime. " +
        "Use @elench/testkit/setup runtime helpers instead."
    );
  }
  if (subpath === "/runtime/index.mjs") return RUNTIME_ENTRY;

  throw new Error(`Unsupported @elench/testkit import "${specifier}" while loading testkit setup`);
}
