/**
 * Sync client — callboard-side API for the key exchange protocol.
 *
 * Usage:
 *   const exchange = startKeyExchange({ remoteUrl, inviteCode, encryptionKey, callerAlias });
 *   // Display exchange.confirmCode to the user
 *   // User enters confirmCode into drawlatch
 *   const result = await exchange.complete();
 *   // Keys exchanged, remote public keys saved locally
 */
import { createCaller, exportCallerPublicKeys, saveServerPublicKeys, callerExists, } from '../crypto/key-manager.js';
import { generateSyncCode, encryptSyncPayload, decryptSyncPayload, } from './sync.js';
export class SyncClientError extends Error {
    code;
    statusCode;
    constructor(message, code, statusCode) {
        super(message);
        this.code = code;
        this.statusCode = statusCode;
        this.name = 'SyncClientError';
    }
}
/**
 * Start the key exchange from callboard's side.
 *
 * 1. Creates caller keys if they don't exist yet
 * 2. Generates a confirm code
 * 3. Returns the confirm code + a `complete()` function
 * 4. `complete()` encrypts and sends POST /sync, decrypts response, saves remote keys
 */
export function startKeyExchange(opts) {
    const { remoteUrl, inviteCode, encryptionKey, callerAlias, configDir } = opts;
    const keyOpts = configDir ? { configDir } : undefined;
    // Create caller keys if they don't exist
    let publicKeys;
    if (callerExists(callerAlias, keyOpts)) {
        publicKeys = exportCallerPublicKeys(callerAlias, keyOpts);
    }
    else {
        const result = createCaller(callerAlias, keyOpts);
        publicKeys = result.publicKeys;
    }
    const confirmCode = generateSyncCode();
    return {
        confirmCode,
        publicKeys,
        async complete() {
            const request = {
                inviteCode,
                confirmCode,
                callerAlias,
                publicKeys,
            };
            const encryptedBody = encryptSyncPayload(request, encryptionKey);
            const url = remoteUrl.replace(/\/+$/, '') + '/sync';
            const response = await fetch(url, {
                method: 'POST',
                headers: { 'Content-Type': 'text/plain' },
                body: encryptedBody,
            });
            if (!response.ok) {
                let errorCode = 'NO_ACTIVE_SESSION';
                try {
                    const errorBody = (await response.json());
                    if (errorBody.error)
                        errorCode = errorBody.error;
                }
                catch {
                    // Could not parse error body
                }
                throw new SyncClientError(`Sync failed (HTTP ${response.status}): ${errorCode}`, errorCode, response.status);
            }
            const encryptedResponse = await response.text();
            let decrypted;
            try {
                decrypted = decryptSyncPayload(encryptedResponse, encryptionKey);
            }
            catch {
                throw new SyncClientError('Failed to decrypt sync response — encryption key mismatch?', 'DECRYPTION_FAILED');
            }
            // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment -- validating unknown payload
            const syncResponse = decrypted;
            if (!syncResponse.remotePublicKeys?.signing ||
                !syncResponse.remotePublicKeys.exchange ||
                !syncResponse.callerAlias) {
                throw new SyncClientError('Invalid sync response payload', 'INVALID_PAYLOAD');
            }
            // Save the remote server's public keys locally
            const validResponse = syncResponse;
            saveServerPublicKeys(validResponse.remotePublicKeys, keyOpts);
            return {
                remotePublicKeys: validResponse.remotePublicKeys,
                callerAlias: validResponse.callerAlias,
                fingerprint: validResponse.fingerprint,
            };
        },
    };
}
//# sourceMappingURL=sync-client.js.map