///////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2002-2025, Open Design Alliance (the "Alliance").
// All rights reserved.
//
// This software and its documentation and related materials are owned by
// the Alliance. The software may only be incorporated into application
// programs owned by members of the Alliance, subject to a signed
// Membership Agreement and Supplemental Software License Agreement with the
// Alliance. The structure and organization of this software are the valuable
// trade secrets of the Alliance and its suppliers. The software is also
// protected by copyright law and international treaty provisions. Application
// programs incorporating this software must include the following statement
// with their copyright notices:
//
//   This application incorporates Open Design Alliance software pursuant to a
//   license agreement with Open Design Alliance.
//   Open Design Alliance Copyright (C) 2002-2025 by Open Design Alliance.
//   All rights reserved.
//
// By use of this software, its documentation or related materials, you
// acknowledge and accept the above terms.
///////////////////////////////////////////////////////////////////////////////

import { IHttpClient } from "./IHttpClient";

/**
 * Base class for the REST API endpoints.
 */
export class Endpoint {
  /**
   * Endpoint API path relative to the REST API server URL.
   */
  public path: string;

  /**
   * Endpoint-specific HTTP headers for the `GET`, `POST`, `PUT` and `DELETE` requests. You can add
   * custom headers at any time.
   */
  public headers: HeadersInit;

  public httpClient: IHttpClient;
  private _useVersion: number | undefined;

  /**
   * @ignore
   * @param path - The API path of the endpoint relative to the REST API server URL of the specified HTTP
   *   client.
   * @param httpClient - HTTP client instance used to send requests to the REST API server.
   * @param headers - Endpoint-specific HTTP headers.
   */
  constructor(path: string, httpClient: IHttpClient, headers = {}) {
    this.path = path;
    this.httpClient = httpClient;
    this.headers = headers;
  }

  appendVersionParam(relativePath: string): string {
    if (this._useVersion === undefined) return relativePath;
    const delimiter = relativePath.includes("?") ? "&" : "?";
    return `${relativePath}${delimiter}version=${this._useVersion}`;
  }

  /**
   * Returns the endpoint API path.
   *
   * @ignore
   * @param relativePath - Nested endpoint relative path.
   */
  getEndpointPath(relativePath: string): string {
    return this.appendVersionParam(`${this.path}${relativePath}`);
  }

  /**
   * Sends the `GET` request to the endpoint.
   *
   * @ignore
   * @param relativePath - Nested endpoint relative path.
   * @param signal - An
   *   {@link https://developer.mozilla.org/docs/Web/API/AbortController | AbortController} signal. Allows
   *   to communicate with a fetch request and abort it if desired.
   */
  get(relativePath: string, signal?: AbortSignal): Promise<Response> {
    return this.httpClient.get(this.getEndpointPath(relativePath), { signal, headers: this.headers });
  }

  /**
   * Sends the `POST` request to the endpoint.
   *
   * @ignore
   * @param relativePath - Nested endpoint relative path.
   * @param body - Request body. Can be
   *   {@link https://developer.mozilla.org/docs/Web/API/FormData | FormData},
   *   {@link https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/ArrayBuffer | ArrayBuffer},
   *   {@link https://developer.mozilla.org/docs/Web/API/Blob/Blob | Blob}, JSON object or plain text.
   */
  post(relativePath: string, body?: BodyInit | object): Promise<Response> {
    return this.httpClient.post(this.getEndpointPath(relativePath), body, { headers: this.headers });
  }

  /**
   * Sends the `PUT` request to the endpoint.
   *
   * @ignore
   * @param relativePath - Nested endpoint relative path.
   * @param body - Request body. Can be
   *   {@link https://developer.mozilla.org/docs/Web/API/FormData | FormData},
   *   {@link https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/ArrayBuffer | ArrayBuffer},
   *   {@link https://developer.mozilla.org/docs/Web/API/Blob/Blob | Blob}, JSON object or plain text.
   */
  put(relativePath: string, body?: BodyInit | object): Promise<Response> {
    return this.httpClient.put(this.getEndpointPath(relativePath), body, { headers: this.headers });
  }

  /**
   * Sends the `DELETE` request to the endpoint.
   *
   * @ignore
   * @param relativePath - Nested endpoint relative path.
   */
  delete(relativePath: string): Promise<Response> {
    return this.httpClient.delete(this.getEndpointPath(relativePath), { headers: this.headers });
  }

  // Internal: append the `version` param to the endpoint requests.

  useVersion(version?: number): this {
    this._useVersion = version;
    return this;
  }
}
