import { isNil } from "lodash";
import vaults from "@server/database/vaults";
import Logger from "@server/logging/Logger";

const key = "sequelize:vault";

/**
 * A decorator that stores the encrypted vault for a particular database column
 * so that it can be used by getters and setters. Must be accompanied by a
 * @Column(DataType.BLOB) annotation.
 */
export default function Encrypted(target: any, propertyKey: string) {
  Reflect.defineMetadata(key, vaults().vault(propertyKey), target, propertyKey);
}

/**
 * Get the value of an encrypted column given the target and the property key.
 */
export function getEncryptedColumn(target: any, propertyKey: string): string {
  if (!target.getDataValue(propertyKey)) {
    return "";
  }
  try {
    return Reflect.getMetadata(key, target, propertyKey).get.call(target);
  } catch (err) {
    if (err.message.includes("Unexpected end of JSON input")) {
      return "";
    }
    if (err.message.includes("bad decrypt")) {
      Logger.error(
        `Failed to decrypt database column (${propertyKey}). The SECRET_KEY environment variable may have changed since installation.`,
        err
      );
      process.exit(1);
    }

    throw err;
  }
}

/**
 * Set the value of an encrypted column given the target and the property key.
 */
export function setEncryptedColumn(
  target: any,
  propertyKey: string,
  value: string
) {
  if (isNil(value)) {
    target.setDataValue(propertyKey, value);
  } else {
    Reflect.getMetadata(key, target, propertyKey).set.call(target, value);
  }
}
