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 attachments_exports = {};
__export(attachments_exports, {
  createMiddleware: () => createMiddleware,
  destroyMiddleware: () => destroyMiddleware
});
module.exports = __toCommonJS(attachments_exports);
var import_node_path = __toESM(require("node:path"));
var import_server = require("@tego/server");
var import_constants = require("../constants");
var Rules = __toESM(require("../rules"));
var import_server2 = __toESM(require("../server"));
var import_storages = require("../storages");
function getFileFilter(storage) {
  return (req, file, cb) => {
    const { size, ...rules } = storage.rules;
    const ruleKeys = Object.keys(rules);
    const result = !ruleKeys.length || !ruleKeys.some((key) => typeof Rules[key] !== "function" || !Rules[key](file, rules[key]));
    cb(null, result);
  };
}
function getFileData(ctx) {
  const { [import_constants.FILE_FIELD_NAME]: file, storage } = ctx;
  if (!file) {
    return ctx.throw(400, "file validation failed");
  }
  const storageConfig = (0, import_storages.getStorageConfig)(storage.type);
  const { [storageConfig.filenameKey || "filename"]: name } = file;
  const filename = import_node_path.default.basename(name);
  const extname = import_node_path.default.extname(filename);
  const urlPath = storage.path ? storage.path.replace(/^([^/])/, "/$1") : "";
  return {
    title: Buffer.from(file.originalname, "latin1").toString("utf8").replace(extname, ""),
    filename,
    extname,
    // TODO(feature): 暂时两者相同，后面 storage.path 模版化以后，这里只是 file 实际的 path
    path: storage.path,
    size: file.size,
    // 直接缓存起来
    url: `${storage.baseUrl}${urlPath}/${filename}`,
    mimetype: file.mimetype,
    // @ts-ignore
    meta: ctx.request.body,
    storageId: storage.id,
    ...storageConfig.getFileData ? storageConfig.getFileData(file) : {}
  };
}
async function multipart(ctx, next) {
  const { storage } = ctx;
  if (!storage) {
    ctx.logger.error("[file-manager] no linked or default storage provided");
    return ctx.throw(500);
  }
  const storageConfig = (0, import_storages.getStorageConfig)(storage.type);
  if (!storageConfig) {
    ctx.logger.error(`[file-manager] storage type "${storage.type}" is not defined`);
    return ctx.throw(500);
  }
  const multerOptions = {
    fileFilter: getFileFilter(storage),
    limits: {
      fileSize: storage.rules.size ?? import_constants.DEFAULT_MAX_FILE_SIZE,
      // 每次只允许提交一个文件
      files: import_constants.LIMIT_FILES
    },
    storage: storageConfig.make(storage)
  };
  const upload = (0, import_server.koaMulter)(multerOptions).single(import_constants.FILE_FIELD_NAME);
  try {
    await upload(ctx, () => {
    });
  } catch (err) {
    if (err.name === "MulterError") {
      return ctx.throw(400, err);
    }
    ctx.logger.error(err);
    return ctx.throw(500);
  }
  const values = getFileData(ctx);
  ctx.action.mergeParams({
    values
  });
  await next();
}
async function createMiddleware(ctx, next) {
  var _a, _b, _c;
  const { resourceName, actionName } = ctx.action;
  const { attachmentField } = ctx.action.params;
  const collection = ctx.db.getCollection(resourceName);
  if (((_a = collection == null ? void 0 : collection.options) == null ? void 0 : _a.template) !== "file" || !["upload", "create"].includes(actionName)) {
    return next();
  }
  const storageName = ((_c = (_b = ctx.db.getFieldByPath(attachmentField)) == null ? void 0 : _b.options) == null ? void 0 : _c.storage) || collection.options.storage;
  const StorageRepo = ctx.db.getRepository("storages");
  const storage = await StorageRepo.findOne({ filter: storageName ? { name: storageName } : { default: true } });
  const plugin = ctx.tego.pm.get(import_server2.default);
  ctx.storage = plugin.parseStorage(storage);
  await multipart(ctx, next);
}
async function destroyMiddleware(ctx, next) {
  var _a;
  const { resourceName, actionName } = ctx.action;
  const collection = ctx.db.getCollection(resourceName);
  if (((_a = collection == null ? void 0 : collection.options) == null ? void 0 : _a.template) !== "file" || actionName !== "destroy") {
    return next();
  }
  const repository = ctx.db.getRepository(resourceName);
  const { filterByTk, filter } = ctx.action.params;
  const records = await repository.find({
    filterByTk,
    filter,
    context: ctx
  });
  const storageIds = new Set(records.map((record) => record.storageId));
  const storageGroupedRecords = records.reduce((result, record) => {
    const storageId = record.storageId;
    if (!result[storageId]) {
      result[storageId] = [];
    }
    result[storageId].push(record);
    return result;
  }, {});
  const storages = await ctx.db.getRepository("storages").find({
    filter: {
      id: [...storageIds],
      paranoid: {
        $ne: true
      }
    }
  });
  let count = 0;
  const undeleted = [];
  await storages.reduce(
    (promise, storage) => promise.then(async () => {
      const storageConfig = (0, import_storages.getStorageConfig)(storage.type);
      const result = await storageConfig.delete(storage, storageGroupedRecords[storage.id]);
      count += result[0];
      undeleted.push(...result[1]);
    }),
    Promise.resolve()
  );
  if (undeleted.length) {
    const ids = undeleted.map((record) => record.id);
    ctx.action.mergeParams({
      filter: {
        id: {
          $notIn: ids
        }
      }
    });
    ctx.logger.error("[file-manager] some of attachment files are not successfully deleted: ", { ids });
  }
  await next();
}
// Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
  createMiddleware,
  destroyMiddleware
});
