/**
 * ailf-resolver.ts — locate `@sanity/ailf` for user TS files, with a bundled fallback.
 *
 * User `.ailf/*.ts` files import `defineTask` / `defineConfig` / `definePreset`
 * from `@sanity/ailf`. In a fresh project with no local install, that bare
 * specifier cannot resolve from the user's tree. To keep `ailf init` → `ailf
 * pipeline` working out of the box we transparently fall back to the CLI's own
 * copy of `@sanity/ailf` by registering a jiti module alias. A user-local
 * install always wins — the fallback kicks in only when resolution fails.
 *
 * All jiti callsites across the eval package use `resolveAilfAlias()` to get
 * a consistent resolution story. The bundled fallback is silent by default;
 * `ailf init` is the single place that advises pinning `@sanity/ailf` locally.
 * Set `AILF_LOG_LEVEL=verbose` to log which copy was loaded. Callers pass the
 * returned map (or nothing) to `createJiti`.
 */
import { existsSync, readFileSync, statSync } from "node:fs";
import { dirname, resolve as pathResolve } from "node:path";
import { fileURLToPath } from "node:url";
/**
 * Walk up from this module's location to find the enclosing `@sanity/ailf`
 * package root. Works in both dev (source under `packages/eval/src/`) and
 * production (compiled under `packages/eval/dist/`) because both share the
 * same package.json anchor.
 */
function findAilfPackageRoot() {
    let dir = dirname(fileURLToPath(import.meta.url));
    while (dir !== dirname(dir)) {
        const pkgPath = pathResolve(dir, "package.json");
        if (existsSync(pkgPath)) {
            try {
                const pkg = JSON.parse(readFileSync(pkgPath, "utf-8"));
                if (pkg.name === "@sanity/ailf")
                    return dir;
            }
            catch {
                /* ignore malformed package.json */
            }
        }
        dir = dirname(dir);
    }
    return null;
}
/**
 * Probe whether the user has `@sanity/ailf` installed as a local dependency
 * reachable from the given path. Walks up the directory tree looking for a
 * `node_modules/@sanity/ailf/package.json`. Returns the package entry point
 * path on success, null otherwise.
 *
 * We intentionally do NOT use Node's `require.resolve` self-reference path:
 * tsx and some bundler setups make it unreliable, and a self-reference
 * would only match when the caller *is* the `@sanity/ailf` package (the
 * monorepo devving case), which is semantically the same as having no
 * install — the bundled fallback handles it.
 */
export function probeUserLocalAilf(fromPath) {
    let dir;
    try {
        dir =
            existsSync(fromPath) && statSync(fromPath).isDirectory()
                ? fromPath
                : dirname(fromPath);
    }
    catch {
        dir = dirname(fromPath);
    }
    while (dir !== dirname(dir)) {
        const pkgJson = pathResolve(dir, "node_modules", "@sanity", "ailf", "package.json");
        if (existsSync(pkgJson)) {
            try {
                const pkg = JSON.parse(readFileSync(pkgJson, "utf-8"));
                const entry = pkg.module ?? pkg.main ?? "index.js";
                return pathResolve(dirname(pkgJson), entry);
            }
            catch {
                return null;
            }
        }
        dir = dirname(dir);
    }
    return null;
}
/**
 * Return the path to the CLI's own bundled copy of `@sanity/ailf`. Used as the
 * fallback target when a user's project does not have it installed.
 *
 * We walk the filesystem rather than `require.resolve("@sanity/ailf")` because
 * self-reference resolution is unreliable under tsx and some bundler setups.
 * Returns null in exotic setups where no ancestor package.json matches.
 */
export function getBundledAilfPath() {
    const pkgRoot = findAilfPackageRoot();
    if (!pkgRoot)
        return null;
    // Production layout: packages/eval/dist/index.js
    const distEntry = pathResolve(pkgRoot, "dist", "index.js");
    if (existsSync(distEntry))
        return distEntry;
    // Development layout (tsx on source): packages/eval/src/index.ts
    const srcEntry = pathResolve(pkgRoot, "src", "index.ts");
    if (existsSync(srcEntry))
        return srcEntry;
    return null;
}
/**
 * Decide whether jiti should alias `@sanity/ailf` → bundled-path for the given
 * file. Returns the alias map or null.
 *
 * The bundled fallback is silent by default — `ailf init` already advises
 * pinning `@sanity/ailf` locally, so re-warning on every pipeline run would be
 * pure noise. Set `AILF_LOG_LEVEL=verbose` to log which copy was loaded.
 *
 * - User-local resolves → returns null (jiti's natural walk finds it).
 * - User-local fails + bundled path available → returns alias map, logs at
 *   verbose level.
 * - User-local fails + no bundled path → returns null (nothing we can do;
 *   jiti will surface the original MODULE_NOT_FOUND).
 */
export function resolveAilfAlias(filePath) {
    const userLocal = probeUserLocalAilf(filePath);
    const verbose = process.env.AILF_LOG_LEVEL === "verbose";
    if (userLocal) {
        if (verbose) {
            process.stderr.write(`  [ts-loader] ${filePath} → @sanity/ailf resolved locally at ${userLocal}\n`);
        }
        return null;
    }
    const bundled = getBundledAilfPath();
    if (!bundled)
        return null;
    if (verbose) {
        process.stderr.write(`  [ts-loader] ${filePath} → @sanity/ailf not installed locally; using bundled copy at ${bundled}\n`);
    }
    return { "@sanity/ailf": bundled };
}
