"use strict";
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
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 __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
var rest_exports = {};
__export(rest_exports, {
  RestClientImpl: () => RestClientImpl,
  connectRest: () => connectRest,
  createRestClient: () => createRestClient
});
module.exports = __toCommonJS(rest_exports);
var import_core = require("@hcengineering/core");
var import_platform = require("@hcengineering/platform");
var import_utils = require("../utils");
var import_utils2 = require("./utils");
function createRestClient(endpoint, workspaceId, token) {
  return new RestClientImpl(endpoint, workspaceId, token);
}
__name(createRestClient, "createRestClient");
async function connectRest(url, options) {
  const { endpoint, token, workspaceId } = await (0, import_utils.getWorkspaceToken)(url, options);
  return createRestClient(endpoint, workspaceId, token);
}
__name(connectRest, "connectRest");
const rateLimitError = "rate-limit";
function isRLE(err) {
  return err.message === rateLimitError;
}
__name(isRLE, "isRLE");
class RestClientImpl {
  constructor(endpoint, workspace, token) {
    this.workspace = workspace;
    this.token = token;
    this.endpoint = endpoint.replace("ws", "http");
  }
  static {
    __name(this, "RestClientImpl");
  }
  endpoint;
  slowDownTimer = 0;
  currentRateLimit = { remaining: 1e3, limit: 1e3 };
  remaining = 1e3;
  limit = 1e3;
  jsonHeaders() {
    return {
      "Content-Type": "application/json",
      Authorization: "Bearer " + this.token,
      "accept-encoding": "snappy, gzip"
    };
  }
  requestInit() {
    return {
      method: "GET",
      keepalive: true,
      headers: this.jsonHeaders()
    };
  }
  async findAll(_class, query, options) {
    const params = new URLSearchParams();
    params.append("class", _class);
    if (query !== void 0 && Object.keys(query).length > 0) {
      params.append("query", JSON.stringify(query));
    }
    if (options !== void 0 && Object.keys(options).length > 0) {
      params.append("options", JSON.stringify(options));
    }
    const requestUrl = (0, import_core.concatLink)(this.endpoint, `/api/v1/find-all/${this.workspace}?${params.toString()}`);
    const result = await (0, import_utils2.withRetry)(async () => {
      const response = await fetch(requestUrl, this.requestInit());
      if (!response.ok) {
        await this.checkRateLimits(response);
        throw new import_platform.PlatformError((0, import_platform.unknownError)(response.statusText));
      }
      this.updateRateLimit(response);
      return await (0, import_utils2.extractJson)(response);
    }, isRLE);
    if (result.error !== void 0) {
      throw new import_platform.PlatformError(result.error);
    }
    if (result.lookupMap != null) {
      for (const d of result) {
        if (d.$lookup !== void 0) {
          for (const [k, v] of Object.entries(d.$lookup)) {
            if (!Array.isArray(v)) {
              ;
              d.$lookup[k] = result.lookupMap[v] ?? null;
            } else {
              ;
              d.$lookup[k] = v.map((it) => result.lookupMap?.[it]);
            }
          }
        }
      }
      delete result.lookupMap;
    }
    for (const doc of result) {
      if (doc._class == null) {
        doc._class = _class;
      }
      for (const [k, v] of Object.entries(query)) {
        if (typeof v === "string" || typeof v === "number" || typeof v === "boolean") {
          if (doc[k] == null) {
            ;
            doc[k] = v;
          }
        }
      }
    }
    return result;
  }
  async checkRate() {
    if (this.currentRateLimit.remaining < this.currentRateLimit.limit / 3) {
      if (this.slowDownTimer < 50) {
        this.slowDownTimer += 50;
      }
      this.slowDownTimer++;
    } else if (this.slowDownTimer > 0) {
      this.slowDownTimer--;
    }
    if (this.slowDownTimer > 0) {
      await new Promise((resolve) => setTimeout(resolve, this.slowDownTimer));
    }
  }
  updateRateLimit(response) {
    const rateLimitLimit = parseInt(response.headers.get("X-RateLimit-Limit") ?? "100");
    const remaining = parseInt(response.headers.get("X-RateLimit-Remaining") ?? "100");
    this.currentRateLimit = { remaining, limit: rateLimitLimit };
  }
  async checkRateLimits(response) {
    if (response.status === 429) {
      const retryAfter = response.headers.get("Retry-After");
      const retryAfterMS = response.headers.get("Retry-After-ms");
      const rateLimitReset = response.headers.get("X-RateLimit-Reset");
      this.updateRateLimit(response);
      const waitTime = (retryAfterMS != null ? parseInt(retryAfterMS) : void 0) ?? (retryAfter != null ? parseInt(retryAfter) * 1e3 : rateLimitReset != null ? new Date(parseInt(rateLimitReset)).getTime() - Date.now() : 1e3);
      await new Promise((resolve) => setTimeout(resolve, waitTime));
      throw new Error(rateLimitError);
    }
  }
  async getAccount() {
    const requestUrl = (0, import_core.concatLink)(this.endpoint, `/api/v1/account/${this.workspace}`);
    await this.checkRate();
    const result = await (0, import_utils2.withRetry)(async () => {
      const response = await fetch(requestUrl, this.requestInit());
      if (!response.ok) {
        await this.checkRateLimits(response);
        throw new import_platform.PlatformError((0, import_platform.unknownError)(response.statusText));
      }
      this.updateRateLimit(response);
      return await (0, import_utils2.extractJson)(response);
    });
    if (result.error !== void 0) {
      throw new import_platform.PlatformError(result.error);
    }
    return result;
  }
  async getModel(full = false) {
    const requestUrl = new URL((0, import_core.concatLink)(this.endpoint, `/api/v1/load-model/${this.workspace}`));
    if (full) {
      requestUrl.searchParams.append("full", "true");
    }
    await this.checkRate();
    const result = await (0, import_utils2.withRetry)(async () => {
      const response = await fetch(requestUrl, this.requestInit());
      if (!response.ok) {
        await this.checkRateLimits(response);
        throw new import_platform.PlatformError((0, import_platform.unknownError)(response.statusText));
      }
      this.updateRateLimit(response);
      const modelResponse = await (0, import_utils2.extractJson)(response);
      const hierarchy = new import_core.Hierarchy();
      const model = new import_core.ModelDb(hierarchy);
      const ctx = new import_core.MeasureMetricsContext("loadModel", {});
      (0, import_core.buildModel)(ctx, modelResponse, void 0, hierarchy, model);
      return { hierarchy, model };
    }, isRLE);
    if (result.error !== void 0) {
      throw new import_platform.PlatformError(result.error);
    }
    return result;
  }
  async findOne(_class, query, options) {
    return (await this.findAll(_class, query, { ...options, limit: 1 })).shift();
  }
  async tx(tx) {
    const requestUrl = (0, import_core.concatLink)(this.endpoint, `/api/v1/tx/${this.workspace}`);
    await this.checkRate();
    const result = await (0, import_utils2.withRetry)(async () => {
      const response = await fetch(requestUrl, {
        method: "POST",
        headers: this.jsonHeaders(),
        keepalive: true,
        body: JSON.stringify(tx)
      });
      if (!response.ok) {
        await this.checkRateLimits(response);
        throw new import_platform.PlatformError((0, import_platform.unknownError)(response.statusText));
      }
      this.updateRateLimit(response);
      return await (0, import_utils2.extractJson)(response);
    }, isRLE);
    if (result.error !== void 0) {
      throw new import_platform.PlatformError(result.error);
    }
    return result;
  }
  async searchFulltext(query, options) {
    const result = await (0, import_utils2.withRetry)(async () => {
      const params = new URLSearchParams();
      params.append("query", query.query);
      if (query.classes != null && Object.keys(query.classes).length > 0) {
        params.append("classes", JSON.stringify(query.classes));
      }
      if (query.spaces != null && Object.keys(query.spaces).length > 0) {
        params.append("spaces", JSON.stringify(query.spaces));
      }
      if (options.limit != null) {
        params.append("limit", `${options.limit}`);
      }
      const requestUrl = (0, import_core.concatLink)(this.endpoint, `/api/v1/search-fulltext/${this.workspace}?${params.toString()}`);
      const response = await fetch(requestUrl, {
        method: "GET",
        headers: this.jsonHeaders(),
        keepalive: true
      });
      if (!response.ok) {
        await this.checkRateLimits(response);
        throw new import_platform.PlatformError((0, import_platform.unknownError)(response.statusText));
      }
      this.updateRateLimit(response);
      return await (0, import_utils2.extractJson)(response);
    });
    if (result.error !== void 0) {
      throw new import_platform.PlatformError(result.error);
    }
    return result;
  }
  async domainRequest(domain, params, options) {
    const requestUrl = (0, import_core.concatLink)(this.endpoint, `/api/v1/request/${domain}/${this.workspace}`);
    await this.checkRate();
    return await (0, import_utils2.withRetry)(async () => {
      const response = await fetch(requestUrl, {
        method: "POST",
        headers: this.jsonHeaders(),
        keepalive: true,
        body: JSON.stringify(params)
      });
      if (!response.ok) {
        await this.checkRateLimits(response);
        throw new import_platform.PlatformError((0, import_platform.unknownError)(response.statusText));
      }
      this.updateRateLimit(response);
      const value = await (0, import_utils2.extractJson)(response);
      return { domain, value };
    }, isRLE);
  }
  async ensurePerson(socialType, socialValue, firstName, lastName) {
    const requestUrl = (0, import_core.concatLink)(this.endpoint, `/api/v1/ensure-person/${this.workspace}`);
    await this.checkRate();
    const result = await (0, import_utils2.withRetry)(async () => {
      const response = await fetch(requestUrl, {
        method: "POST",
        headers: this.jsonHeaders(),
        keepalive: true,
        body: JSON.stringify({
          socialType,
          socialValue,
          firstName,
          lastName
        })
      });
      if (!response.ok) {
        await this.checkRateLimits(response);
        throw new import_platform.PlatformError((0, import_platform.unknownError)(response.statusText));
      }
      this.updateRateLimit(response);
      return await (0, import_utils2.extractJson)(response);
    }, isRLE);
    if (result.error !== void 0) {
      throw new import_platform.PlatformError(result.error);
    }
    return result;
  }
}
//# sourceMappingURL=rest.js.map
