///////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2002-2026, 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-2026 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";
import { Endpoint } from "./Endpoint";

/**
 * Provides properties and methods for obtaining information about a server plugin on the Open Cloud
 * Server and managing its data.
 */
export class Plugin extends Endpoint {
  private _data: any;

  /**
   * @param data - Raw plugin data received from the server. For more information, see
   *   {@link https://cloud.opendesign.com/docs//pages/server/api.html#Plugin | Open Cloud Plugins API}.
   * @param httpClient - HTTP client instance used to send requests to the REST API server.
   */
  constructor(data: any, httpClient: IHttpClient) {
    super(`/plugins/${data.name}/${data.version}`, httpClient);
    this.data = data;
  }

  /**
   * Plugin author information. The `author` is an object with a `name` field and optionally `url` and
   * `email`. Or it can be shorten that all into a single string.
   *
   * @readonly
   */
  get author(): any {
    return this.data.author;
  }

  /**
   * Raw plugin data received from the server. For more information, see
   * {@link https://cloud.opendesign.com/docs//pages/server/api.html#Plugin | Open Cloud Plugins API}.
   */
  get data(): any {
    return this._data;
  }

  set data(value: any) {
    this._data = value;
  }

  /**
   * Short description of the plugin.
   *
   * @readonly
   */
  get description(): string {
    return this.data.description;
  }

  /**
   * Plugin state.
   *
   * @readonly
   */
  get enabled(): boolean {
    return this.data.enabled;
  }

  /**
   * The URL to the plugin homepage.
   *
   * @readonly
   */
  get homepage(): string {
    return this.data.homepage;
  }

  /**
   * Unique plugin ID.
   *
   * @readonly
   */
  get id(): string {
    return this.data.id;
  }

  /**
   * A license for the plugin.
   *
   * @readonly
   */
  get license(): string {
    return this.data.license;
  }

  /**
   * Plugin name.
   *
   * @readonly
   */
  get name(): string {
    return this.data.name;
  }

  /**
   * API permissions required.
   *
   * @readonly
   */
  get permissions(): string[] {
    return this.data.permissions;
  }

  /**
   * Plugin type. Can be set of:
   *
   * - `app` - Viewer plugin, the client‑side web app that the server hosts and serves as static content.
   * - `server` - Binary dll that extends server functionality.
   * - `jobrunner` - Binary dll that adds a new Job Runner on the server.
   *
   * @readonly
   */
  get pluginType(): string[] {
    return this.data.pluginType;
  }

  /**
   * {@link https://semver.org/ | SemVer} compatible version of the plugin.
   *
   * @readonly
   */
  get version(): number {
    return this.data.version;
  }

  /**
   * Reloads plugin data from the server.
   */
  async checkout(): Promise<this> {
    const response = await this.get("");
    this.data = await response.json();
    return this;
  }

  /**
   * Uninstalls and deletes a plugin from the server.
   *
   * @returns Returns the raw data of a deleted plugin. For more information, see
   *   {@link https://cloud.opendesign.com/docs//pages/server/api.html#Plugin | Open Cloud Plugins API}.
   */
  override delete(): Promise<any> {
    return super.delete("").then((response) => response.json());
  }

  /**
   * Enables a plugin.
   */
  async enable(): Promise<this> {
    const response = await this.put("/enable");
    this.data = await response.json();
    return this;
  }

  /**
   * Disables a plugin.
   */
  async disable(): Promise<this> {
    const response = await this.put("/disable");
    this.data = await response.json();
    return this;
  }

  /**
   * Downloads the plugins package from the server.
   *
   * @param onProgress - Download progress callback.
   * @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.
   */
  download(onProgress?: (progress: number) => void, signal?: AbortSignal): Promise<ArrayBuffer> {
    return this.httpClient
      .downloadFile(this.getEndpointPath("/download"), onProgress, { signal, headers: this.headers })
      .then((response) => response.arrayBuffer());
  }

  /**
   * Returns a plugin manfest.
   */
  getManifest(): Promise<any> {
    return this.get("/manifest").then((response) => response.json());
  }

  /**
   * Returns the plugin settings.
   *
   * @returns Returns an object with plugin settings.
   */
  getSettings(): Promise<any> {
    return this.get("/settings").then((response) => response.json());
  }

  /**
   * Changes the plugin settings.
   *
   * @param settings - An object with the new plugin settings or part of the settings.
   * @returns Returns an object with updated plugin settings.
   */
  updateSettings(settings: any): Promise<any> {
    return this.post("/settings", settings).then((response) => response.json());
  }

  /**
   * Executes a plugin command.
   *
   * This method executes the command for the current version of the plugin. To execute a command for the
   * latest installed version of the plugin, use the {@link Client.executePluginCommand}.
   *
   * @param command - Command to execute.
   * @param parameters - Command parameters. Command-dependent.
   */
  executeCommand(command: string, parameters?: BodyInit | object): Promise<any> {
    const commands = new Endpoint(`/plugins/${this.name}/commands`, this.httpClient, this.headers);
    return commands.post(`/${command}?version=${this.version}`, parameters).then((response) => response.json());
  }
}
