// @ts-strict-ignore
import { v4 as uuidv4 } from 'uuid';

import * as internals from './encryption-internals';

// A map of all possible master encryption keys to use, keyed by
// unique id
let keys = {};

class Key {
  id;
  value;

  constructor({ id }) {
    this.id = id || uuidv4();
  }

  async createFromPassword({ password, salt }) {
    this.value = await internals.createKey({ secret: password, salt });
  }

  async createFromBase64(str) {
    this.value = await internals.importKey(str);
  }

  getId() {
    return this.id;
  }

  getValue() {
    return this.value;
  }

  serialize() {
    return {
      id: this.id,
      base64: this.value.base64,
    };
  }
}

export function getKey(keyId) {
  if (keyId == null || keys[keyId] == null) {
    throw new Error('missing-key');
  }
  return keys[keyId];
}

export function hasKey(keyId) {
  return keyId in keys;
}

export function encrypt(value, keyId) {
  return internals.encrypt(getKey(keyId), value);
}

export function decrypt(encrypted, meta) {
  return internals.decrypt(getKey(meta.keyId), encrypted, meta);
}

export function randomBytes(n) {
  return internals.randomBytes(n);
}

export async function loadKey(key) {
  let keyInstance;
  if (!(key instanceof Key)) {
    keyInstance = new Key({ id: key.id });
    await keyInstance.createFromBase64(key.base64);
  } else {
    keyInstance = key;
  }

  keys[keyInstance.getId()] = keyInstance;
}

export function unloadKey(key) {
  delete keys[key.getId()];
}

export function unloadAllKeys() {
  keys = {};
}

export async function createKey({ id, password, salt }) {
  const key = new Key({ id });
  await key.createFromPassword({ password, salt });
  return key;
}
