import forge from 'node-forge';

export interface GeneratedCertificate {
  certificatePem: string;
  privateKeyPem: string;
}

export interface Pkcs12Identity {
  certificatePem: string;
  privateKeyPem: string;
}

function randomSerialNumber(): string {
  const bytes = forge.random.getBytesSync(16).split('');
  const firstByte = bytes[0]?.charCodeAt(0) ?? 1;
  bytes[0] = String.fromCharCode(firstByte & 0x7f || 1);
  return forge.util.bytesToHex(bytes.join(''));
}

export function createSelfSignedServerCertificate(
  commonName: string,
  altNames: string[],
  validityDays = 30
): GeneratedCertificate {
  const keypair = forge.pki.rsa.generateKeyPair({ bits: 2048 });
  const cert = forge.pki.createCertificate();

  cert.publicKey = keypair.publicKey;
  cert.serialNumber = randomSerialNumber();
  cert.validity.notBefore = new Date();
  cert.validity.notAfter = new Date();
  cert.validity.notAfter.setDate(cert.validity.notBefore.getDate() + validityDays);

  const subject = [
    { name: 'commonName', value: commonName },
    { name: 'organizationName', value: 'Caddy Proxy Manager E2E' },
  ];

  cert.setSubject(subject);
  cert.setIssuer(subject);
  cert.setExtensions([
    { name: 'basicConstraints', cA: false },
    { name: 'keyUsage', digitalSignature: true, keyEncipherment: true, critical: true },
    { name: 'extKeyUsage', serverAuth: true },
    {
      name: 'subjectAltName',
      altNames: altNames.map((value) => ({ type: 2, value })),
    },
  ]);

  cert.sign(keypair.privateKey, forge.md.sha256.create());

  return {
    certificatePem: forge.pki.certificateToPem(cert),
    privateKeyPem: forge.pki.privateKeyToPem(keypair.privateKey),
  };
}

export function parsePkcs12Identity(bundle: Buffer, password: string): Pkcs12Identity {
  const der = forge.util.createBuffer(bundle.toString('binary'));
  const p12Asn1 = forge.asn1.fromDer(der);
  const p12 = forge.pkcs12.pkcs12FromAsn1(p12Asn1, password);

  const keyBags = p12.getBags({
    bagType: forge.pki.oids.pkcs8ShroudedKeyBag,
  })[forge.pki.oids.pkcs8ShroudedKeyBag] ?? [];
  const key = keyBags[0]?.key;
  if (!key) {
    throw new Error('PKCS#12 bundle did not contain a private key');
  }

  const certBags = p12.getBags({
    bagType: forge.pki.oids.certBag,
  })[forge.pki.oids.certBag] ?? [];
  const certBag = certBags.find((bag) => {
    const extKeyUsage = bag.cert?.getExtension('extKeyUsage');
    const basicConstraints = bag.cert?.getExtension('basicConstraints');
    const isClientCert = Boolean(extKeyUsage && 'clientAuth' in extKeyUsage && extKeyUsage.clientAuth);
    const isCa = Boolean(basicConstraints && 'cA' in basicConstraints && basicConstraints.cA);
    return isClientCert || !isCa;
  }) ?? certBags[0];

  if (!certBag?.cert) {
    throw new Error('PKCS#12 bundle did not contain a certificate');
  }

  return {
    certificatePem: forge.pki.certificateToPem(certBag.cert),
    privateKeyPem: forge.pki.privateKeyToPem(key),
  };
}
