import {
  generateKeyPair,
  generateSelfSignedCodeSigningCertificate,
  convertCertificateToCertificatePEM,
  convertKeyPairToPEM,
} from '@expo/code-signing-certificates';
import assert from 'assert';
import { promises as fs } from 'fs';
import path from 'path';

import { ensureDirAsync } from './utils/dir';
import { log } from './utils/log';

type Options = {
  certificateValidityDurationYears: number;
  keyOutput: string;
  certificateOutput: string;
  certificateCommonName: string;
};

export async function generateCodeSigningAsync(
  projectRoot: string,
  { certificateValidityDurationYears, keyOutput, certificateOutput, certificateCommonName }: Options
) {
  const validityDurationYears = Math.floor(certificateValidityDurationYears);

  const certificateOutputDir = path.resolve(projectRoot, certificateOutput);
  const keyOutputDir = path.resolve(projectRoot, keyOutput);
  await Promise.all([ensureDirAsync(certificateOutputDir), ensureDirAsync(keyOutputDir)]);

  const [certificateOutputDirContents, keyOutputDirContents] = await Promise.all([
    fs.readdir(certificateOutputDir),
    fs.readdir(keyOutputDir),
  ]);
  assert(certificateOutputDirContents.length === 0, 'Certificate output directory must be empty');
  assert(keyOutputDirContents.length === 0, 'Key output directory must be empty');

  const keyPair = generateKeyPair();
  const validityNotBefore = new Date();
  const validityNotAfter = new Date();
  validityNotAfter.setFullYear(validityNotAfter.getFullYear() + validityDurationYears);
  const certificate = generateSelfSignedCodeSigningCertificate({
    keyPair,
    validityNotBefore,
    validityNotAfter,
    commonName: certificateCommonName,
  });

  const keyPairPEM = convertKeyPairToPEM(keyPair);
  const certificatePEM = convertCertificateToCertificatePEM(certificate);

  await Promise.all([
    fs.writeFile(path.join(keyOutputDir, 'public-key.pem'), keyPairPEM.publicKeyPEM),
    fs.writeFile(path.join(keyOutputDir, 'private-key.pem'), keyPairPEM.privateKeyPEM),
    fs.writeFile(path.join(certificateOutputDir, 'certificate.pem'), certificatePEM),
  ]);

  log(
    `Generated public and private keys output in ${keyOutputDir}. Remember to add them to .gitignore or to encrypt them. (e.g. with git-crypt)`
  );
  log(`Generated code signing certificate output in ${certificateOutputDir}.`);
  log(
    `To automatically configure this project for code signing, run \`yarn expo-updates codesigning:configure --certificate-input-directory=${certificateOutput} --key-input-directory=${keyOutput}\`.`
  );
}
