/**
 * @module sync-withid.pingpong.respec
 *
 * Phase 3A — Basic Single-Timeline Sync with Identity
 *
 * Goal: Verify that the ping pong sync process completes successfully with identity.
 *
 * @see libs/core-gib/src/sync/docs/ping_pong_plan.md
 */
import { respecfully, iReckon, ifWe, } from '@ibgib/helper-gib/dist/respec-gib/respec-gib.mjs';
const maam = `[${import.meta.url}]`, sir = maam;
import { clone, delay, extractErrorMsg } from '@ibgib/helper-gib/dist/helpers/utils-helper.mjs';
import { getIbGibAddr } from '@ibgib/ts-gib/dist/helper.mjs';
import { GLOBAL_LOG_A_LOT } from '../core-constants.mjs';
import { SyncSagaCoordinator } from './sync-saga-coordinator.mjs';
import { Metaspace_Innerspace } from '../witness/space/metaspace/metaspace-innerspace/metaspace-innerspace.mjs';
import { InnerSpace_V1 } from '../witness/space/inner-space/inner-space-v1.mjs';
import { SyncPeerInnerspace_V1 } from './sync-peer/sync-peer-innerspace/sync-peer-innerspace-v1.mjs';
import { DEFAULT_INNER_SPACE_DATA_V1 } from '../witness/space/inner-space/inner-space-types.mjs';
import { SYNC_PEER_INNERSPACE_DEFAULT_DATA_V1 } from './sync-peer/sync-peer-innerspace/sync-peer-innerspace-constants.mjs';
import { KeystoneService_V1 } from '../keystone/keystone-service-v1.mjs';
import { KEYSTONE_VERB_SYNC, POOL_ID_SYNC, } from '../keystone/keystone-constants.mjs';
import { createStandardPoolConfig } from '../keystone/keystone-config-builder.mjs';
import { KeystoneReplenishStrategy } from '../keystone/keystone-types.mjs';
import { SyncConflictStrategy } from './sync-constants.mjs';
import { Factory_V1 } from '@ibgib/ts-gib/dist/V1/factory.mjs';
import { getDependencyGraph, graphsAreEquivalent } from '../common/other/graph-helper.mjs';
import { getFromSpace } from '../witness/space/space-helper.mjs';
const logalot = GLOBAL_LOG_A_LOT;
const lc = sir;
const SENDER_SECRET = 'test-sender-secret-phase1';
const SENDER_IDENTITY_SYNC_POOL_CONFIG = createStandardPoolConfig({
    id: POOL_ID_SYNC,
    salt: 'senderidentitysyncsaltphase1',
    verbs: [KEYSTONE_VERB_SYNC],
    size: 200,
    sequential: 1,
    random: 1,
    targetBinding: 2,
    replenishStrategy: KeystoneReplenishStrategy.topUp,
});
await respecfully(sir, `Test Phase 3A: Ping Pong Sync with Identity`, async () => {
    // #region Init/Setup
    const metaspace = new Metaspace_Innerspace(undefined);
    await metaspace.initialize({
        getFnAlert: () => async ({ title, msg }) => { console.log(`[Alert] ${title}: ${msg}`); },
        getFnPrompt: () => async ({ title, msg }) => { console.log(`[Prompt] ${title}: ${msg}`); return ''; },
        getFnPromptPassword: () => async (title, msg) => { console.log(`[PromptPwd] ${title}: ${msg}`); return null; },
    });
    while (!metaspace.initialized) {
        await delay(10);
    }
    const defaultLocalUserSpace = await metaspace.getLocalUserSpace({ lock: false });
    await defaultLocalUserSpace.initialized;
    const sourceSpace = new InnerSpace_V1({
        ...DEFAULT_INNER_SPACE_DATA_V1,
        name: 'source',
        uuid: 'source_uuid',
        description: 'sender durable space',
    });
    await sourceSpace.initialized;
    const destSpace = new InnerSpace_V1({
        ...DEFAULT_INNER_SPACE_DATA_V1,
        name: 'dest',
        uuid: 'dest_uuid',
        description: 'receiver (domain provider) durable space',
    });
    await destSpace.initialized;
    const senderCoordinator = new SyncSagaCoordinator();
    const receiverCoordinator = new SyncSagaCoordinator();
    async function newTestIbGib_stone({ ib = 'test', data }) {
        const stone = await Factory_V1.stone({
            parentPrimitiveIb: ib.split(' ').at(0) ?? 'test',
            ib,
            data,
            uuid: true,
        });
        return stone;
    }
    async function newTestPeer() {
        const peer = new SyncPeerInnerspace_V1(clone(SYNC_PEER_INNERSPACE_DEFAULT_DATA_V1));
        await peer.initialized;
        await peer.initializeOpts({
            sagaId: '',
            localMetaspace: metaspace,
            localSpace: sourceSpace,
            receiverSpace: destSpace,
            receiverCoordinator,
            receiverMetaspace: metaspace,
        });
        return peer;
    }
    const keystoneSvc = new KeystoneService_V1();
    // #endregion Init/Setup
    let senderIdentity = await keystoneSvc.genesis({
        masterSecret: SENDER_SECRET,
        configs: [SENDER_IDENTITY_SYNC_POOL_CONFIG],
        metaspace,
        space: sourceSpace,
    });
    // post the senderIdentity to receiver
    await metaspace.put({ ibGib: senderIdentity, space: destSpace });
    await metaspace.registerNewIbGib({ ibGib: senderIdentity, space: destSpace });
    let syncError = null;
    let xStone;
    let xStoneAddr;
    let peer;
    try {
        xStone = await newTestIbGib_stone({ ib: 'test-pingpong' });
        xStoneAddr = getIbGibAddr({ ibGib: xStone });
        await metaspace.put({ ibGib: xStone, space: sourceSpace });
        await metaspace.registerNewIbGib({ ibGib: xStone, space: sourceSpace });
        peer = await newTestPeer();
        const syncSaga = await senderCoordinator.sync({
            domainIbGibs: [xStone],
            senderIdentity,
            fnSenderSecret: async () => SENDER_SECRET,
            peer,
            localSpace: sourceSpace,
            metaspace,
            conflictStrategy: SyncConflictStrategy.optimisticWithLCS,
        });
        await syncSaga.done;
    }
    catch (error) {
        syncError = error;
    }
    await ifWe(sir, 'completes the entire sync saga successfully', async () => {
        const errorMsg = syncError ? extractErrorMsg(syncError) : '';
        iReckon(sir, syncError).asTo(`syncError: ${errorMsg}`).isGonnaBeFalsy();
    });
    await ifWe(sir, 'alpha dep graph matches on source and dest', async () => {
        const resGetDest = await getFromSpace({ space: destSpace, addr: xStoneAddr });
        iReckon(sir, resGetDest.success && resGetDest.ibGibs?.length === 1).asTo('xStone exists in destSpace').isGonnaBeTrue();
        if (resGetDest.success && resGetDest.ibGibs?.[0]) {
            const xStoneDest = resGetDest.ibGibs[0];
            const depGraphSource = await getDependencyGraph({ ibGib: xStone, space: sourceSpace });
            const depGraphDest = await getDependencyGraph({ ibGib: xStoneDest, space: destSpace });
            const equal = graphsAreEquivalent({ graphA: depGraphSource, graphB: depGraphDest });
            iReckon(sir, equal).asTo('graphs are equivalent').isGonnaBeTrue();
        }
    });
    await ifWe(sir, 'sessionIdentity evolved the expected number of times', async () => {
        const latestS = peer.currentSessionIdentity;
        iReckon(sir, latestS).asTo('latest session identity exists on peer').isGonnaBeTruthy();
        if (latestS) {
            iReckon(sir, latestS.data?.n).asTo('session identity data.n').isGonnaBe(2);
        }
    });
    await ifWe(sir, 'sender durable space has I (evolved frame)', async () => {
        const latestSenderIdentityAddr = await metaspace.getLatestAddr({
            addr: getIbGibAddr({ ibGib: senderIdentity }),
            space: sourceSpace,
        });
        iReckon(sir, latestSenderIdentityAddr).asTo('latestSenderIdentityAddr exists').isGonnaBeTruthy();
        if (latestSenderIdentityAddr) {
            const resGetSourceI = await getFromSpace({ space: sourceSpace, addr: latestSenderIdentityAddr });
            iReckon(sir, resGetSourceI.success && resGetSourceI.ibGibs?.length === 1).asTo('evolved I exists in sourceSpace').isGonnaBeTrue();
            if (resGetSourceI.success && resGetSourceI.ibGibs?.[0]) {
                const evolvedI = resGetSourceI.ibGibs[0];
                iReckon(sir, evolvedI.data?.n).asTo('evolved I data.n').isGonnaBe(1);
            }
        }
    });
    await ifWe(sir, 'receiver durable space has I (evolved frame)', async () => {
        const latestSenderIdentityAddr = await metaspace.getLatestAddr({
            addr: getIbGibAddr({ ibGib: senderIdentity }),
            space: sourceSpace,
        });
        iReckon(sir, latestSenderIdentityAddr).asTo('latestSenderIdentityAddr exists').isGonnaBeTruthy();
        if (latestSenderIdentityAddr) {
            const resGetDestI = await getFromSpace({ space: destSpace, addr: latestSenderIdentityAddr });
            iReckon(sir, resGetDestI.success && resGetDestI.ibGibs?.length === 1).asTo('evolved I exists in destSpace').isGonnaBeTrue();
            if (resGetDestI.success && resGetDestI.ibGibs?.[0]) {
                const evolvedI = resGetDestI.ibGibs[0];
                iReckon(sir, evolvedI.data?.n).asTo('evolved I data.n').isGonnaBe(1);
            }
        }
    });
    await ifWe(sir, 'sender durable space has full S dep graph', async () => {
        const latestS = peer.currentSessionIdentity;
        iReckon(sir, latestS).asTo('latestS exists').isGonnaBeTruthy();
        if (latestS) {
            const depGraphS = await getDependencyGraph({ ibGib: latestS, space: sourceSpace });
            const addrs = Object.keys(depGraphS);
            iReckon(sir, addrs.length > 2).asTo('number of nodes in S graph > 2').isGonnaBeTrue();
        }
    });
    await ifWe(sir, 'receiver durable space has full S dep graph', async () => {
        const latestS = peer.currentSessionIdentity;
        iReckon(sir, latestS).asTo('latestS exists').isGonnaBeTruthy();
        if (latestS) {
            const depGraphS = await getDependencyGraph({ ibGib: latestS, space: destSpace });
            const addrs = Object.keys(depGraphS);
            iReckon(sir, addrs.length > 2).asTo('number of nodes in S graph in destSpace > 2').isGonnaBeTrue();
        }
    });
});
//# sourceMappingURL=sync-withid.pingpong.respec.mjs.map