import { parseEntityRef, stringifyLocationRef } from '@backstage/catalog-model';
import { ResponseError } from '@backstage/errors';
import { CATALOG_FILTER_EXISTS } from './types/api.esm.js';
import { splitRefsIntoChunks, isQueryEntitiesInitialRequest } from './utils.esm.js';
import { DefaultApiClient } from './schema/openapi/generated/apis/Api.client.esm.js';

class CatalogClient {
  apiClient;
  constructor(options) {
    this.apiClient = new DefaultApiClient(options);
  }
  /**
   * {@inheritdoc CatalogApi.getEntityAncestors}
   */
  async getEntityAncestors(request, options) {
    return await this.requestRequired(
      await this.apiClient.getEntityAncestryByName(
        { path: parseEntityRef(request.entityRef) },
        options
      )
    );
  }
  /**
   * {@inheritdoc CatalogApi.getLocations}
   */
  async getLocations(request, options) {
    const res = await this.requestRequired(
      await this.apiClient.getLocations(request ?? {}, options)
    );
    return {
      items: res.map((item) => item.data)
    };
  }
  /**
   * {@inheritdoc CatalogApi.getLocationById}
   */
  async getLocationById(id, options) {
    return await this.requestOptional(
      await this.apiClient.getLocation({ path: { id } }, options)
    );
  }
  /**
   * {@inheritdoc CatalogApi.getLocationByEntity}
   */
  async getLocationByEntity(entityRef, options) {
    return await this.requestOptional(
      await this.apiClient.getLocationByEntity(
        { path: parseEntityRef(entityRef) },
        options
      )
    );
  }
  /**
   * {@inheritdoc CatalogApi.getEntities}
   */
  async getEntities(request, options) {
    const {
      filter = [],
      fields = [],
      order,
      offset,
      limit,
      after
    } = request ?? {};
    const encodedOrder = [];
    if (order) {
      for (const directive of [order].flat()) {
        if (directive) {
          encodedOrder.push(`${directive.order}:${directive.field}`);
        }
      }
    }
    const entities = await this.requestRequired(
      await this.apiClient.getEntities(
        {
          query: {
            fields,
            limit,
            filter: this.getFilterValue(filter),
            offset,
            after,
            order: order ? encodedOrder : void 0
          }
        },
        options
      )
    );
    return { items: entities };
  }
  /**
   * {@inheritdoc CatalogApi.getEntitiesByRefs}
   */
  async getEntitiesByRefs(request, options) {
    const getOneChunk = async (refs) => {
      const response = await this.apiClient.getEntitiesByRefs(
        {
          body: { entityRefs: refs, fields: request.fields },
          query: { filter: this.getFilterValue(request.filter) }
        },
        options
      );
      if (!response.ok) {
        throw await ResponseError.fromResponse(response);
      }
      const body = await response.json();
      return body.items.map((i) => i ?? void 0);
    };
    let result;
    for (const refs of splitRefsIntoChunks(request.entityRefs)) {
      const entities = await getOneChunk(refs);
      if (!result) {
        result = entities;
      } else {
        result.push(...entities);
      }
    }
    return { items: result ?? [] };
  }
  /**
   * {@inheritdoc CatalogApi.queryEntities}
   */
  async queryEntities(request = {}, options) {
    const params = {};
    if (isQueryEntitiesInitialRequest(request)) {
      const {
        fields = [],
        filter,
        limit,
        offset,
        orderFields,
        fullTextFilter
      } = request;
      params.filter = this.getFilterValue(filter);
      if (limit !== void 0) {
        params.limit = limit;
      }
      if (offset !== void 0) {
        params.offset = offset;
      }
      if (orderFields !== void 0) {
        params.orderField = (Array.isArray(orderFields) ? orderFields : [orderFields]).map(({ field, order }) => `${field},${order}`);
      }
      if (fields.length) {
        params.fields = fields;
      }
      const normalizedFullTextFilterTerm = fullTextFilter?.term?.trim();
      if (normalizedFullTextFilterTerm) {
        params.fullTextFilterTerm = normalizedFullTextFilterTerm;
      }
      if (fullTextFilter?.fields?.length) {
        params.fullTextFilterFields = fullTextFilter.fields;
      }
    } else {
      const { fields = [], limit, cursor } = request;
      params.cursor = cursor;
      if (limit !== void 0) {
        params.limit = limit;
      }
      if (fields.length) {
        params.fields = fields;
      }
    }
    return this.requestRequired(
      await this.apiClient.getEntitiesByQuery({ query: params }, options)
    );
  }
  /**
   * {@inheritdoc CatalogApi.getEntityByRef}
   */
  async getEntityByRef(entityRef, options) {
    return this.requestOptional(
      await this.apiClient.getEntityByName(
        {
          path: parseEntityRef(entityRef)
        },
        options
      )
    );
  }
  // NOTE(freben): When we deprecate getEntityByName from the interface, we may
  // still want to leave this implementation in place for quite some time
  // longer, to minimize the risk for breakages. Suggested date for removal:
  // August 2022
  /**
   * @deprecated Use getEntityByRef instead
   */
  async getEntityByName(compoundName, options) {
    const { kind, namespace = "default", name } = compoundName;
    return this.requestOptional(
      await this.apiClient.getEntityByName(
        { path: { kind, namespace, name } },
        options
      )
    );
  }
  /**
   * {@inheritdoc CatalogApi.refreshEntity}
   */
  async refreshEntity(entityRef, options) {
    const response = await this.apiClient.refreshEntity(
      { body: { entityRef } },
      options
    );
    if (response.status !== 200) {
      throw new Error(await response.text());
    }
  }
  /**
   * {@inheritdoc CatalogApi.getEntityFacets}
   */
  async getEntityFacets(request, options) {
    const { filter = [], facets } = request;
    return await this.requestOptional(
      await this.apiClient.getEntityFacets(
        {
          query: { facet: facets, filter: this.getFilterValue(filter) }
        },
        options
      )
    );
  }
  /**
   * {@inheritdoc CatalogApi.addLocation}
   */
  async addLocation(request, options) {
    const { type = "url", target, dryRun } = request;
    const response = await this.apiClient.createLocation(
      {
        body: { type, target },
        query: { dryRun: dryRun ? "true" : void 0 }
      },
      options
    );
    if (response.status !== 201) {
      throw new Error(await response.text());
    }
    const { location, entities, exists } = await response.json();
    if (!location) {
      throw new Error(`Location wasn't added: ${target}`);
    }
    return {
      location,
      entities,
      exists
    };
  }
  /**
   * {@inheritdoc CatalogApi.getLocationByRef}
   */
  async getLocationByRef(locationRef, options) {
    const all = await this.requestRequired(
      await this.apiClient.getLocations({}, options)
    );
    return all.map((r) => r.data).find((l) => locationRef === stringifyLocationRef(l));
  }
  /**
   * {@inheritdoc CatalogApi.removeLocationById}
   */
  async removeLocationById(id, options) {
    await this.requestIgnored(
      await this.apiClient.deleteLocation({ path: { id } }, options)
    );
  }
  /**
   * {@inheritdoc CatalogApi.removeEntityByUid}
   */
  async removeEntityByUid(uid, options) {
    await this.requestIgnored(
      await this.apiClient.deleteEntityByUid({ path: { uid } }, options)
    );
  }
  /**
   * {@inheritdoc CatalogApi.validateEntity}
   */
  async validateEntity(entity, locationRef, options) {
    const response = await this.apiClient.validateEntity(
      { body: { entity, location: locationRef } },
      options
    );
    if (response.ok) {
      return {
        valid: true
      };
    }
    if (response.status !== 400) {
      throw await ResponseError.fromResponse(response);
    }
    const { errors = [] } = await response.json();
    return {
      valid: false,
      errors
    };
  }
  //
  // Private methods
  //
  async requestIgnored(response) {
    if (!response.ok) {
      throw await ResponseError.fromResponse(response);
    }
  }
  async requestRequired(response) {
    if (!response.ok) {
      throw await ResponseError.fromResponse(response);
    }
    return response.json();
  }
  async requestOptional(response) {
    if (!response.ok) {
      if (response.status === 404) {
        return void 0;
      }
      throw await ResponseError.fromResponse(response);
    }
    return await response.json();
  }
  getFilterValue(filter = []) {
    const filters = [];
    for (const filterItem of [filter].flat()) {
      const filterParts = [];
      for (const [key, value] of Object.entries(filterItem)) {
        for (const v of [value].flat()) {
          if (v === CATALOG_FILTER_EXISTS) {
            filterParts.push(key);
          } else if (typeof v === "string") {
            filterParts.push(`${key}=${v}`);
          }
        }
      }
      if (filterParts.length) {
        filters.push(filterParts.join(","));
      }
    }
    return filters;
  }
}

export { CatalogClient };
//# sourceMappingURL=CatalogClient.esm.js.map
