"use strict";
var __create = Object.create;
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __getProtoOf = Object.getPrototypeOf;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
  for (var name in all)
    __defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
  if (from && typeof from === "object" || typeof from === "function") {
    for (let key of __getOwnPropNames(from))
      if (!__hasOwnProp.call(to, key) && key !== except)
        __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
  }
  return to;
};
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
  // If the importer is in node compatibility mode or this is not an ESM
  // file that has been converted to a CommonJS file using a Babel-
  // compatible transform (i.e. "__esModule" has not been set), then set
  // "default" to the CommonJS "module.exports" for node compatibility.
  isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
  mod
));
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
var add_command_exports = {};
__export(add_command_exports, {
  AddCommand: () => AddCommand
});
module.exports = __toCommonJS(add_command_exports);
var sdk = __toESM(require("@botpress/sdk"));
var fslib = __toESM(require("fs"));
var pathlib = __toESM(require("path"));
var import_semver = __toESM(require("semver"));
var apiUtils = __toESM(require("../api"));
var codegen = __toESM(require("../code-generation"));
var consts = __toESM(require("../consts"));
var errors = __toESM(require("../errors"));
var pkgRef = __toESM(require("../package-ref"));
var utils = __toESM(require("../utils"));
var import_global_command = require("./global-command");
var import_project_command = require("./project-command");
class AddCommand extends import_global_command.GlobalCommand {
  async run() {
    const ref = this._parseArgvRef();
    if (ref) {
      return await this._addSinglePackage(ref);
    }
    const pkgJson = await utils.pkgJson.readPackageJson(this.argv.installPath);
    if (!pkgJson) {
      this.logger.warn("No package.json found in the install path");
      return;
    }
    const { bpDependencies } = pkgJson;
    if (!bpDependencies) {
      this.logger.log("No bp dependencies found in package.json");
      return;
    }
    const bpDependenciesSchema = sdk.z.record(sdk.z.string());
    const parseResults = bpDependenciesSchema.safeParse(bpDependencies);
    if (!parseResults.success) {
      throw new errors.BotpressCLIError("Invalid bpDependencies found in package.json");
    }
    for (const [pkgAlias, pkgRefStr] of Object.entries(parseResults.data)) {
      const parsed = pkgRef.parsePackageRef(pkgRefStr);
      if (!parsed) {
        throw new errors.InvalidPackageReferenceError(pkgRefStr);
      }
      await this._addSinglePackage({ ...parsed, alias: pkgAlias });
    }
  }
  _parseArgvRef = () => {
    if (!this.argv.packageRef) {
      return;
    }
    const parsed = pkgRef.parsePackageRef(this.argv.packageRef);
    if (!parsed) {
      throw new errors.InvalidPackageReferenceError(this.argv.packageRef);
    }
    if (parsed.type !== "name") {
      return parsed;
    }
    const argvPkgType = this.argv.packageType;
    if (!argvPkgType) {
      return parsed;
    }
    const ref = { ...parsed, pkg: argvPkgType };
    const strRef = pkgRef.formatPackageRef(ref);
    this.logger.warn(`argument --packageType is deprecated; please use the package reference format "${strRef}"`);
    return ref;
  };
  async _addSinglePackage(ref) {
    const targetPackage = ref.type === "path" ? await this._findLocalPackage(ref) : await this._findRemotePackage(ref);
    if (!targetPackage) {
      const strRef = pkgRef.formatPackageRef(ref);
      throw new errors.BotpressCLIError(`Could not find package "${strRef}"`);
    }
    const packageName = ref.alias ?? targetPackage.pkg.name;
    const baseInstallPath = utils.path.absoluteFrom(utils.path.cwd(), this.argv.installPath);
    const packageDirName = utils.casing.to.kebabCase(packageName);
    const installPath = utils.path.join(baseInstallPath, consts.installDirName, packageDirName);
    const alreadyInstalled = fslib.existsSync(installPath);
    if (alreadyInstalled) {
      this.logger.warn(`Package with name "${packageName}" already installed.`);
      const res = await this.prompt.confirm("Do you want to overwrite the existing package?");
      if (!res) {
        this.logger.log("Aborted");
        return;
      }
      await this._uninstall(installPath);
    }
    if (ref.type === "name" && ref.version === pkgRef.LATEST_TAG) {
      const major = import_semver.default.major(targetPackage.pkg.version);
      targetPackage.pkg.version = `>=${major}.0.0 <${major + 1}.0.0`;
      this.logger.log(
        `Dependency "${packageName}" will be installed with version "${targetPackage.pkg.version}". To pin a specific version or version range, please change "${targetPackage.type}:${packageName}@latest" to a specific version number or range instead of "latest".`
      );
    } else if (ref.type === "name") {
      targetPackage.pkg.version = ref.version;
    }
    let files;
    if (targetPackage.type === "integration") {
      files = await codegen.generateIntegrationPackage(targetPackage.pkg);
    } else if (targetPackage.type === "interface") {
      files = await codegen.generateInterfacePackage(targetPackage.pkg);
    } else if (targetPackage.type === "plugin") {
      files = await codegen.generatePluginPackage(targetPackage.pkg);
    } else {
      throw new errors.BotpressCLIError("Invalid package type");
    }
    await this._install(installPath, files);
    await this._addDependencyToPackage(packageName, targetPackage);
  }
  async _findRemotePackage(ref) {
    const api = await this.ensureLoginAndCreateClient(this.argv);
    if (this._pkgCouldBe(ref, "integration")) {
      const integration = await api.findPublicOrPrivateIntegration(ref);
      if (integration) {
        const { name, version } = integration;
        return { type: "integration", pkg: { integration, name, version } };
      }
    }
    if (this._pkgCouldBe(ref, "interface")) {
      const intrface = await api.findPublicOrPrivateInterface(ref);
      if (intrface) {
        const { name, version } = intrface;
        return { type: "interface", pkg: { interface: intrface, name, version } };
      }
    }
    if (this._pkgCouldBe(ref, "plugin")) {
      const plugin = await api.findPublicOrPrivatePlugin(ref);
      if (plugin) {
        const { code } = plugin.public ? await api.client.getPublicPluginCode({ id: plugin.id, platform: "node" }) : await api.client.getPluginCode({ id: plugin.id, platform: "node" });
        const { name, version } = plugin;
        return {
          type: "plugin",
          pkg: {
            name,
            version,
            plugin,
            code
          }
        };
      }
    }
    return;
  }
  async _findLocalPackage(ref) {
    const absPath = utils.path.absoluteFrom(utils.path.cwd(), ref.path);
    const {
      definition: projectDefinition,
      implementation: projectImplementation,
      devId: projectDevId
    } = await this._readProject(absPath);
    if (projectDefinition?.type === "integration") {
      const { name, version } = projectDefinition.definition;
      let devId;
      if (this.argv.useDev && projectDevId) {
        this.logger.warn(`Installing integration "${name}" with dev version "${projectDevId}"`);
        devId = projectDevId;
      }
      let createIntegrationReqBody = await this._getProjectCmd(ref.path).prepareCreateIntegrationBody(
        projectDefinition.definition
      );
      createIntegrationReqBody = {
        ...createIntegrationReqBody,
        interfaces: utils.records.mapValues(projectDefinition.definition.interfaces ?? {}, (i) => ({
          id: "",
          // TODO: do this better
          ...i
        }))
      };
      return {
        type: "integration",
        pkg: { path: absPath, devId, name, version, integration: createIntegrationReqBody }
      };
    }
    if (projectDefinition?.type === "interface") {
      const { name, version } = projectDefinition.definition;
      const createInterfaceReqBody = await apiUtils.prepareCreateInterfaceBody(projectDefinition.definition);
      return {
        type: "interface",
        pkg: { path: absPath, name, version, interface: createInterfaceReqBody }
      };
    }
    if (projectDefinition?.type === "plugin") {
      if (!projectImplementation) {
        throw new errors.BotpressCLIError(
          "Plugin implementation not found; Please build the plugin project before installing"
        );
      }
      const pluginDefinition = projectDefinition.definition;
      const { name, version } = pluginDefinition;
      const code = projectImplementation;
      const createPluginReqBody = await apiUtils.prepareCreatePluginBody(pluginDefinition);
      return {
        type: "plugin",
        pkg: {
          path: absPath,
          name,
          version,
          code,
          plugin: {
            ...createPluginReqBody,
            dependencies: {
              interfaces: pluginDefinition.interfaces,
              integrations: pluginDefinition.integrations
            },
            recurringEvents: pluginDefinition.recurringEvents
          }
        }
      };
    }
    if (projectDefinition?.type === "bot") {
      throw new errors.BotpressCLIError("Cannot install a bot as a package");
    }
    return;
  }
  async _install(installPath, files) {
    const line = this.logger.line();
    line.started(`Installing ${files.length} files to "${installPath}"`);
    try {
      for (const file of files) {
        const filePath = utils.path.absoluteFrom(installPath, file.path);
        const dirPath = pathlib.dirname(filePath);
        await fslib.promises.mkdir(dirPath, { recursive: true });
        await fslib.promises.writeFile(filePath, file.content);
      }
      line.success(`Installed ${files.length} files to "${installPath}"`);
    } finally {
      line.commit();
    }
  }
  async _uninstall(installPath) {
    await fslib.promises.rm(installPath, { recursive: true });
  }
  async _readProject(workDir) {
    const cmd = this._getProjectCmd(workDir);
    const { resolveProjectDefinition } = cmd.readProjectDefinitionFromFS();
    const definition = await resolveProjectDefinition().catch((thrown) => {
      if (thrown instanceof errors.ProjectDefinitionNotFoundError) {
        return void 0;
      }
      throw thrown;
    });
    const devId = await cmd.projectCache.get("devId");
    const implementationAbsPath = utils.path.join(workDir, consts.fromWorkDir.outFileCJS);
    if (!fslib.existsSync(implementationAbsPath)) {
      return { definition, devId };
    }
    const implementation = await fslib.promises.readFile(implementationAbsPath, "utf8");
    return { definition, implementation, devId };
  }
  _pkgCouldBe = (ref, pkgType) => {
    if (ref.type === "id") {
      return true;
    }
    if (!ref.pkg) {
      return true;
    }
    return ref.pkg === pkgType;
  };
  _getProjectCmd(workDir) {
    return new _AnyProjectCommand(apiUtils.ApiClient, this.prompt, this.logger, {
      ...this.argv,
      workDir
    });
  }
  async _addDependencyToPackage(packageName, targetPackage) {
    const pkgJson = await utils.pkgJson.readPackageJson(this.argv.installPath);
    const version = targetPackage.pkg.path ?? targetPackage.pkg.version;
    if (!pkgJson) {
      this.logger.warn("No package.json found in the install path");
      return;
    }
    const { bpDependencies } = pkgJson;
    if (!bpDependencies) {
      pkgJson.bpDependencies = { [packageName]: version };
      await utils.pkgJson.writePackageJson(this.argv.installPath, pkgJson);
      return;
    }
    const bpDependenciesSchema = sdk.z.record(sdk.z.string());
    const parseResult = bpDependenciesSchema.safeParse(bpDependencies);
    if (!parseResult.success) {
      throw new errors.BotpressCLIError("Invalid bpDependencies found in package.json");
    }
    const { data: validatedBpDeps } = parseResult;
    const alreadyPresentDep = Object.entries(validatedBpDeps).find(([key]) => key === packageName);
    if (alreadyPresentDep) {
      if (alreadyPresentDep[1] !== version) {
        this.logger.warn(
          `The dependency ${packageName} is already present in the bpDependencies of package.json. It will not be replaced.`
        );
      }
      return;
    }
    pkgJson.bpDependencies = {
      ...validatedBpDeps,
      [packageName]: version
    };
    await utils.pkgJson.writePackageJson(this.argv.installPath, pkgJson);
  }
}
class _AnyProjectCommand extends import_project_command.ProjectCommand {
  async run() {
    throw new errors.BotpressCLIError("Not implemented");
  }
  readProjectDefinitionFromFS() {
    return super.readProjectDefinitionFromFS();
  }
  async prepareCreateIntegrationBody(integrationDef) {
    return super.prepareCreateIntegrationBody(integrationDef);
  }
  get projectCache() {
    return super.projectCache;
  }
}
// Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
  AddCommand
});
//# sourceMappingURL=add-command.js.map
