import { readFileSync, existsSync } from "fs";

/** First PEM block label, e.g. "CERTIFICATE", "PRIVATE KEY", "RSA PRIVATE KEY". */
export function firstPemLabel(pem: string): string | undefined {
  const m = pem.match(/-----BEGIN ([^-]+)-----/);
  return m?.[1]?.trim();
}

export function isPrivateKeyPemLabel(label: string | undefined): boolean {
  if (!label) return false;
  return label.toUpperCase().includes("PRIVATE KEY");
}

export function isCertificatePemLabel(label: string | undefined): boolean {
  if (!label) return false;
  return label.toUpperCase().includes("CERTIFICATE");
}

function assertKeyNotEncrypted(pem: string, pathForError: string): void {
  if (pem.includes("Proc-Type: 4,ENCRYPTED")) {
    throw new Error(
      `TLS private key "${pathForError}" is passphrase-encrypted. Node's https server needs an unencrypted ` +
        `key, or use a reverse proxy for TLS. Decrypt with: openssl rsa -in encrypted.key -out server.key`
    );
  }
}

/**
 * Loads key + cert (+ optional CA chain) for https.createServer.
 * - Handles KEY/CERT swapped (cert in first file, private key in second).
 * - Handles common cPanel mistakes: KEY=domain .crt, CERT=chain .crt, CA= .key — private key is read from CA path.
 */
export function loadTlsCredentials(
  keyPath: string,
  certPath: string,
  caPath?: string
): { key: Buffer; cert: Buffer; ca?: Buffer } {
  let keyPem = readFileSync(keyPath, "utf8");
  let certPem = readFileSync(certPath, "utf8");
  const caPemFile =
    caPath && existsSync(caPath) ? readFileSync(caPath, "utf8") : undefined;

  let keyLabel = firstPemLabel(keyPem);
  let certLabel = firstPemLabel(certPem);

  // Swapped pair: first file is certificate, second is private key
  if (isCertificatePemLabel(keyLabel) && isPrivateKeyPemLabel(certLabel)) {
    [keyPem, certPem] = [certPem, keyPem];
    keyLabel = firstPemLabel(keyPem);
    certLabel = firstPemLabel(certPem);
  }

  // cPanel-style: "KEY" is leaf cert, "CERT" is chain, real private key under CA/SSL_CA_PATH
  if (isCertificatePemLabel(keyLabel) && caPemFile) {
    const caLabel = firstPemLabel(caPemFile);
    if (isPrivateKeyPemLabel(caLabel)) {
      const leafCertPem = keyPem;
      assertKeyNotEncrypted(caPemFile, caPath!);
      const chainLabel = firstPemLabel(certPem);
      const includeChain =
        isCertificatePemLabel(chainLabel) && certPem.trim() !== leafCertPem.trim();

      console.warn(
        "TLS: Using cPanel-style layout (private key from CA/SSL_CA_PATH, domain cert from KEY, chain from CERT where applicable). " +
          "Prefer SSL_KEY_PATH=.key, SSL_CERT_PATH=domain.crt, SSL_CA_PATH=chain.crt."
      );

      return {
        key: Buffer.from(caPemFile, "utf8"),
        cert: Buffer.from(leafCertPem, "utf8"),
        ...(includeChain ? { ca: Buffer.from(certPem, "utf8") } : {}),
      };
    }
  }

  const keyIsPk = isPrivateKeyPemLabel(keyLabel);
  const certIsCert = isCertificatePemLabel(certLabel);

  if (!keyIsPk) {
    throw new Error(
      `TLS private key file must be a PEM private key (BEGIN PRIVATE KEY / RSA PRIVATE KEY). ` +
        `"${keyPath}" starts with: ${keyLabel ? `BEGIN ${keyLabel}` : "unknown / empty"}. ` +
        `Do not point KEY/SSL_KEY_PATH at the .crt file unless the private key is in SSL_CA_PATH/CA (cPanel-style). ` +
        `Otherwise set KEY to your .key file.`
    );
  }

  if (!certIsCert) {
    throw new Error(
      `TLS certificate file must be a PEM certificate (BEGIN CERTIFICATE). ` +
        `"${certPath}" starts with: ${certLabel ? `BEGIN ${certLabel}` : "unknown / empty"}.`
    );
  }

  assertKeyNotEncrypted(keyPem, keyPath);

  const out: { key: Buffer; cert: Buffer; ca?: Buffer } = {
    key: Buffer.from(keyPem, "utf8"),
    cert: Buffer.from(certPem, "utf8"),
  };

  if (caPemFile && caPath) {
    const caLabel = firstPemLabel(caPemFile);
    if (isCertificatePemLabel(caLabel)) {
      out.ca = Buffer.from(caPemFile, "utf8");
    }
  }

  return out;
}
