import * as plugins from './plugins.js';
import { DockerHost } from './classes.host.js';
import { DockerResource } from './classes.base.js';

// interfaces
import * as interfaces from './interfaces/index.js';

export class DockerSecret extends DockerResource {
  // STATIC (Internal - prefixed with _ to indicate internal use)

  /**
   * Internal: Get all secrets
   * Public API: Use dockerHost.listSecrets() instead
   */
  public static async _list(dockerHostArg: DockerHost) {
    const response = await dockerHostArg.request('GET', '/secrets');
    const secrets: DockerSecret[] = [];
    for (const secret of response.body) {
      const dockerSecretInstance = new DockerSecret(dockerHostArg);
      Object.assign(dockerSecretInstance, secret);
      secrets.push(dockerSecretInstance);
    }
    return secrets;
  }

  /**
   * Internal: Get secret by ID
   * Public API: Use dockerHost.getSecretById(id) instead
   */
  public static async _fromId(dockerHostArg: DockerHost, idArg: string) {
    const secrets = await this._list(dockerHostArg);
    return secrets.find((secret) => secret.ID === idArg);
  }

  /**
   * Internal: Get secret by name
   * Public API: Use dockerHost.getSecretByName(name) instead
   */
  public static async _fromName(
    dockerHostArg: DockerHost,
    nameArg: string,
  ) {
    const secrets = await this._list(dockerHostArg);
    return secrets.find((secret) => secret.Spec.Name === nameArg);
  }

  /**
   * Internal: Create a secret
   * Public API: Use dockerHost.createSecret(descriptor) instead
   */
  public static async _create(
    dockerHostArg: DockerHost,
    secretDescriptor: interfaces.ISecretCreationDescriptor,
  ) {
    const labels: interfaces.TLabels = {
      ...secretDescriptor.labels,
      version: secretDescriptor.version,
    };
    const response = await dockerHostArg.request('POST', '/secrets/create', {
      Name: secretDescriptor.name,
      Labels: labels,
      Data: plugins.smartstring.base64.encode(secretDescriptor.contentArg),
    });

    const newSecretInstance = new DockerSecret(dockerHostArg);
    Object.assign(newSecretInstance, response.body);
    Object.assign(
      newSecretInstance,
      await DockerSecret._fromId(dockerHostArg, newSecretInstance.ID),
    );
    return newSecretInstance;
  }

  // INSTANCE PROPERTIES
  public ID!: string;
  public Spec!: {
    Name: string;
    Labels: interfaces.TLabels;
  };
  public Version!: {
    Index: string;
  };

  constructor(dockerHostArg: DockerHost) {
    super(dockerHostArg);
  }

  // INSTANCE METHODS

  /**
   * Refreshes this secret's state from the Docker daemon
   */
  public async refresh(): Promise<void> {
    const updated = await DockerSecret._fromId(this.dockerHost, this.ID);
    if (updated) {
      Object.assign(this, updated);
    }
  }

  /**
   * Updates a secret
   */
  public async update(contentArg: string) {
    const response = await this.dockerHost.request(
      'POST',
      `/secrets/${this.ID}/update?version=${this.Version.Index}`,
      {
        Name: this.Spec.Name,
        Labels: this.Spec.Labels,
        Data: plugins.smartstring.base64.encode(contentArg),
      },
    );
  }

  /**
   * Removes this secret from the Docker daemon
   */
  public async remove() {
    await this.dockerHost.request('DELETE', `/secrets/${this.ID}`);
  }

  /**
   * Gets the version label of this secret
   */
  public async getVersion() {
    return this.Spec.Labels.version;
  }
}
