/**
 * End-to-End Encryption utilities using Web Crypto API
 * Uses ECDH for key exchange and AES-GCM for message encryption
 */

const STORAGE_KEY = "sociowire:e2e:keypair";
const PEER_KEYS_KEY = "sociowire:e2e:peerKeys";

// Generate or retrieve the user's key pair
export async function getOrCreateKeyPair() {
  try {
    const stored = localStorage.getItem(STORAGE_KEY);
    if (stored) {
      const { publicKey, privateKey } = JSON.parse(stored);
      return {
        publicKey: await importPublicKey(publicKey),
        privateKey: await importPrivateKey(privateKey),
        publicKeyExport: publicKey,
      };
    }
  } catch (e) {
    console.warn("[E2E] Failed to load stored keys, generating new ones", e);
  }

  // Generate new ECDH key pair
  const keyPair = await crypto.subtle.generateKey(
    { name: "ECDH", namedCurve: "P-256" },
    true,
    ["deriveBits"]
  );

  // Export keys for storage
  const publicKeyExport = await exportPublicKey(keyPair.publicKey);
  const privateKeyExport = await exportPrivateKey(keyPair.privateKey);

  // Store in localStorage
  localStorage.setItem(STORAGE_KEY, JSON.stringify({
    publicKey: publicKeyExport,
    privateKey: privateKeyExport,
  }));

  return {
    publicKey: keyPair.publicKey,
    privateKey: keyPair.privateKey,
    publicKeyExport,
  };
}

// Export public key to base64
async function exportPublicKey(key) {
  const exported = await crypto.subtle.exportKey("spki", key);
  return btoa(String.fromCharCode(...new Uint8Array(exported)));
}

// Export private key to base64
async function exportPrivateKey(key) {
  const exported = await crypto.subtle.exportKey("pkcs8", key);
  return btoa(String.fromCharCode(...new Uint8Array(exported)));
}

// Import public key from base64
async function importPublicKey(base64) {
  const binary = Uint8Array.from(atob(base64), c => c.charCodeAt(0));
  return crypto.subtle.importKey(
    "spki",
    binary,
    { name: "ECDH", namedCurve: "P-256" },
    true,
    []
  );
}

// Import private key from base64
async function importPrivateKey(base64) {
  const binary = Uint8Array.from(atob(base64), c => c.charCodeAt(0));
  return crypto.subtle.importKey(
    "pkcs8",
    binary,
    { name: "ECDH", namedCurve: "P-256" },
    true,
    ["deriveBits"]
  );
}

// Store peer's public key
export function storePeerPublicKey(username, publicKeyBase64) {
  try {
    const peerKeys = JSON.parse(localStorage.getItem(PEER_KEYS_KEY) || "{}");
    peerKeys[username] = publicKeyBase64;
    localStorage.setItem(PEER_KEYS_KEY, JSON.stringify(peerKeys));
  } catch (e) {
    console.warn("[E2E] Failed to store peer key", e);
  }
}

// Get peer's public key
export function getPeerPublicKey(username) {
  try {
    const peerKeys = JSON.parse(localStorage.getItem(PEER_KEYS_KEY) || "{}");
    return peerKeys[username] || null;
  } catch (e) {
    return null;
  }
}

// Derive shared secret from own private key and peer's public key
async function deriveSharedKey(privateKey, peerPublicKeyBase64) {
  const peerPublicKey = await importPublicKey(peerPublicKeyBase64);

  // Derive shared bits using ECDH
  const sharedBits = await crypto.subtle.deriveBits(
    { name: "ECDH", public: peerPublicKey },
    privateKey,
    256
  );

  // Import as AES-GCM key
  return crypto.subtle.importKey(
    "raw",
    sharedBits,
    { name: "AES-GCM", length: 256 },
    false,
    ["encrypt", "decrypt"]
  );
}

// Encrypt a message
export async function encryptMessage(privateKey, peerPublicKeyBase64, plaintext) {
  try {
    const sharedKey = await deriveSharedKey(privateKey, peerPublicKeyBase64);

    // Generate random IV
    const iv = crypto.getRandomValues(new Uint8Array(12));

    // Encrypt
    const encoder = new TextEncoder();
    const encrypted = await crypto.subtle.encrypt(
      { name: "AES-GCM", iv },
      sharedKey,
      encoder.encode(plaintext)
    );

    // Combine IV + ciphertext and encode as base64
    const combined = new Uint8Array(iv.length + encrypted.byteLength);
    combined.set(iv);
    combined.set(new Uint8Array(encrypted), iv.length);

    return "e2e:" + btoa(String.fromCharCode(...combined));
  } catch (e) {
    console.error("[E2E] Encryption failed", e);
    return plaintext; // Fallback to plaintext
  }
}

// Decrypt a message
export async function decryptMessage(privateKey, peerPublicKeyBase64, ciphertext) {
  try {
    // Check if message is encrypted
    if (!ciphertext.startsWith("e2e:")) {
      return ciphertext; // Not encrypted
    }

    const sharedKey = await deriveSharedKey(privateKey, peerPublicKeyBase64);

    // Decode base64
    const combined = Uint8Array.from(atob(ciphertext.slice(4)), c => c.charCodeAt(0));

    // Extract IV and ciphertext
    const iv = combined.slice(0, 12);
    const encrypted = combined.slice(12);

    // Decrypt
    const decrypted = await crypto.subtle.decrypt(
      { name: "AES-GCM", iv },
      sharedKey,
      encrypted
    );

    const decoder = new TextDecoder();
    return decoder.decode(decrypted);
  } catch (e) {
    console.error("[E2E] Decryption failed", e);
    return "[Encrypted message - decryption failed]";
  }
}

// Check if E2E is available (Web Crypto API support)
export function isE2EAvailable() {
  return typeof crypto !== "undefined" &&
         typeof crypto.subtle !== "undefined" &&
         typeof crypto.subtle.generateKey === "function";
}
