/**
 * Zero-Knowledge WebAuthn Client
 * Hardware-bound authentication without passwords
 */

interface RegistrationOptions {
  challenge: string;
  rp: {
    name: string;
    id: string;
  };
  user: {
    id: string;
    name: string;
    displayName: string;
  };
  pubKeyCredParams: Array<{
    type: string;
    alg: number;
  }>;
  timeout: number;
  attestation: string;
  authenticatorSelection: {
    authenticatorAttachment?: string;
    requireResidentKey: boolean;
    userVerification: string;
  };
}

interface AuthenticationOptions {
  challenge: string;
  timeout: number;
  rpId: string;
  allowCredentials: string[];
  userVerification: string;
}

/**
 * Base64URL encoding/decoding utilities
 */
function base64urlToBuffer(base64url: string): ArrayBuffer {
  const base64 = base64url.replace(/-/g, '+').replace(/_/g, '/');
  const padded = base64.padEnd(base64.length + (4 - (base64.length % 4)) % 4, '=');
  const binary = atob(padded);
  const bytes = new Uint8Array(binary.length);
  for (let i = 0; i < binary.length; i++) {
    bytes[i] = binary.charCodeAt(i);
  }
  return bytes.buffer;
}

function bufferToBase64url(buffer: ArrayBuffer): string {
  const bytes = new Uint8Array(buffer);
  let binary = '';
  for (let i = 0; i < bytes.length; i++) {
    binary += String.fromCharCode(bytes[i]);
  }
  return btoa(binary).replace(/\+/g, '-').replace(/\//g, '_').replace(/=/g, '');
}

/**
 * Register a new WebAuthn credential (hardware key, biometric, etc.)
 */
export async function registerWebAuthn(
  username: string,
  displayName: string,
  email: string
): Promise<{ success: boolean; error?: string }> {
  try {
    // Step 1: Request registration options from backend
    const beginRes = await fetch('/api/auth/register/begin', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ username, displayName, email }),
    });

    if (!beginRes.ok) {
      throw new Error('Failed to begin registration');
    }

    const options: RegistrationOptions = await beginRes.json();

    // Step 2: Create credential via browser's WebAuthn API
    const credential = await navigator.credentials.create({
      publicKey: {
        challenge: base64urlToBuffer(options.challenge),
        rp: options.rp,
        user: {
          id: new TextEncoder().encode(options.user.id),
          name: options.user.name,
          displayName: options.user.displayName,
        },
        pubKeyCredParams: options.pubKeyCredParams as PublicKeyCredentialParameters[],
        timeout: options.timeout,
        attestation: options.attestation as AttestationConveyancePreference,
        authenticatorSelection: options.authenticatorSelection as AuthenticatorSelectionCriteria,
      },
    }) as PublicKeyCredential | null;

    if (!credential) {
      throw new Error('Credential creation cancelled');
    }

    const response = credential.response as AuthenticatorAttestationResponse;

    // Step 3: Send credential to backend for verification
    const finishRes = await fetch('/api/auth/register/finish', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({
        id: credential.id,
        rawId: bufferToBase64url(credential.rawId),
        type: credential.type,
        response: {
          attestationObject: bufferToBase64url(response.attestationObject),
          clientDataJSON: bufferToBase64url(response.clientDataJSON),
        },
      }),
    });

    if (!finishRes.ok) {
      throw new Error('Registration verification failed');
    }

    return { success: true };
  } catch (error) {
    console.error('WebAuthn registration error:', error);
    return { success: false, error: (error as Error).message };
  }
}

/**
 * Authenticate using WebAuthn and retrieve the wrapped master key
 */
export async function authenticateWebAuthn(
  username: string
): Promise<{ success: boolean; masterKey?: Uint8Array; communityId?: string; error?: string }> {
  try {
    // Step 1: Request authentication options from backend
    const beginRes = await fetch('/api/auth/login/begin', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ username }),
    });

    if (!beginRes.ok) {
      throw new Error('Failed to begin authentication');
    }

    const options: AuthenticationOptions = await beginRes.json();

    // Step 2: Get credential via browser's WebAuthn API
    const credential = await navigator.credentials.get({
      publicKey: {
        challenge: base64urlToBuffer(options.challenge),
        timeout: options.timeout,
        rpId: options.rpId,
        allowCredentials: options.allowCredentials.map((id) => ({
          type: 'public-key' as const,
          id: base64urlToBuffer(id),
        })),
        userVerification: options.userVerification as UserVerificationRequirement,
      },
    }) as PublicKeyCredential | null;

    if (!credential) {
      throw new Error('Authentication cancelled');
    }

    const response = credential.response as AuthenticatorAssertionResponse;

    // Step 3: Send signature to backend for verification
    const finishRes = await fetch('/api/auth/login/finish', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({
        id: credential.id,
        rawId: bufferToBase64url(credential.rawId),
        type: credential.type,
        response: {
          authenticatorData: bufferToBase64url(response.authenticatorData),
          clientDataJSON: bufferToBase64url(response.clientDataJSON),
          signature: bufferToBase64url(response.signature),
          userHandle: response.userHandle ? bufferToBase64url(response.userHandle) : null,
        },
      }),
    });

    if (!finishRes.ok) {
      throw new Error('Authentication failed');
    }

    const result = await finishRes.json();

    // Step 4: Unwrap the master key (returned from backend after successful auth)
    const masterKeyBytes = new TextEncoder().encode(result.masterKey);

    return {
      success: true,
      masterKey: masterKeyBytes,
      communityId: result.communityId,
    };
  } catch (error) {
    console.error('WebAuthn authentication error:', error);
    return { success: false, error: (error as Error).message };
  }
}

/**
 * Check if WebAuthn is supported in this browser
 */
export function isWebAuthnSupported(): boolean {
  return (
    window.PublicKeyCredential !== undefined &&
    navigator.credentials !== undefined &&
    navigator.credentials.create !== undefined
  );
}
