import crypto from 'crypto';

import { SecretDataProps, Tag } from 'public/data/frequentInterfaces';

import getLatestFileKey from '@app/pages/api/workspace/getLatestFileKey';

import { decryptAssymmetric, encryptSymmetric } from '../cryptography/crypto';

interface EncryptedSecretProps {
  id: string;
  createdAt: string;
  environment: string;
  secretCommentCiphertext: string;
  secretCommentIV: string;
  secretCommentTag: string;
  secretKeyCiphertext: string;
  secretKeyIV: string;
  secretKeyTag: string;
  secretValueCiphertext: string;
  secretValueIV: string;
  secretValueTag: string;
  type: 'personal' | 'shared';
  tags: Tag[];
}

/**
 * Encypt secrets before pushing the to the DB
 * @param {object} obj
 * @param {object} obj.secretsToEncrypt - secrets that we want to encrypt
 * @param {object} obj.workspaceId - the id of a project in which we are encrypting secrets
 * @returns
 */
const encryptSecrets = async ({
  secretsToEncrypt,
  workspaceId,
  env
}: {
  secretsToEncrypt: SecretDataProps[];
  workspaceId: string;
  env: string;
}) => {
  let secrets;
  try {
    const sharedKey = await getLatestFileKey({ workspaceId });

    const PRIVATE_KEY = localStorage.getItem('PRIVATE_KEY') as string;

    let randomBytes: string;
    if (Object.keys(sharedKey).length > 0) {
      // case: a (shared) key exists for the workspace
      randomBytes = decryptAssymmetric({
        ciphertext: sharedKey.latestKey.encryptedKey,
        nonce: sharedKey.latestKey.nonce,
        publicKey: sharedKey.latestKey.sender.publicKey,
        privateKey: PRIVATE_KEY
      });
    } else {
      // case: a (shared) key does not exist for the workspace
      randomBytes = crypto.randomBytes(16).toString('hex');
    }

    secrets = secretsToEncrypt.map((secret) => {
      // encrypt key
      const {
        ciphertext: secretKeyCiphertext,
        iv: secretKeyIV,
        tag: secretKeyTag
      } = encryptSymmetric({
        plaintext: secret.key,
        key: randomBytes
      });

      // encrypt value
      const {
        ciphertext: secretValueCiphertext,
        iv: secretValueIV,
        tag: secretValueTag
      } = encryptSymmetric({
        plaintext: secret.value ?? '',
        key: randomBytes
      });

      // encrypt comment
      const {
        ciphertext: secretCommentCiphertext,
        iv: secretCommentIV,
        tag: secretCommentTag
      } = encryptSymmetric({
        plaintext: secret.comment ?? '',
        key: randomBytes
      });

      const result: EncryptedSecretProps = {
        id: secret.id,
        createdAt: '',
        environment: env,
        secretKeyCiphertext,
        secretKeyIV,
        secretKeyTag,
        secretValueCiphertext,
        secretValueIV,
        secretValueTag,
        secretCommentCiphertext,
        secretCommentIV,
        secretCommentTag,
        type:
          secret.valueOverride === undefined || secret?.value !== secret?.valueOverride
            ? 'shared'
            : 'personal',
        tags: secret.tags
      };

      return result;
    });
  } catch (error) {
    console.log('Error while encrypting secrets');
  }

  return secrets;
};

export default encryptSecrets;
