import { dirname, join } from "node:path";
import { fileURLToPath } from "node:url";
import { existsSync } from "node:fs";
import { readFile } from "node:fs/promises";
import { isBuiltin } from "node:module";
import { createJiti } from "./jiti.mjs";

let jiti;

// https://nodejs.org/api/module.html#initialize
export async function initialize() {
  jiti = createJiti();
}

// https://nodejs.org/api/module.html#resolvespecifier-context-nextresolve
export async function resolve(specifier, context, nextResolve) {
  if (_shouldSkip(specifier)) {
    return nextResolve(specifier, context);
  }
  const resolvedPath = jiti.esmResolve(specifier, {
    parentURL: context?.parentURL,
    conditions: context?.conditions,
  });
  return {
    url: resolvedPath,
    shortCircuit: true,
  };
}

// https://nodejs.org/api/module.html#loadurl-context-nextload
export async function load(url, context, nextLoad) {
  if (_shouldSkip(url)) {
    return nextLoad(url, context);
  }

  const filename = fileURLToPath(url);

  if (url.endsWith(".js")) {
    const pkg = await _findClosestPackageJson(dirname(filename));
    if (pkg && pkg.type === "module") {
      return nextLoad(url, context);
    }
  }

  const rawSource = await readFile(filename, "utf8");

  if (url.endsWith(".json")) {
    return {
      source: `export default ${rawSource}`,
      format: "module",
      shortCircuit: true,
    };
  }

  const transpiledSource = jiti.transform({
    source: rawSource,
    filename: filename,
    ts: url.endsWith("ts"),
    retainLines: true,
    async: true,
    jsx: jiti.options.jsx,
  });

  if (url.endsWith(".js") && !transpiledSource.includes("jitiImport")) {
    return {
      source: transpiledSource,
      format: "commonjs",
      shortCircuit: true,
    };
  }

  return {
    source: _wrapSource(transpiledSource, filename),
    format: "module",
    shortCircuit: true,
  };
}

function _wrapSource(source, filename) {
  const _jitiPath = new URL("jiti.mjs", import.meta.url).href;
  return /*js*/ `import { createJiti as __createJiti__ } from ${JSON.stringify(_jitiPath)};async function _module(exports, require, module, __filename, __dirname, jitiImport) { ${source}\n};
// GENERATED BY JITI ESM LOADER
const filename = ${JSON.stringify(filename)};
const dirname = ${JSON.stringify(dirname(filename))};
const jiti = __createJiti__(filename);
const module = { exports: Object.create(null) };
await _module(module.exports, jiti, module, filename, dirname, jiti.import);
if (module.exports && module.exports.__JITI_ERROR__) {
  const { filename, line, column, code, message } =
  module.exports.__JITI_ERROR__;
  const loc = [filename, line, column].join(':');
  const err = new Error(code + ": " + message + " " + loc);
  Error.captureStackTrace(err, _module);
  throw err;
}
export default module.exports;
  `;
}

function _shouldSkip(url) {
  return (
    !jiti ||
    url.endsWith(".mjs") ||
    url.endsWith(".cjs") ||
    (!url.startsWith("./") && !url.startsWith("file://")) ||
    isBuiltin(url)
  );
}

async function _findClosestPackageJson(dir) {
  if (dir === "/") return null;
  const packageJsonPath = join(dir, "package.json");
  if (existsSync(packageJsonPath)) {
    return JSON.parse(await readFile(packageJsonPath, "utf8"));
  }
  return _findClosestPackageJson(dirname(dir));
}
