import droplet from "@drop-oss/droplet";
import type { CertificateStore } from "./ca-store";

export type CertificateBundle = {
  priv: string;
  cert: string;
};

/*
This is designed to handle client certificates, as described in the README.md
*/
export class CertificateAuthority {
  private certificateStore: CertificateStore;

  private root: CertificateBundle;

  constructor(store: CertificateStore, root: CertificateBundle) {
    this.certificateStore = store;
    this.root = root;
  }

  static async new(store: CertificateStore) {
    const root = await store.fetch("ca");
    let ca;
    if (root === undefined) {
      const [cert, priv] = droplet.generateRootCa();
      const bundle: CertificateBundle = { priv, cert };
      await store.store("ca", bundle);
      ca = new CertificateAuthority(store, bundle);
    } else {
      ca = new CertificateAuthority(store, root);
    }

    const serverCertificate = await ca.fetchClientCertificate("server");
    if (!serverCertificate) {
      await ca.generateClientCertificate("server", "Drop Server");
    }

    return ca;
  }

  async generateClientCertificate(clientId: string, clientName: string) {
    const caCertificate = await this.certificateStore.fetch("ca");
    if (!caCertificate)
      throw new Error("Certificate authority not initialised");
    const [cert, priv] = droplet.generateClientCertificate(
      clientId,
      clientName,
      caCertificate.cert,
      caCertificate.priv,
    );
    const certBundle: CertificateBundle = {
      priv,
      cert,
    };
    return certBundle;
  }

  async storeClientCertificate(clientId: string, bundle: CertificateBundle) {
    await this.certificateStore.store(`client:${clientId}`, bundle);
  }

  async fetchClientCertificate(clientId: string) {
    const isBlacklist = await this.certificateStore.checkBlacklistCertificate(
      `client:${clientId}`,
    );
    if (isBlacklist) return undefined;
    return await this.certificateStore.fetch(`client:${clientId}`);
  }

  async blacklistClient(clientId: string) {
    await this.certificateStore.blacklistCertificate(`client:${clientId}`);
  }
}
