/**
 * Copyright IBM Corp. 2016, 2025
 * SPDX-License-Identifier: BUSL-1.1
 */

import Model, { attr } from '@ember-data/model';
import { set, get } from '@ember/object';
import clamp from 'vault/utils/clamp';
import lazyCapabilities, { apiPath } from 'vault/macros/lazy-capabilities';

const ACTION_VALUES = {
  encrypt: {
    isSupported: 'supportsEncryption',
    description: 'Looks up wrapping properties for the given token.',
    glyph: 'lock-fill',
  },
  decrypt: {
    isSupported: 'supportsDecryption',
    description: 'Decrypts the provided ciphertext using this key.',
    glyph: 'mail-open',
  },
  datakey: {
    isSupported: 'supportsEncryption',
    description: 'Generates a new key and value encrypted with this key.',
    glyph: 'key',
  },
  rewrap: {
    isSupported: 'supportsEncryption',
    description: 'Rewraps the ciphertext using the latest version of the named key.',
    glyph: 'reload',
  },
  sign: {
    isSupported: 'supportsSigning',
    description: 'Get the cryptographic signature of the given data.',
    glyph: 'pencil-tool',
  },
  hmac: {
    isSupported: true,
    description: 'Generate a data digest using a hash algorithm.',
    glyph: 'shuffle',
  },
  verify: {
    isSupported: true,
    description: 'Validate the provided signature for the given data.',
    glyph: 'check-circle',
  },
  export: {
    isSupported: 'exportable',
    description: 'Get the named key.',
    glyph: 'external-link',
  },
};

export default class TransitKeyModel extends Model {
  @attr('string') backend;
  @attr('string', {
    defaultValue: 'aes256-gcm96',
  })
  type;

  @attr('string', {
    label: 'Name',
    readOnly: true,
  })
  name;

  @attr({
    defaultValue: '0',
    defaultShown: 'Key is not automatically rotated',
    editType: 'ttl',
    label: 'Auto-rotation period',
  })
  autoRotatePeriod;

  @attr('boolean') deletionAllowed;
  @attr('boolean') derived;
  @attr('boolean') exportable;

  @attr('number', {
    defaultValue: 1,
  })
  minDecryptionVersion;

  @attr('number', {
    defaultValue: 0,
  })
  minEncryptionVersion;

  @attr('number') latestVersion;
  @attr('object') keys;
  @attr('boolean') convergentEncryption;
  @attr('number') convergentEncryptionVersion;

  @attr('boolean') supportsSigning;
  @attr('boolean') supportsEncryption;
  @attr('boolean') supportsDecryption;
  @attr('boolean') supportsDerivation;

  setConvergentEncryption(val) {
    if (val === true) {
      set(this, 'derived', val);
    }
    set(this, 'convergentEncryption', val);
  }

  setDerived(val) {
    if (val === false) {
      set(this, 'convergentEncryption', val);
    }
    set(this, 'derived', val);
  }

  get supportedActions() {
    return Object.keys(ACTION_VALUES)
      .filter((name) => {
        const { isSupported } = ACTION_VALUES[name];
        return typeof isSupported === 'boolean' || get(this, isSupported);
      })
      .map((name) => {
        const { description, glyph } = ACTION_VALUES[name];
        return { name, description, glyph };
      });
  }

  get keyVersions() {
    let maxVersion = Math.max(...this.validKeyVersions);
    const versions = [];
    while (maxVersion > 0) {
      versions.unshift(maxVersion);
      maxVersion--;
    }
    return versions;
  }

  get encryptionKeyVersions() {
    const { keyVersions, minDecryptionVersion } = this;

    return keyVersions
      .filter((version) => {
        return version >= minDecryptionVersion;
      })
      .reverse();
  }

  get keysForEncryption() {
    let { minEncryptionVersion, latestVersion } = this;
    const minVersion = clamp(minEncryptionVersion - 1, 0, latestVersion);
    const versions = [];
    while (latestVersion > minVersion) {
      versions.push(latestVersion);
      latestVersion--;
    }
    return versions;
  }

  get validKeyVersions() {
    return Object.keys(this.keys);
  }

  get exportKeyTypes() {
    const types = ['hmac'];
    if (this.supportsSigning) {
      types.unshift('signing');
    }
    if (this.supportsEncryption) {
      types.unshift('encryption');
    }
    return types;
  }
  @lazyCapabilities(apiPath`${'backend'}/keys/${'id'}/rotate`, 'backend', 'id') rotatePath;
  @lazyCapabilities(apiPath`${'backend'}/keys/${'id'}`, 'backend', 'id') secretPath;

  get canRotate() {
    return this.rotatePath.get('canUpdate') !== false;
  }
  get canRead() {
    return this.secretPath.get('canUpdate') !== false;
  }
  get canUpdate() {
    return this.secretPath.get('canUpdate') !== false;
  }
  get canDelete() {
    // there's more to just a permissions check here.
    // must also check if there's a property on the key called deletionAllowed that is set to true
    const deleteAttrChanged = Boolean(this.changedAttributes().deletionAllowed);
    const keyAllowedDeletion = this.deletionAllowed && deleteAttrChanged === false;
    return this.secretPath.get('canDelete') !== false && keyAllowedDeletion;
  }

  get canEdit() {
    return this.secretPath.get('canUpdate') !== false;
  }
}
