import { MutableKeyStore } from '@app/_pgp/pgp-key-store';
import { LoggingService } from '@app/_services/logging.service';

const openpgp = require('openpgp');

interface Signable {
  digest(): string;
}

interface Signature {
  engine: string;
  algo: string;
  data: string;
  digest: string;
}

interface Signer {
  onsign(signature: Signature): void;
  onverify(flag: boolean): void;
  fingerprint(): string;
  prepare(material: Signable): boolean;
  verify(digest: string, signature: Signature): void;
  sign(digest: string): Promise<void>;
}

class PGPSigner implements Signer {
  engine = 'pgp';
  algo = 'sha256';
  dgst: string;
  signature: Signature;
  keyStore: MutableKeyStore;
  onsign: (signature: Signature) => void;
  onverify: (flag: boolean) => void;
  loggingService: LoggingService;

  constructor(keyStore: MutableKeyStore) {
    this.keyStore = keyStore;
    this.onsign = (signature: Signature) => {};
    this.onverify = (flag: boolean) => {};
  }

  public fingerprint(): string {
    return this.keyStore.getFingerprint();
  }

  public prepare(material: Signable): boolean {
    this.dgst = material.digest();
    return true;
  }

  public verify(digest: string, signature: Signature): void {
    openpgp.signature
      .readArmored(signature.data)
      .then((sig) => {
        const opts = {
          message: openpgp.cleartext.fromText(digest),
          publicKeys: this.keyStore.getTrustedKeys(),
          signature: sig,
        };
        openpgp.verify(opts).then((v) => {
          let i = 0;
          for (i = 0; i < v.signatures.length; i++) {
            const s = v.signatures[i];
            if (s.valid) {
              this.onverify(s);
              return;
            }
          }
          this.loggingService.sendErrorLevelMessage(
            `Checked ${i} signature(s) but none valid`,
            this,
            { error: '404 Not found!' }
          );
          this.onverify(false);
        });
      })
      .catch((e) => {
        this.loggingService.sendErrorLevelMessage(e.message, this, { error: e });
        this.onverify(false);
      });
  }

  public async sign(digest: string): Promise<void> {
    const m = openpgp.cleartext.fromText(digest);
    const pk = this.keyStore.getPrivateKey();
    if (!pk.isDecrypted()) {
      const password = window.prompt('password');
      await pk.decrypt(password);
    }
    const opts = {
      message: m,
      privateKeys: [pk],
      detached: true,
    };
    openpgp
      .sign(opts)
      .then((s) => {
        this.signature = {
          engine: this.engine,
          algo: this.algo,
          data: s.signature,
          // TODO: fix for browser later
          digest,
        };
        this.onsign(this.signature);
      })
      .catch((e) => {
        this.loggingService.sendErrorLevelMessage(e.message, this, { error: e });
        this.onsign(undefined);
      });
  }
}

export { Signable, Signature, Signer, PGPSigner };
