function getCryptoApi() {
  const api = globalThis.crypto;
  if (!api?.subtle || typeof api.getRandomValues !== 'function') {
    throw new Error('WebCrypto недоступен в текущем браузере.');
  }
  return api;
}

function getSubtleApi() {
  return getCryptoApi().subtle;
}

function base64UrlToBase64(value) {
  const normalized = String(value || '').trim().replace(/-/g, '+').replace(/_/g, '/');
  return normalized + '='.repeat((4 - (normalized.length % 4)) % 4);
}

export function utf8Bytes(value) {
  return new TextEncoder().encode(String(value ?? ''));
}

export function bytesToBase64(bytes) {
  let binary = '';
  const chunk = 0x8000;
  for (let i = 0; i < bytes.length; i += chunk) {
    const slice = bytes.subarray(i, i + chunk);
    binary += String.fromCharCode(...slice);
  }
  return btoa(binary);
}

export function base64ToBytes(value) {
  const binary = atob(base64UrlToBase64(value));
  const out = new Uint8Array(binary.length);
  for (let i = 0; i < binary.length; i += 1) out[i] = binary.charCodeAt(i);
  return out;
}

export async function generateEd25519Pair() {
  return getSubtleApi().generateKey({ name: 'Ed25519' }, true, ['sign', 'verify']);
}

export async function exportEd25519PublicKeyB64(publicKey) {
  const raw = await getSubtleApi().exportKey('raw', publicKey);
  return bytesToBase64(new Uint8Array(raw));
}

export async function exportPkcs8B64(privateKey) {
  const raw = await getSubtleApi().exportKey('pkcs8', privateKey);
  return bytesToBase64(new Uint8Array(raw));
}

export async function importPkcs8Ed25519(pkcs8B64) {
  return getSubtleApi().importKey('pkcs8', base64ToBytes(pkcs8B64), { name: 'Ed25519' }, false, ['sign']);
}

export async function signBase64(privateKey, text) {
  const signature = await getSubtleApi().sign({ name: 'Ed25519' }, privateKey, utf8Bytes(text));
  return bytesToBase64(new Uint8Array(signature));
}

export async function sha256Bytes(bytes) {
  const digest = await getSubtleApi().digest('SHA-256', bytes);
  return new Uint8Array(digest);
}

export async function sha256Text(text) {
  return sha256Bytes(utf8Bytes(text));
}

export function randomBase64(size) {
  const bytes = getCryptoApi().getRandomValues(new Uint8Array(size));
  return bytesToBase64(bytes);
}

export function bytesToHex(bytes) {
  return [...bytes].map((byte) => byte.toString(16).padStart(2, '0')).join('');
}
