/**
 * Ed25519 key generation, persistence, and challenge signing for MCP agent auth.
 */
import * as ed from '@noble/ed25519'
import { sha512 } from '@noble/hashes/sha2.js'
import { readFile, writeFile, mkdir } from 'node:fs/promises'

// @noble/ed25519 v3 hash hook
ed.hashes.sha512 = sha512
ed.hashes.sha512Async = (m: Uint8Array) => Promise.resolve(sha512(m))
import { existsSync } from 'node:fs'
import { homedir } from 'node:os'
import { join, dirname } from 'node:path'

const DEFAULT_KEY_PATH = join(homedir(), '.abracadabra', 'agent.key')

function toBase64url(bytes: Uint8Array): string {
  return Buffer.from(bytes).toString('base64url')
}

function fromBase64url(b64: string): Uint8Array {
  return new Uint8Array(Buffer.from(b64, 'base64url'))
}

export interface AgentKeypair {
  privateKey: Uint8Array
  publicKeyB64: string
}

/**
 * Load an existing Ed25519 keypair from disk, or generate and persist a new one.
 * The file stores the raw 32-byte private key seed.
 */
export async function loadOrCreateKeypair(keyPath?: string): Promise<AgentKeypair> {
  const path = keyPath || DEFAULT_KEY_PATH

  if (existsSync(path)) {
    const seed = await readFile(path)
    if (seed.length !== 32) {
      throw new Error(`Invalid key file at ${path}: expected 32 bytes, got ${seed.length}`)
    }
    const privateKey = new Uint8Array(seed)
    const publicKey = ed.getPublicKey(privateKey)
    return { privateKey, publicKeyB64: toBase64url(publicKey) }
  }

  // Generate new keypair
  const privateKey = ed.utils.randomSecretKey()
  const publicKey = ed.getPublicKey(privateKey)

  // Ensure directory exists and write seed with restricted permissions
  const dir = dirname(path)
  if (!existsSync(dir)) {
    await mkdir(dir, { recursive: true, mode: 0o700 })
  }
  await writeFile(path, Buffer.from(privateKey), { mode: 0o600 })

  console.error(`[abracadabra-mcp] Generated new agent keypair at ${path}`)
  console.error(`[abracadabra-mcp] Public key: ${toBase64url(publicKey)}`)

  return { privateKey, publicKeyB64: toBase64url(publicKey) }
}

/** Sign a base64url challenge with the agent's private key; returns base64url signature. */
export function signChallenge(challengeB64: string, privateKey: Uint8Array): string {
  const challenge = fromBase64url(challengeB64)
  const sig = ed.sign(challenge, privateKey)
  return toBase64url(sig)
}
