/**
 * Ed25519 keypair: generate, store in localStorage, sign requests.
 * Public key exported as SPKI base64url for server verification.
 */

const STORAGE_KEY_PUBLIC = "builazoo_public_key";
const STORAGE_KEY_PRIVATE = "builazoo_private_key";

/**
 * @returns {Promise<CryptoKeyPair | null>}
 */
function generateKeyPair() {
  return crypto.subtle.generateKey(
    { name: "Ed25519" },
    true,
    ["sign", "verify"]
  );
}

/**
 * @param {ArrayBuffer} buf
 * @returns {string}
 */
function base64url(buf) {
  const b64 = btoa(String.fromCharCode(...new Uint8Array(buf)));
  return b64.replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
}

/**
 * @returns {Promise<{ publicKeyBase64: string, privateKey: CryptoKey } | null>}
 */
export async function getOrCreateKeyPair() {
  if (typeof crypto === "undefined" || !crypto.subtle) {
    throw new Error("Connexion sécurisée requise (HTTPS ou localhost) pour créer un compte.");
  }
  try {
    const pubRaw = localStorage.getItem(STORAGE_KEY_PUBLIC);
    const privRaw = localStorage.getItem(STORAGE_KEY_PRIVATE);
    if (pubRaw && privRaw) {
      const privateKey = await crypto.subtle.importKey(
        "pkcs8",
      base64urlDecodeToBuf(privRaw),
        { name: "Ed25519" },
        true,
        ["sign"]
      );
      await crypto.subtle.importKey(
        "spki",
        base64urlDecodeToBuf(pubRaw),
        { name: "Ed25519" },
        true,
        ["verify"]
      );
      return { publicKeyBase64: pubRaw, privateKey };
    }
    const pair = await generateKeyPair();
    const [pubExported, privExported] = await Promise.all([
      crypto.subtle.exportKey("spki", pair.publicKey),
      crypto.subtle.exportKey("pkcs8", pair.privateKey),
    ]);
    localStorage.setItem(STORAGE_KEY_PUBLIC, base64url(pubExported));
    localStorage.setItem(STORAGE_KEY_PRIVATE, base64url(privExported));
    return { publicKeyBase64: base64url(pubExported), privateKey: pair.privateKey };
  } catch (e) {
    console.warn("getOrCreateKeyPair failed", e);
    return null;
  }
}

/**
 * @param {string} str base64url
 * @returns {ArrayBuffer}
 */
function base64urlDecodeToBuf(str) {
  const pad = (4 - (str.length % 4)) % 4;
  const b64 = (str + "==".slice(0, pad)).replace(/-/g, "+").replace(/_/g, "/");
  const binary = atob(b64);
  const buf = new Uint8Array(binary.length);
  for (let i = 0; i < binary.length; i++) buf[i] = binary.charCodeAt(i);
  return buf.buffer;
}

/**
 * @param {CryptoKey} privateKey
 * @param {string} message
 * @returns {Promise<string>} base64url signature
 */
export async function signMessage(privateKey, message) {
  const enc = new TextEncoder().encode(message);
  const sig = await crypto.subtle.sign("Ed25519", privateKey, enc);
  return base64url(sig);
}

/**
 * Call after register to ensure we have keys stored (already done by getOrCreateKeyPair).
 * @returns {boolean} true if keys exist
 */
export function hasStoredKeys() {
  return Boolean(localStorage.getItem(STORAGE_KEY_PUBLIC) && localStorage.getItem(STORAGE_KEY_PRIVATE));
}
