"use strict";

var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.InternalFileService = void 0;
var _server = require("@kbn/core/server");
var _pLimit = _interopRequireDefault(require("p-limit"));
var _file = require("../file");
var _errors = require("./errors");
var _file_client = require("../file_client/file_client");
/*
 * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
 * or more contributor license agreements. Licensed under the Elastic License
 * 2.0 and the Server Side Public License, v 1; you may not use this file except
 * in compliance with, at your election, the Elastic License 2.0 or the Server
 * Side Public License, v 1.
 */

const bulkDeleteConcurrency = (0, _pLimit.default)(10);

/**
 * Service containing methods for working with files.
 *
 * All file business logic is encapsulated in the {@link File} class.
 *
 * @internal
 */
class InternalFileService {
  constructor(metadataClient, blobStorageService, fileShareService, auditLogger, fileKindRegistry, logger) {
    this.metadataClient = metadataClient;
    this.blobStorageService = blobStorageService;
    this.fileShareService = fileShareService;
    this.auditLogger = auditLogger;
    this.fileKindRegistry = fileKindRegistry;
    this.logger = logger;
  }
  async createFile(args) {
    return this.createFileClient(args.fileKind).create({
      metadata: {
        ...args
      }
    });
  }
  writeAuditLog(event) {
    if (this.auditLogger) {
      this.auditLogger.log(event);
    } else {
      // Otherwise just log to info
      this.logger.info(event.message);
    }
  }
  async updateFile({
    attributes,
    id
  }) {
    const file = await this.getById({
      id
    });
    return await file.update(attributes);
  }
  async deleteFile({
    id
  }) {
    const file = await this.getById({
      id
    });
    await file.delete();
  }
  async bulkDeleteFiles({
    ids
  }) {
    const promises = ids.map(id => bulkDeleteConcurrency(() => this.deleteFile({
      id
    })));
    const result = await Promise.allSettled(promises);
    return result;
  }
  async get(id) {
    try {
      const {
        metadata
      } = await this.metadataClient.get({
        id
      });
      if (metadata.Status === 'DELETED') {
        throw new _errors.FileNotFoundError('File has been deleted');
      }
      return this.toFile(id, metadata, metadata.FileKind);
    } catch (e) {
      if (_server.SavedObjectsErrorHelpers.isNotFoundError(e)) {
        throw new _errors.FileNotFoundError('File not found');
      }
      this.logger.error(`Could not retrieve file: ${e}`);
      throw e;
    }
  }
  async bulkGet(ids, {
    throwIfNotFound = true,
    format = 'array'
  } = {}) {
    try {
      const metadatas = await this.metadataClient.bulkGet({
        ids,
        throwIfNotFound: false
      });
      const result = metadatas.map((fileMetadata, i) => {
        var _fileMetadata$metadat;
        const notFound = !fileMetadata || !fileMetadata.metadata;
        const deleted = (fileMetadata === null || fileMetadata === void 0 ? void 0 : (_fileMetadata$metadat = fileMetadata.metadata) === null || _fileMetadata$metadat === void 0 ? void 0 : _fileMetadata$metadat.Status) === 'DELETED';
        if (notFound || deleted) {
          var _fileMetadata$id;
          if (!throwIfNotFound) {
            return null;
          }
          throw new _errors.FileNotFoundError(deleted ? 'File has been deleted' : `File [${(_fileMetadata$id = fileMetadata === null || fileMetadata === void 0 ? void 0 : fileMetadata.id) !== null && _fileMetadata$id !== void 0 ? _fileMetadata$id : ids[i]}] not found`);
        }
        const {
          id,
          metadata
        } = fileMetadata;
        return this.toFile(id, metadata, metadata.FileKind);
      });
      return format === 'array' ? result : ids.reduce((acc, id, i) => ({
        ...acc,
        [id]: result[i]
      }), {});
    } catch (e) {
      this.logger.error(`Could not retrieve files: ${e}`);
      throw e;
    }
  }
  async getById({
    id
  }) {
    return await this.get(id);
  }
  async bulkGetById({
    ids,
    throwIfNotFound,
    format
  }) {
    return await this.bulkGet(ids, {
      throwIfNotFound,
      format
    });
  }
  getFileKind(id) {
    return this.fileKindRegistry.get(id);
  }
  async findFilesJSON(args) {
    const {
      total,
      files
    } = await this.metadataClient.find(args);
    return {
      total,
      files: files.map(({
        id,
        metadata
      }) => (0, _file.toJSON)(id, metadata))
    };
  }
  async getUsageMetrics() {
    return this.metadataClient.getUsageMetrics({
      esFixedSizeIndex: {
        capacity: this.blobStorageService.getStaticBlobStorageSettings().esFixedSizeIndex.capacity
      }
    });
  }
  async getByToken(token) {
    const {
      fileId
    } = await this.fileShareService.getByToken(token);
    return this.get(fileId);
  }
  toFile(id, fileMetadata, fileKind, fileClient) {
    return new _file.File(id, (0, _file.toJSON)(id, fileMetadata), fileClient !== null && fileClient !== void 0 ? fileClient : this.createFileClient(fileKind), this.logger.get(`file-${id}`));
  }
  createFileClient(fileKindId) {
    const fileKind = this.fileKindRegistry.get(fileKindId);
    return (0, _file_client.createFileClient)({
      auditLogger: this.auditLogger,
      blobStorageClient: this.blobStorageService.createBlobStorageClient(fileKind.blobStoreSettings),
      fileKindDescriptor: fileKind,
      internalFileShareService: this.fileShareService,
      logger: this.logger,
      metadataClient: this.metadataClient
    });
  }
}
exports.InternalFileService = InternalFileService;