var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
    function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
    return new (P || (P = Promise))(function (resolve, reject) {
        function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
        function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
        function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
        step((generator = generator.apply(thisArg, _arguments || [])).next());
    });
};
import { Level } from 'level';
import { DataStoreLevel, DurableEventLog, EventEmitterWakePublisher, MessageStoreLevel, ResumableTaskStoreLevel } from '@enbox/dwn-sdk-js';
import { DidDht, DidJwk, DidResolverCacheMemory } from '@enbox/dids';
import { LevelStore, MemoryStore } from '@enbox/common';
import { AgentCryptoApi } from './crypto-api.js';
import { AgentDidApi } from './did-api.js';
import { AgentDidResolverCache } from './agent-did-resolver-cache.js';
import { AgentDwnApi } from './dwn-api.js';
import { AgentIdentityApi } from './identity-api.js';
import { AgentPermissionsApi } from './permissions-api.js';
import { EnboxRpcClient } from '@enbox/dwn-clients';
import { HdIdentityVault } from './hd-identity-vault.js';
import { LocalKeyManager } from './local-key-manager.js';
import { SyncEngineLevel } from './sync-engine-level.js';
import { DwnDidStore, InMemoryDidStore } from './store-did.js';
import { DwnIdentityStore, InMemoryIdentityStore } from './store-identity.js';
import { DwnKeyStore, InMemoryKeyStore } from './store-key.js';
import { InMemorySecretStore, VaultBackedSecretStore } from './secret-store.js';
export class PlatformAgentTestHarness {
    constructor(params) {
        this.agent = params.agent;
        this.agentStores = params.agentStores;
        this.didResolverCache = params.didResolverCache;
        this.dwn = params.dwn;
        this.dwnDataStore = params.dwnDataStore;
        this.dwnEventLog = params.dwnEventLog;
        this.dwnMessageStore = params.dwnMessageStore;
        this.secretStore = params.secretStore;
        this.syncStore = params.syncStore;
        this.vaultStore = params.vaultStore;
        this.dwnResumableTaskStore = params.dwnResumableTaskStore;
        this.dwnStores = params.dwnStores;
    }
    clearStorage() {
        return __awaiter(this, void 0, void 0, function* () {
            // first stop any ongoing sync operations
            yield this.agent.sync.stopSync();
            // Drain any in-flight fire-and-forget eager-send promises dispatched by
            // `AgentDwnApi.writeContextKeyRecord` so they cannot outlive the agent
            // and touch a nulled `agentDid` or a cleared LevelDB store. Fast path
            // when the tracker is empty (no pending sends).
            yield this.agent.dwn.drainPendingEagerSends();
            // @ts-expect-error since normally this property shouldn't be set to undefined.
            this.agent.agentDid = undefined;
            yield this.didResolverCache.clear();
            yield this.resetDwnEventLog();
            yield this.dwnDataStore.clear();
            yield this.dwnMessageStore.clear();
            yield this.dwnResumableTaskStore.clear();
            yield this.clearSyncStore();
            yield this.vaultStore.clear();
            if (this.secretStore) {
                yield this.secretStore.clear();
            }
            this.agent.vault['_cachedInitialized'] = undefined;
            yield this.agent.permissions.clear();
            this.dwnStores.clear();
            // Reset the indexes and caches for the Agent's DWN data stores.
            // if (this.agentStores === 'dwn') {
            //   const { didApi, identityApi } = PlatformAgentTestHarness.useDiskStores({ testDataLocation: '__TESTDATA__', agent: this.agent });
            //   this.agent.crypto = cryptoApi;
            //   this.agent.did = didApi;
            //   this.agent.identity = identityApi;
            // }
            // Easiest way to start with fresh in-memory stores is to re-instantiate Agent components.
            if (this.agentStores === 'memory') {
                const { didApi, identityApi, permissionsApi, keyManager, secretsApi } = PlatformAgentTestHarness.useMemoryStores({ agent: this.agent });
                this.agent.did = didApi;
                this.agent.identity = identityApi;
                this.agent.keyManager = keyManager;
                this.agent.permissions = permissionsApi;
                this.agent.secrets = secretsApi;
            }
        });
    }
    /**
     * Clear only DWN-level stores (data, messages, resumable tasks, sync,
     * permissions) and the DWN-backed store caches — but preserve the
     * agent DID, vault, and all key/DID/identity material.
     *
     * Use this in `beforeEach` when `createAgentDid()` (and optionally
     * `createIdentity()`) was called once in `beforeAll` to avoid expensive
     * DID re-creation on every test.
     */
    clearDwnStores() {
        return __awaiter(this, void 0, void 0, function* () {
            yield this.agent.sync.stopSync();
            yield this.resetDwnEventLog();
            yield this.clearSyncStore();
            yield this.dwnDataStore.clear();
            yield this.dwnMessageStore.clear();
            yield this.dwnResumableTaskStore.clear();
            yield this.agent.permissions.clear();
            this.dwnStores.clear();
        });
    }
    closeStorage() {
        return __awaiter(this, void 0, void 0, function* () {
            // Drain any in-flight fire-and-forget eager-send promises dispatched by
            // `AgentDwnApi.writeContextKeyRecord` before closing the LevelDB-backed
            // stores. Prevents orphan promises from hitting closed handles with
            // `LEVEL_DATABASE_NOT_OPEN` after teardown. Fast path when empty.
            yield this.agent.dwn.drainPendingEagerSends();
            yield this.didResolverCache.close();
            yield this.dwnDataStore.close();
            yield this.dwnMessageStore.close();
            yield this.dwnResumableTaskStore.close();
            yield this.dwnEventLog.close();
            if (this.secretStore) {
                yield this.secretStore.close();
            }
            yield this.syncStore.close();
            yield this.vaultStore.close();
        });
    }
    clearSyncStore() {
        return __awaiter(this, void 0, void 0, function* () {
            const sublevelNames = [
                'deadLetters',
                'deferredPulls',
                'registeredIdentities',
                'replicationLinks',
            ];
            for (const sublevelName of sublevelNames) {
                yield this.syncStore.sublevel(sublevelName).clear();
            }
            yield this.syncStore.clear();
        });
    }
    resetDwnEventLog() {
        return __awaiter(this, void 0, void 0, function* () {
            yield this.dwnEventLog.close();
            yield this.dwnEventLog.open();
        });
    }
    createAgentDid() {
        return __awaiter(this, void 0, void 0, function* () {
            var _a;
            // Create a DID for the Agent with Ed25519 (signing) and X25519 (keyAgreement).
            // X25519 is required by DwnKeyStore for JWE record-level encryption.
            // Must be published so the DWN can resolve it for JWS signature verification.
            this.agent.agentDid = yield DidDht.create({
                options: {
                    publish: true,
                    gatewayUri: (_a = process.env.DID_DHT_GATEWAY_URI) !== null && _a !== void 0 ? _a : 'http://localhost:7527',
                    verificationMethods: [
                        {
                            algorithm: 'Ed25519',
                            id: 'sig',
                            purposes: ['assertionMethod', 'authentication']
                        },
                        {
                            algorithm: 'X25519',
                            id: 'enc',
                            purposes: ['keyAgreement']
                        }
                    ]
                }
            });
        });
    }
    createIdentity(_a) {
        return __awaiter(this, arguments, void 0, function* ({ name, testDwnUrls }) {
            const bearerIdentity = yield this.agent.identity.create({
                didMethod: 'dht',
                didOptions: {
                    services: [
                        {
                            id: 'dwn',
                            type: 'DecentralizedWebNode',
                            serviceEndpoint: testDwnUrls,
                        }
                    ],
                    verificationMethods: [
                        {
                            algorithm: 'Ed25519',
                            id: 'sig',
                            purposes: ['assertionMethod', 'authentication']
                        },
                        {
                            algorithm: 'X25519',
                            id: 'enc',
                            purposes: ['keyAgreement']
                        }
                    ]
                },
                metadata: { name }
            });
            return bearerIdentity;
        });
    }
    static setup(_a) {
        return __awaiter(this, arguments, void 0, function* ({ agentClass, agentStores, testDataLocation }) {
            agentStores !== null && agentStores !== void 0 ? agentStores : (agentStores = 'memory');
            testDataLocation !== null && testDataLocation !== void 0 ? testDataLocation : (testDataLocation = '__TESTDATA__');
            const testDataPath = (path) => `${testDataLocation}/${path}`;
            // Instantiate Agent's Crypto API.
            const cryptoApi = new AgentCryptoApi();
            // Instantiate Agent's RPC Client.
            const rpcClient = new EnboxRpcClient();
            const dwnStores = {
                keyStore: new DwnKeyStore(),
                identityStore: new DwnIdentityStore(),
                didStore: new DwnDidStore(),
                clear: () => {
                    var _a, _b, _c;
                    (_a = dwnStores.keyStore['_protocolInitializedCache']) === null || _a === void 0 ? void 0 : _a.clear();
                    (_b = dwnStores.identityStore['_protocolInitializedCache']) === null || _b === void 0 ? void 0 : _b.clear();
                    (_c = dwnStores.didStore['_protocolInitializedCache']) === null || _c === void 0 ? void 0 : _c.clear();
                }
            };
            const { agentVault, didApi, identityApi, keyManager, didResolverCache, vaultStore, permissionsApi, secretsApi, secretStore, } = (agentStores === 'memory')
                ? PlatformAgentTestHarness.useMemoryStores()
                : PlatformAgentTestHarness.useDiskStores({ testDataLocation, stores: dwnStores });
            // Instantiate custom stores to use with DWN instance.
            // Note: There is no in-memory store for DWN, so we always use LevelDB-based disk stores.
            const dwnDataStore = new DataStoreLevel({ blockstoreLocation: testDataPath('DWN_DATASTORE') });
            const wakePublisher = new EventEmitterWakePublisher();
            const dwnMessageStore = new MessageStoreLevel({
                location: testDataPath('DWN_MESSAGESTORE'),
                wakePublisher,
            });
            const dwnEventLog = new DurableEventLog(dwnMessageStore, wakePublisher);
            const dwnResumableTaskStore = new ResumableTaskStoreLevel({ location: testDataPath('DWN_RESUMABLETASKSTORE') });
            // Instantiate DWN instance using the custom stores.
            const dwn = yield AgentDwnApi.createDwn({
                dataPath: testDataLocation,
                dataStore: dwnDataStore,
                didResolver: didApi,
                messageLog: { eventLog: dwnEventLog, messageStore: dwnMessageStore },
                resumableTaskStore: dwnResumableTaskStore
            });
            // Instantiate Agent's DWN API using the custom DWN instance.
            // Disable local DWN discovery so tests don't accidentally probe localhost.
            const dwnApi = new AgentDwnApi({ dwn, localDwnStrategy: 'off' });
            // Instantiate Agent's Sync API using a custom LevelDB-backed store.
            const syncStore = new Level(testDataPath('SYNC_STORE'));
            const syncApi = new SyncEngineLevel({ db: syncStore });
            // Create EnboxPlatformAgent instance
            const agent = new agentClass({
                agentVault,
                cryptoApi,
                didApi,
                dwnApi,
                identityApi,
                keyManager,
                permissionsApi,
                rpcClient,
                secretsApi,
                syncApi,
            });
            return new PlatformAgentTestHarness({
                agent,
                agentStores,
                didResolverCache,
                dwn,
                dwnDataStore,
                dwnEventLog,
                dwnMessageStore,
                dwnResumableTaskStore,
                dwnStores,
                secretStore,
                syncStore,
                vaultStore,
            });
        });
    }
    static useDiskStores({ agent, testDataLocation, stores }) {
        const testDataPath = (path) => `${testDataLocation}/${path}`;
        const vaultStore = new LevelStore({ location: testDataPath('VAULT_STORE') });
        const agentVault = new HdIdentityVault({ keyDerivationWorkFactor: 1, store: vaultStore });
        const { didStore, identityStore, keyStore } = stores;
        // Setup DID Resolver Cache
        const didResolverCache = new AgentDidResolverCache({
            location: testDataPath('DID_RESOLVERCACHE')
        });
        const didApi = new AgentDidApi({
            agent: agent,
            didMethods: [DidDht, DidJwk],
            resolverCache: didResolverCache,
            store: didStore
        });
        const identityApi = new AgentIdentityApi({ agent, store: identityStore });
        const keyManager = new LocalKeyManager({ agent, keyStore: keyStore });
        const permissionsApi = new AgentPermissionsApi({ agent });
        const secretStore = new LevelStore({ location: testDataPath('SECRET_STORE') });
        const secretsApi = new VaultBackedSecretStore({
            vault: agentVault,
            store: secretStore,
        });
        return { agentVault, didApi, didResolverCache, identityApi, keyManager, permissionsApi, secretsApi, secretStore, vaultStore };
    }
    static useMemoryStores({ agent } = {}) {
        const vaultStore = new MemoryStore();
        const agentVault = new HdIdentityVault({ keyDerivationWorkFactor: 1, store: vaultStore });
        // Setup DID Resolver Cache
        const didResolverCache = new DidResolverCacheMemory();
        const didApi = new AgentDidApi({
            agent: agent,
            didMethods: [DidDht, DidJwk],
            resolverCache: didResolverCache,
            store: new InMemoryDidStore()
        });
        const keyManager = new LocalKeyManager({ agent, keyStore: new InMemoryKeyStore() });
        const identityApi = new AgentIdentityApi({ agent, store: new InMemoryIdentityStore() });
        const permissionsApi = new AgentPermissionsApi({ agent });
        const secretsApi = new InMemorySecretStore();
        return { agentVault, didApi, didResolverCache, identityApi, keyManager, permissionsApi, secretsApi, vaultStore };
    }
}
//# sourceMappingURL=test-harness.js.map