import * as fs from 'node:fs';
import { homedir } from 'node:os';
import { dirname, extname, join as join$1 } from 'node:path';
import process from 'node:process';
import { r as resolveModulePath } from '../shared/nuxi.BLb4POFS.mjs';
import { S as SUPPORTED_EXTENSIONS } from '../shared/nuxi.DRpqwPny.mjs';
import { mkdir, writeFile, readFile } from 'node:fs/promises';
import 'node:url';
import 'node:module';
import '../shared/nuxi.BHaQQUPr.mjs';
import { j as join, n as normalize, r as resolve } from '../shared/nuxi.Do0aYBCO.mjs';
import { d as defineCommand, l as logLevelArgs, c as cwdArgs, r as runCommand } from '../shared/nuxi.C0YEEVs2.mjs';
import { l as logger, a as colors } from '../shared/nuxi.DQjfoGl0.mjs';
import 'node:tty';
import { f as fetchModules, c as checkNuxtCompatibility, $ as $fetch, g as getRegistryFromContent } from '../shared/nuxi.Gg4oZG1J.mjs';
import { r as readPackageJSON } from '../shared/nuxi.8uLzFmy0.mjs';
import { s as semverExports } from '../shared/nuxi.DU9QXUOf.mjs';
import { j as joinURL } from '../shared/nuxi.QWnQy_ku.mjs';
import { g as getNuxtVersion } from '../shared/nuxi.DlMb1OKt.mjs';
import prepareCommand from './prepare.mjs';
import { a as addDependency } from '../shared/nuxi.DkoVccfL.mjs';
import 'node:assert';
import 'node:v8';
import 'node:util';
import 'fs';
import 'path';
import 'os';
import 'crypto';
import '../shared/nuxi.BAGoDD49.mjs';
import 'node:crypto';
import 'node:perf_hooks';
import 'node:vm';
import 'node:http';
import 'node:https';
import 'node:zlib';
import 'node:stream';
import 'node:buffer';
import 'node:net';
import '../shared/nuxi.4t9faemh.mjs';
import '../shared/nuxi.CrvQqJcE.mjs';
import '../shared/nuxi.BiWTNJI4.mjs';
import './main.mjs';
import 'child_process';
import 'process';
import 'stream';
import 'readline';

const UPDATABLE_EXTS = [".js", ".ts", ".mjs", ".cjs", ".mts", ".cts"];
async function updateConfig(opts) {
  const { parseModule } = await import('./index6.mjs');
  let configFile = tryResolve(`./${opts.configFile}`, opts.cwd, SUPPORTED_EXTENSIONS) || tryResolve(
    `./.config/${opts.configFile}`,
    opts.cwd,
    SUPPORTED_EXTENSIONS
  ) || tryResolve(
    `./.config/${opts.configFile.split(".")[0]}`,
    opts.cwd,
    SUPPORTED_EXTENSIONS
  );
  let created = false;
  if (!configFile) {
    configFile = join(
      opts.cwd,
      opts.configFile + (opts.createExtension || ".ts")
    );
    const createResult = await opts.onCreate?.({ configFile }) ?? true;
    if (!createResult) {
      throw new Error("Config file creation aborted.");
    }
    const content = typeof createResult === "string" ? createResult : `export default {}
`;
    await mkdir(dirname(configFile), { recursive: true });
    await writeFile(configFile, content, "utf8");
    created = true;
  }
  const ext = extname(configFile);
  if (!UPDATABLE_EXTS.includes(ext)) {
    throw new Error(
      `Unsupported config file extension: ${ext} (${configFile}) (supported: ${UPDATABLE_EXTS.join(", ")})`
    );
  }
  const contents = await readFile(configFile, "utf8");
  const _module = parseModule(contents, opts.magicast);
  const defaultExport = _module.exports.default;
  if (!defaultExport) {
    throw new Error("Default export is missing in the config file!");
  }
  const configObj = defaultExport.$type === "function-call" ? defaultExport.$args[0] : defaultExport;
  await opts.onUpdate?.(configObj);
  await writeFile(configFile, _module.generate().code);
  return {
    configFile,
    created
  };
}
function tryResolve(path, cwd, extensions) {
  const res = resolveModulePath(path, {
    try: true,
    from: join(cwd, "/"),
    extensions,
    suffixes: ["", "/index"],
    cache: false
  });
  return res ? normalize(res) : void 0;
}

const addModuleCommand = defineCommand({
  meta: {
    name: "add",
    description: "Add Nuxt modules"
  },
  args: {
    ...cwdArgs,
    ...logLevelArgs,
    moduleName: {
      type: "positional",
      description: "Specify one or more modules to install by name, separated by spaces"
    },
    skipInstall: {
      type: "boolean",
      description: "Skip npm install"
    },
    skipConfig: {
      type: "boolean",
      description: "Skip nuxt.config.ts update"
    },
    dev: {
      type: "boolean",
      description: "Install modules as dev dependencies"
    }
  },
  async setup(ctx) {
    const cwd = resolve(ctx.args.cwd);
    const modules = ctx.args._.map((e) => e.trim()).filter(Boolean);
    const projectPkg = await readPackageJSON(cwd).catch(() => ({}));
    if (!projectPkg.dependencies?.nuxt && !projectPkg.devDependencies?.nuxt) {
      logger.warn(`No \`nuxt\` dependency detected in \`${cwd}\`.`);
      const shouldContinue = await logger.prompt(
        `Do you want to continue anyway?`,
        {
          type: "confirm",
          initial: false,
          cancel: "default"
        }
      );
      if (shouldContinue !== true) {
        process.exit(1);
      }
    }
    const maybeResolvedModules = await Promise.all(modules.map((moduleName) => resolveModule(moduleName, cwd)));
    const resolvedModules = maybeResolvedModules.filter((x) => x != null);
    logger.info(`Resolved \`${resolvedModules.map((x) => x.pkgName).join("`, `")}\`, adding module${resolvedModules.length > 1 ? "s" : ""}...`);
    await addModules(resolvedModules, { ...ctx.args, cwd }, projectPkg);
    if (!ctx.args.skipInstall) {
      const args = Object.entries(ctx.args).filter(([k]) => k in cwdArgs || k in logLevelArgs).map(([k, v]) => `--${k}=${v}`);
      await runCommand(prepareCommand, args);
    }
  }
});
async function addModules(modules, { skipInstall, skipConfig, cwd, dev }, projectPkg) {
  if (!skipInstall) {
    const installedModules = [];
    const notInstalledModules = [];
    const dependencies = /* @__PURE__ */ new Set([
      ...Object.keys(projectPkg.dependencies || {}),
      ...Object.keys(projectPkg.devDependencies || {})
    ]);
    for (const module of modules) {
      if (dependencies.has(module.pkgName)) {
        installedModules.push(module);
      } else {
        notInstalledModules.push(module);
      }
    }
    if (installedModules.length > 0) {
      const installedModulesList = installedModules.map((module) => module.pkgName).join("`, `");
      const are = installedModules.length > 1 ? "are" : "is";
      logger.info(`\`${installedModulesList}\` ${are} already installed`);
    }
    if (notInstalledModules.length > 0) {
      const isDev = Boolean(projectPkg.devDependencies?.nuxt) || dev;
      const notInstalledModulesList = notInstalledModules.map((module) => module.pkg).join("`, `");
      const dependency = notInstalledModules.length > 1 ? "dependencies" : "dependency";
      const a = notInstalledModules.length > 1 ? "" : " a";
      logger.info(`Installing \`${notInstalledModulesList}\` as${a}${isDev ? " development" : ""} ${dependency}`);
      const res = await addDependency(notInstalledModules.map((module) => module.pkg), {
        cwd,
        dev: isDev,
        installPeerDependencies: true
      }).then(() => true).catch(
        (error) => {
          logger.error(error);
          const failedModulesList = notInstalledModules.map((module) => colors.cyan(module.pkg)).join("`, `");
          const s = notInstalledModules.length > 1 ? "s" : "";
          return logger.prompt(`Install failed for \`${failedModulesList}\`. Do you want to continue adding the module${s} to ${colors.cyan("nuxt.config")}?`, {
            type: "confirm",
            initial: false,
            cancel: "default"
          });
        }
      );
      if (res !== true) {
        return;
      }
    }
  }
  if (!skipConfig) {
    await updateConfig({
      cwd,
      configFile: "nuxt.config",
      async onCreate() {
        logger.info(`Creating \`nuxt.config.ts\``);
        return getDefaultNuxtConfig();
      },
      async onUpdate(config) {
        if (!config.modules) {
          config.modules = [];
        }
        for (const resolved of modules) {
          if (config.modules.includes(resolved.pkgName)) {
            logger.info(`\`${resolved.pkgName}\` is already in the \`modules\``);
            continue;
          }
          logger.info(`Adding \`${resolved.pkgName}\` to the \`modules\``);
          config.modules.push(resolved.pkgName);
        }
      }
    }).catch((error) => {
      logger.error(`Failed to update \`nuxt.config\`: ${error.message}`);
      logger.error(`Please manually add \`${modules.map((module) => module.pkgName).join("`, `")}\` to the \`modules\` in \`nuxt.config.ts\``);
      return null;
    });
  }
}
function getDefaultNuxtConfig() {
  return `
// https://nuxt.com/docs/api/configuration/nuxt-config
export default defineNuxtConfig({
  modules: []
})`;
}
const packageRegex = /^(@[a-z0-9-~][a-z0-9-._~]*\/)?([a-z0-9-~][a-z0-9-._~]*)(@[^@]+)?$/;
async function resolveModule(moduleName, cwd) {
  let pkgName = moduleName;
  let pkgVersion;
  const reMatch = moduleName.match(packageRegex);
  if (reMatch) {
    if (reMatch[3]) {
      pkgName = `${reMatch[1] || ""}${reMatch[2] || ""}`;
      pkgVersion = reMatch[3].slice(1);
    }
  } else {
    logger.error(`Invalid package name \`${pkgName}\`.`);
    return false;
  }
  const modulesDB = await fetchModules().catch((err) => {
    logger.warn(`Cannot search in the Nuxt Modules database: ${err}`);
    return [];
  });
  const matchedModule = modulesDB.find(
    (module) => module.name === moduleName || pkgVersion && module.name === pkgName || module.npm === pkgName || module.aliases?.includes(pkgName)
  );
  if (matchedModule?.npm) {
    pkgName = matchedModule.npm;
  }
  if (matchedModule && matchedModule.compatibility.nuxt) {
    const nuxtVersion = await getNuxtVersion(cwd);
    if (!checkNuxtCompatibility(matchedModule, nuxtVersion)) {
      logger.warn(
        `The module \`${pkgName}\` is not compatible with Nuxt \`${nuxtVersion}\` (requires \`${matchedModule.compatibility.nuxt}\`)`
      );
      const shouldContinue = await logger.prompt(
        "Do you want to continue installing incompatible version?",
        {
          type: "confirm",
          initial: false,
          cancel: "default"
        }
      );
      if (!shouldContinue) {
        return false;
      }
    }
    const versionMap = matchedModule.compatibility.versionMap;
    if (versionMap) {
      for (const [_nuxtVersion, _moduleVersion] of Object.entries(versionMap)) {
        if (semverExports.satisfies(nuxtVersion, _nuxtVersion)) {
          if (!pkgVersion) {
            pkgVersion = _moduleVersion;
          } else {
            logger.warn(
              `Recommended version of \`${pkgName}\` for Nuxt \`${nuxtVersion}\` is \`${_moduleVersion}\` but you have requested \`${pkgVersion}\``
            );
            pkgVersion = await logger.prompt("Choose a version:", {
              type: "select",
              options: [_moduleVersion, pkgVersion],
              cancel: "undefined"
            });
            if (!pkgVersion) {
              return false;
            }
          }
          break;
        }
      }
    }
  }
  let version = pkgVersion || "latest";
  const pkgScope = pkgName.startsWith("@") ? pkgName.split("/")[0] : null;
  const meta = await detectNpmRegistry(pkgScope);
  const headers = {};
  if (meta.authToken) {
    headers.Authorization = `Bearer ${meta.authToken}`;
  }
  const pkgDetails = await $fetch(joinURL(meta.registry, `${pkgName}`), { headers });
  if (pkgDetails["dist-tags"]?.[version]) {
    version = pkgDetails["dist-tags"][version];
  } else {
    version = Object.keys(pkgDetails.versions)?.findLast((v) => semverExports.satisfies(v, version)) || version;
  }
  const pkg = pkgDetails.versions[version];
  const pkgDependencies = Object.assign(
    pkg.dependencies || {},
    pkg.devDependencies || {}
  );
  if (!pkgDependencies.nuxt && !pkgDependencies["nuxt-edge"] && !pkgDependencies["@nuxt/kit"]) {
    logger.warn(`It seems that \`${pkgName}\` is not a Nuxt module.`);
    const shouldContinue = await logger.prompt(
      `Do you want to continue installing ${colors.cyan(pkgName)} anyway?`,
      {
        type: "confirm",
        initial: false,
        cancel: "default"
      }
    );
    if (!shouldContinue) {
      return false;
    }
  }
  return {
    nuxtModule: matchedModule,
    pkg: `${pkgName}@${version}`,
    pkgName,
    pkgVersion: version
  };
}
function getNpmrcPaths() {
  const userNpmrcPath = join$1(homedir(), ".npmrc");
  const cwdNpmrcPath = join$1(process.cwd(), ".npmrc");
  return [cwdNpmrcPath, userNpmrcPath];
}
async function getAuthToken(registry) {
  const paths = getNpmrcPaths();
  const authTokenRegex = new RegExp(`^//${registry.replace(/^https?:\/\//, "").replace(/\/$/, "")}/:_authToken=(.+)$`, "m");
  for (const npmrcPath of paths) {
    let fd;
    try {
      fd = await fs.promises.open(npmrcPath, "r");
      if (await fd.stat().then((r) => r.isFile())) {
        const npmrcContent = await fd.readFile("utf-8");
        const authTokenMatch = npmrcContent.match(authTokenRegex)?.[1];
        if (authTokenMatch) {
          return authTokenMatch.trim();
        }
      }
    } catch {
    } finally {
      await fd?.close();
    }
  }
  return null;
}
async function detectNpmRegistry(scope) {
  const registry = await getRegistry(scope);
  const authToken = await getAuthToken(registry);
  return {
    registry,
    authToken
  };
}
async function getRegistry(scope) {
  if (process.env.COREPACK_NPM_REGISTRY) {
    return process.env.COREPACK_NPM_REGISTRY;
  }
  const registry = await getRegistryFromFile(getNpmrcPaths(), scope);
  if (registry) {
    process.env.COREPACK_NPM_REGISTRY = registry;
  }
  return registry || "https://registry.npmjs.org";
}
async function getRegistryFromFile(paths, scope) {
  for (const npmrcPath of paths) {
    let fd;
    try {
      fd = await fs.promises.open(npmrcPath, "r");
      if (await fd.stat().then((r) => r.isFile())) {
        const npmrcContent = await fd.readFile("utf-8");
        const registry = getRegistryFromContent(npmrcContent, scope);
        if (registry) {
          return registry;
        }
      }
    } catch {
    } finally {
      await fd?.close();
    }
  }
  return null;
}

export { addModuleCommand as default };
