"use strict";
/**
 * WalletFacade orchestration — main-thread side (Phase 1: worker-thread aware).
 *
 * The real `WalletFacade` lives in a `worker_threads` worker (see
 * `srv/midnight/wallet-worker.ts`). The wallet SDK's Effect.ts Fiber scheduler
 * monopolises Node's microtask queue while a chain sync is running, so we keep
 * it off the main thread. This builder is now a thin glue layer:
 *
 *   1. Load any persisted sub-state blobs from `midnight.WalletSyncStates`
 *      (standard CAP `db.run`, the main thread is free).
 *   2. Tell the worker to `init(sessionId, seedHex, ..., restoreBlobs?)`.
 *   3. Cache the fact that this session has an active facade in the worker.
 *
 *   Periodic state-save: worker pushes `{state-save, sessionId, blobs}`
 *   events on `parentPort`. The client receives them and writes via CAP
 *   `db.run(UPDATE/INSERT)`. Wiring lives in `wireWorkerStateSaveSink()`
 *   below, called once by the plugin lifecycle.
 *
 * Phase 1 surface — what callers still get from this module:
 *   - `getOrBuildWalletFacade(cacheKey, args)`  initialises the worker-side
 *     facade and resolves once it's ready. The returned object is a stub —
 *     properties (.facade, .keys) exist only to keep the OLD callers from
 *     blowing up at import time. Their methods will throw `Error("phase-2:
 *     migrate to wallet-worker-client")` if called.
 *   - `evictWalletFacade(cacheKey)`  tells the worker to drop and final-save.
 *
 * Phase 2 will rewire `dust-registration.ts` and
 * `wallet-material-factory.ts:createFacadeBackedWalletAdapter` to call the
 * worker-client RPC API directly for `balanceTx` / `submitTx` /
 * `registerNightUtxosForDustGeneration`, removing the stub.
 */
Object.defineProperty(exports, "__esModule", { value: true });
exports.getOrBuildWalletFacade = getOrBuildWalletFacade;
exports.evictWalletFacade = evictWalletFacade;
exports.getCacheSize = getCacheSize;
exports.clearAllFacades = clearAllFacades;
exports.wireWorkerStateSaveSink = wireWorkerStateSaveSink;
const wallet_worker_client_1 = require("../midnight/wallet-worker-client");
const wallet_sync_state_store_1 = require("./wallet-sync-state-store");
const format_error_1 = require("../utils/format-error");
// Opt-in facade-restore diagnostics (off by default; enable with
// NIGHTGATE_DEBUG_WALLET_SYNC=true). Keeps the plugin quiet on a consumer's stdout.
const DEBUG_SYNC = process.env.NIGHTGATE_DEBUG_WALLET_SYNC === 'true';
const dbgSync = (msg) => { if (DEBUG_SYNC)
    console.log(msg); };
/** Phase 1 stub returned to callers that still expect a `facade` object. */
const phase2Stub = (op) => () => {
    throw new Error(`[phase-1 worker migration] ${op} is not yet wired through wallet-worker-client. ` +
        `Re-route this call site to use srv/midnight/wallet-worker-client directly.`);
};
const subStub = (label) => ({
    start: phase2Stub(`${label}.start`),
    stop: phase2Stub(`${label}.stop`),
    waitForSyncedState: phase2Stub(`${label}.waitForSyncedState`),
    balanceTransaction: phase2Stub(`${label}.balanceTransaction`),
    serializeState: phase2Stub(`${label}.serializeState`)
});
const facadeStub = {
    state: phase2Stub('facade.state'),
    waitForSyncedState: phase2Stub('facade.waitForSyncedState'),
    submitTransaction: phase2Stub('facade.submitTransaction'),
    balanceUnboundTransaction: phase2Stub('facade.balanceUnboundTransaction'),
    finalizeRecipe: phase2Stub('facade.finalizeRecipe'),
    registerNightUtxosForDustGeneration: phase2Stub('facade.registerNightUtxosForDustGeneration'),
    revert: phase2Stub('facade.revert'),
    stop: phase2Stub('facade.stop'),
    shielded: subStub('shielded'),
    unshielded: subStub('unshielded'),
    dust: subStub('dust')
};
const sessionRegistry = new Map();
/**
 * Initialise a wallet for the given cacheKey via the worker. Idempotent:
 * subsequent calls for the same cacheKey hit the worker's cache.
 *
 * Returns a placeholder shape compatible with the pre-worker callers; methods
 * on `.facade` and `.keys.*` throw with a Phase 2 migration error if hit, so
 * code paths that haven't been migrated yet fail loudly instead of silently
 * regressing.
 */
async function getOrBuildWalletFacade(cacheKey, args) {
    // Attempt to restore from CAP-persisted state. Main thread is no longer
    // blocked by the SDK, so this is a plain `db.run(SELECT)` call.
    let restoreBlobs;
    if (args.syncStatePassphrase) {
        const loaded = await (0, wallet_sync_state_store_1.loadSyncState)({
            accountId: cacheKey,
            passphrase: args.syncStatePassphrase,
            expectedSdkVersion: (0, wallet_sync_state_store_1.getWalletSdkVersion)()
        });
        if (loaded) {
            restoreBlobs = {
                shielded: loaded.shielded,
                unshielded: loaded.unshielded,
                dust: loaded.dust
            };
            dbgSync(`[facade] restored prior state for ${cacheKey.slice(0, 16)}: ` +
                `shielded=${!!loaded.shielded} unshielded=${!!loaded.unshielded} dust=${!!loaded.dust}`);
        }
        else {
            dbgSync(`[facade] no usable prior state for ${cacheKey.slice(0, 16)} (cold start)`);
        }
    }
    const initArgs = {
        sessionId: cacheKey,
        seedHex: args.seedHex,
        networkId: args.networkId,
        indexerHttpUrl: args.indexerHttpUrl,
        indexerWsUrl: args.indexerWsUrl,
        proofServerUrl: args.proofServerUrl,
        relayUrl: args.relayUrl,
        restoreBlobs
    };
    const result = await (0, wallet_worker_client_1.walletInit)(initArgs);
    dbgSync(`[facade] worker init ok for ${cacheKey.slice(0, 16)}: ` +
        `alreadyExisted=${result.alreadyExisted} sdk=${result.sdkVersion ?? '?'}`);
    if (args.syncStatePassphrase) {
        sessionRegistry.set(cacheKey, {
            passphrase: args.syncStatePassphrase,
            accountId: cacheKey
        });
    }
    return {
        facade: facadeStub,
        zswapKeys: { clear: () => undefined, __phase2: true },
        dustKey: { __phase2: true },
        unshieldedKeystore: { __phase2: true }
    };
}
/**
 * Tell the worker to drop and final-save the facade for this cacheKey.
 */
async function evictWalletFacade(cacheKey) {
    sessionRegistry.delete(cacheKey);
    try {
        await (0, wallet_worker_client_1.walletEvict)(cacheKey);
    }
    catch (err) {
        console.warn(`[facade] evict failed for ${cacheKey.slice(0, 16)}:`, (0, format_error_1.formatErr)(err));
    }
}
function getCacheSize() {
    return sessionRegistry.size;
}
function clearAllFacades() {
    sessionRegistry.clear();
}
/**
 * Wire the worker → main-thread `state-save` events to standard CAP
 * `db.run(...)` via `saveSyncState`. Call ONCE at plugin init AFTER
 * `startWalletWorker()` has resolved.
 */
function wireWorkerStateSaveSink() {
    (0, wallet_worker_client_1.setStateSaveSink)(async (event) => {
        const session = sessionRegistry.get(event.sessionId);
        if (!session) {
            // The session was evicted between save scheduling and arrival —
            // OR it was never registered (e.g. caller didn't pass
            // syncStatePassphrase to getOrBuildWalletFacade).
            console.warn(`[facade-persist] DROPPED save for ${event.sessionId.slice(0, 16)}: ` +
                `no session in registry (known: [${Array.from(sessionRegistry.keys()).map(k => k.slice(0, 16)).join(',')}])`);
            return;
        }
        console.log(`[facade-persist] received save for ${event.sessionId.slice(0, 16)}, persisting...`);
        try {
            await (0, wallet_sync_state_store_1.saveSyncState)({
                accountId: session.accountId,
                passphrase: session.passphrase,
                sdkVersion: event.sdkVersion,
                states: event.blobs
            });
            const sizes = [
                event.blobs.shielded ? `sh=${event.blobs.shielded.length}` : 'sh=-',
                event.blobs.unshielded ? `un=${event.blobs.unshielded.length}` : 'un=-',
                event.blobs.dust ? `du=${event.blobs.dust.length}` : 'du=-'
            ].join(' ');
            console.log(`[facade-persist] saved ${event.sessionId.slice(0, 16)} ${sizes}`);
        }
        catch (err) {
            console.warn(`[facade-persist] save failed for ${event.sessionId.slice(0, 16)}:`, (0, format_error_1.formatErr)(err));
        }
    });
}
