import { encryptForSync, decryptFromSync } from './SecretStore.js';
/** Build the `secret-share` command for one recipient (encrypts the secret set to them). */
export function buildSecretShareCommand(secrets, recipient) {
    const payload = encryptForSync(secrets, recipient.encryptionPublicKey);
    return { type: 'secret-share', encrypted: JSON.stringify(payload) };
}
/**
 * Flatten a (possibly nested) secrets map to its leaf dot-notation key-paths — NAMES ONLY,
 * never values. Used by the read-only `/secrets/sync-status` route so a caller can see WHICH
 * secrets a machine holds without the route ever touching a secret value. Mirrors the
 * dot-notation `set`/`get` the vault uses (e.g. `{ telegram: { token: 'x' } }` → `telegram.token`).
 */
export function secretKeyPaths(secrets, prefix = '') {
    const out = [];
    for (const [key, value] of Object.entries(secrets)) {
        const path = prefix ? `${prefix}.${key}` : key;
        if (value && typeof value === 'object' && !Array.isArray(value)) {
            out.push(...secretKeyPaths(value, path));
        }
        else {
            out.push(path);
        }
    }
    return out;
}
/**
 * Provisioner (sender side). Encrypts the secret set PER-RECIPIENT and pushes it to each
 * online registered peer over the injected mesh transport. Returns a per-peer result; never
 * throws on a single peer's failure (best-effort fan-out).
 */
export class SecretProvisioner {
    deps;
    constructor(deps) {
        this.deps = deps;
    }
    /** Push the current secret set to every online peer. */
    async provisionAll() {
        const secrets = this.deps.secretsToSync();
        const peers = this.deps.listPeers();
        if (Object.keys(secrets).length === 0 || peers.length === 0)
            return [];
        const out = [];
        for (const peer of peers) {
            try {
                const cmd = buildSecretShareCommand(secrets, peer);
                const r = await this.deps.send(peer.machineId, cmd);
                out.push({ machineId: peer.machineId, ok: r.ok, reason: r.reason });
                if (r.ok)
                    this.deps.log?.(`[secret-sync] pushed ${Object.keys(secrets).length} secret(s) to ${peer.machineId}`);
            }
            catch (err) {
                out.push({ machineId: peer.machineId, ok: false, reason: err instanceof Error ? err.message : String(err) });
            }
        }
        return out;
    }
}
/**
 * Handler (receiver side). Decrypts an inbound `secret-share` with THIS machine's private
 * key and writes each secret into the local vault. The sender's authenticity + the
 * registered-peer gate are already enforced by the mesh acceptance layer BEFORE this runs;
 * confidentiality is enforced here (decryption fails for a payload not sealed to our key).
 */
export class SecretShareHandler {
    deps;
    constructor(deps) {
        this.deps = deps;
    }
    /** Decrypt + store. Returns the keyPaths stored. Throws only on a malformed/foreign payload. */
    handle(command, sender) {
        // The legacy permissive path must be structurally unreachable for credential-class data
        // (WS5.2 R3a / §6.5): a credential rides the distinct `account-credential-share` verb +
        // `decryptAccountCredential`, never this one. Reject anything that is not a secret-share.
        if (!command || command.type !== 'secret-share') {
            throw new Error(`SecretShareHandler refuses non-secret-share command (got "${command?.type}") — credential-class data must use account-credential-share`);
        }
        const payload = JSON.parse(command.encrypted);
        const secrets = decryptFromSync(payload, this.deps.ownEncryptionPrivateKey());
        const stored = [];
        for (const [keyPath, value] of Object.entries(secrets)) {
            this.deps.store.set(keyPath, value);
            stored.push(keyPath);
        }
        this.deps.log?.(`[secret-sync] stored ${stored.length} secret(s) from ${sender}`);
        return { stored };
    }
}
//# sourceMappingURL=SecretSync.js.map