/**
 * ed25519 SSH keypair generation for per-workspace Coolify deploy keys.
 *
 * We generate once at provisioning time:
 *   - public key in OpenSSH format (for Gitea's SSH keys API)
 *   - private key in PKCS8 PEM (Coolify accepts this via /security/keys)
 *
 * Keys live in memory just long enough to be pushed to Gitea and Coolify;
 * neither is ever persisted by Vibn directly (Coolify holds the private
 * key, Gitea holds the public key). We keep the Coolify key uuid and the
 * Gitea key id on vibn_workspaces so we can rotate them later.
 */

import { generateKeyPairSync, createPublicKey, type KeyObject } from 'crypto';

export interface Ed25519Keypair {
  /** PKCS8 PEM — what Coolify's POST /security/keys wants. */
  privateKeyPem: string;
  /** OpenSSH public-key line: "ssh-ed25519 AAAA… <comment>" */
  publicKeyOpenSsh: string;
  /** SHA256 fingerprint string (without the trailing "=") */
  fingerprint: string;
}

export function generateEd25519Keypair(comment = ''): Ed25519Keypair {
  const { privateKey, publicKey } = generateKeyPairSync('ed25519', {
    privateKeyEncoding: { type: 'pkcs8', format: 'pem' },
    publicKeyEncoding: { type: 'spki', format: 'pem' },
  });
  const pubKeyObj = createPublicKey(publicKey);
  const publicKeyOpenSsh = ed25519PublicKeyToOpenSsh(pubKeyObj, comment);
  return {
    privateKeyPem: privateKey,
    publicKeyOpenSsh,
    fingerprint: opensshFingerprint(publicKeyOpenSsh),
  };
}

/**
 * Convert a Node ed25519 public KeyObject into an OpenSSH-format line.
 * Wire format for the base64 blob:
 *   uint32 len=11 | "ssh-ed25519" | uint32 len=32 | raw-pubkey-bytes
 */
function ed25519PublicKeyToOpenSsh(publicKey: KeyObject, comment: string): string {
  const jwk = publicKey.export({ format: 'jwk' }) as { x?: string };
  if (!jwk.x) {
    throw new Error('public key has no jwk.x component — not ed25519?');
  }
  const rawKey = Buffer.from(jwk.x, 'base64url');
  if (rawKey.length !== 32) {
    throw new Error(`expected 32-byte ed25519 pubkey, got ${rawKey.length}`);
  }
  const keyTypeBytes = Buffer.from('ssh-ed25519');
  const blob = Buffer.concat([
    uint32BE(keyTypeBytes.length),
    keyTypeBytes,
    uint32BE(rawKey.length),
    rawKey,
  ]).toString('base64');
  const tail = comment ? ` ${comment}` : '';
  return `ssh-ed25519 ${blob}${tail}`;
}

function uint32BE(n: number): Buffer {
  const b = Buffer.alloc(4);
  b.writeUInt32BE(n);
  return b;
}

/**
 * OpenSSH `ssh-keygen -lf`-style SHA256 fingerprint of a public key line.
 */
function opensshFingerprint(publicKeyLine: string): string {
  const b64 = publicKeyLine.trim().split(/\s+/)[1];
  if (!b64) return '';
  const raw = Buffer.from(b64, 'base64');
  const { createHash } = require('crypto') as typeof import('crypto');
  const fp = createHash('sha256').update(raw).digest('base64');
  return `SHA256:${fp.replace(/=+$/, '')}`;
}
