/**
 * @module sync-innerspace-dest-ahead.withid.respec
 *
 * Verifies Destination Ahead (Remote Newer) Sync Scenario using InnerSpace with Identity.
 */
import { respecfully, iReckon, ifWe } from '@ibgib/helper-gib/dist/respec-gib/respec-gib.mjs';
const maam = `[${import.meta.url}]`, sir = maam;
import { clone, delay } 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 { getFromSpace, putInSpace, registerNewIbGib } from '../witness/space/space-helper.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 { createTimelineRootTestHelper } from '../test-helpers.mjs';
import { mut8Timeline } from '../timeline/timeline-api.mjs';
import { DEFAULT_INNER_SPACE_DATA_V1 } from '../witness/space/inner-space/inner-space-types.mjs';
import { SyncPeerInnerspace_V1 } from './sync-peer/sync-peer-innerspace/sync-peer-innerspace-v1.mjs';
import { SYNC_PEER_INNERSPACE_DEFAULT_DATA_V1 } from './sync-peer/sync-peer-innerspace/sync-peer-innerspace-constants.mjs';
import { getDependencyGraph, graphsAreEquivalent } from '../common/other/graph-helper.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';
const logalot = GLOBAL_LOG_A_LOT || true;
const lc = `[sync-innerspace-dest-ahead.withid.respec]`;
const SENDER_SECRET = 'test-sender-secret-dest-ahead-withid';
const SENDER_IDENTITY_SYNC_POOL_CONFIG = createStandardPoolConfig({
    id: POOL_ID_SYNC,
    salt: 'senderidentitysyncsaltdestaheadwithid',
    verbs: [KEYSTONE_VERB_SYNC],
    size: 200,
    sequential: 1,
    random: 1,
    targetBinding: 2,
    replenishStrategy: KeystoneReplenishStrategy.topUp,
});
await respecfully(sir, `Sync InnerSpaces (Dest Ahead) with Identity`, async () => {
    let metaspace;
    let sourceSpace;
    let destSpace;
    // #region Round 1 - Dest Ahead Sync with Identity
    await respecfully(sir, `Dest Ahead (Remote Newer with Identity)`, async () => {
        // 1. Setup Spaces
        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);
        }
        sourceSpace = new InnerSpace_V1({
            ...DEFAULT_INNER_SPACE_DATA_V1,
            name: 'source',
            uuid: 'source_uuid',
            description: 'source test space',
        });
        await sourceSpace.initialized;
        destSpace = new InnerSpace_V1({
            ...DEFAULT_INNER_SPACE_DATA_V1,
            name: 'dest',
            uuid: 'dest_uuid',
            description: 'dest test space',
        });
        await destSpace.initialized;
        // 2. Setup Identities
        const keystoneSvc = new KeystoneService_V1();
        const r1_senderIdentity_v0_source = await keystoneSvc.genesis({
            masterSecret: SENDER_SECRET,
            configs: [SENDER_IDENTITY_SYNC_POOL_CONFIG],
            metaspace,
            space: sourceSpace,
        });
        // Register sender identity on destination space as well
        await metaspace.put({ ibGib: r1_senderIdentity_v0_source, space: destSpace });
        await metaspace.registerNewIbGib({ ibGib: r1_senderIdentity_v0_source, space: destSpace });
        // #region r1 setup
        // 3. Seed Data (Dest Ahead)
        // Root -> V1 (Shared) -> V2 (Dest has New)
        // Source only has V1.
        console.log(`${lc} Seeding data for Dest Ahead scenario...`);
        // Root (V0)
        const r1_alpha_v0_source = await createTimelineRootTestHelper({
            ib: 'timeline_root_dest_ahead',
            data: { type: 'root', label: 'Root' },
            space: sourceSpace,
        });
        const r1_alpha_v0_source_addr = getIbGibAddr({ ibGib: r1_alpha_v0_source });
        // V1 (Both have it, but we create in source and copy to dest)
        const r1_alpha_v1_source = await mut8Timeline({
            timeline: r1_alpha_v0_source,
            mut8Opts: { dataToAddOrPatch: { type: 'comment', label: 'V1' } },
            metaspace,
            space: sourceSpace,
        });
        const r1_alpha_v1_source_addr = getIbGibAddr({ ibGib: r1_alpha_v1_source });
        // Transfer Root & V1 to Dest
        const initialDepGraph = await getDependencyGraph({ ibGibs: [r1_alpha_v0_source, r1_alpha_v1_source], space: sourceSpace });
        await putInSpace({ space: destSpace, ibGibs: Object.values(initialDepGraph) });
        await registerNewIbGib({ space: destSpace, ibGib: r1_alpha_v0_source });
        await registerNewIbGib({ space: destSpace, ibGib: r1_alpha_v1_source });
        // V2 (Created in Dest ONLY)
        const r1_alpha_v2_dest_newer = await mut8Timeline({
            timeline: r1_alpha_v1_source,
            mut8Opts: { dataToAddOrPatch: { type: 'comment', label: 'V2' } },
            metaspace,
            space: destSpace, // Mutate in Dest
        });
        const r1_alpha_v2_dest_newer_addr = getIbGibAddr({ ibGib: r1_alpha_v2_dest_newer });
        // #endregion r1 setup
        // #region r1 verify pre
        await respecfully(sir, `r1 verify pre`, async () => {
            await ifWe(sir, `verify setup state matches expectations`, async () => {
                const sourceHasV0 = await getFromSpace({ space: sourceSpace, addr: r1_alpha_v0_source_addr });
                const sourceHasV1 = await getFromSpace({ space: sourceSpace, addr: r1_alpha_v1_source_addr });
                const sourceHasV2 = await getFromSpace({ space: sourceSpace, addr: r1_alpha_v2_dest_newer_addr });
                const destHasV0 = await getFromSpace({ space: destSpace, addr: r1_alpha_v0_source_addr });
                const destHasV1 = await getFromSpace({ space: destSpace, addr: r1_alpha_v1_source_addr });
                const destHasV2 = await getFromSpace({ space: destSpace, addr: r1_alpha_v2_dest_newer_addr });
                iReckon(sir, sourceHasV0.success).asTo('source has V0').isGonnaBeTrue();
                iReckon(sir, sourceHasV1.success).asTo('source has V1').isGonnaBeTrue();
                iReckon(sir, sourceHasV2.success).asTo('source does NOT have V2').isGonnaBeFalse();
                iReckon(sir, destHasV0.success).asTo('dest has V0').isGonnaBeTrue();
                iReckon(sir, destHasV1.success).asTo('dest has V1').isGonnaBeTrue();
                iReckon(sir, destHasV2.success).asTo('dest has V2').isGonnaBeTrue();
            });
        });
        // #endregion r1 verify pre
        // 4. Setup Coordinator & Peer
        const senderCoordinator = new SyncSagaCoordinator();
        const receiverCoordinator = new SyncSagaCoordinator();
        const peer = new SyncPeerInnerspace_V1(clone(SYNC_PEER_INNERSPACE_DEFAULT_DATA_V1));
        await peer.initialized;
        await peer.initializeOpts({
            sagaId: '',
            localMetaspace: metaspace,
            localSpace: sourceSpace, // "Client"
            receiverSpace: destSpace, // "Server"
            receiverCoordinator,
            receiverMetaspace: metaspace,
        });
        // #region r1 syncs
        // 5. Run Sync (Source Pushes V1, but Remote is Ahead with V2)
        console.log(`${lc} Running Sync...`);
        const resSync = await senderCoordinator.sync({
            peer: peer,
            localSpace: sourceSpace,
            metaspace: metaspace,
            domainIbGibs: [r1_alpha_v1_source],
            senderIdentity: r1_senderIdentity_v0_source,
            fnSenderSecret: async () => SENDER_SECRET,
        });
        await resSync.done;
        // #endregion r1 syncs
        // #region r1 verify post
        await respecfully(sir, `r1 verify post`, async () => {
            await ifWe(sir, `verify V2 and its history are now present on Source`, async () => {
                const sourceHasV2 = await getFromSpace({ space: sourceSpace, addr: r1_alpha_v2_dest_newer_addr });
                iReckon(sir, sourceHasV2.success).asTo('Source now has V2').isGonnaBeTrue();
                if (sourceHasV2.success && sourceHasV2.ibGibs?.[0]) {
                    const v2IbGib = sourceHasV2.ibGibs[0];
                    iReckon(sir, v2IbGib.data?.label).asTo('V2 label matches').isGonnaBe('V2');
                }
                // Verify V2 dependency graph equivalence
                const sourceV2Graph = await getDependencyGraph({ ibGibAddr: r1_alpha_v2_dest_newer_addr, space: sourceSpace });
                const destV2Graph = await getDependencyGraph({ ibGibAddr: r1_alpha_v2_dest_newer_addr, space: destSpace });
                const graphsEqual = graphsAreEquivalent({ graphA: sourceV2Graph, graphB: destV2Graph });
                iReckon(sir, graphsEqual).asTo('V2 dependency graphs are identical on source and dest').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) {
                    // Connect handshake is no-op, init request = n=1, commit request = n=2
                    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: r1_senderIdentity_v0_source }),
                    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: r1_senderIdentity_v0_source }),
                    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, 'session keystone dependency graphs are equivalent on source and dest', async () => {
                const latestS = peer.currentSessionIdentity;
                iReckon(sir, latestS).asTo('latestS exists').isGonnaBeTruthy();
                if (latestS) {
                    const depGraphSource = await getDependencyGraph({ ibGib: latestS, space: sourceSpace });
                    const depGraphDest = await getDependencyGraph({ ibGib: latestS, space: destSpace });
                    const equal = graphsAreEquivalent({ graphA: depGraphSource, graphB: depGraphDest });
                    iReckon(sir, equal).asTo('session graphs are equivalent').isGonnaBeTrue();
                }
            });
        });
        // #endregion r1 verify post
    });
    // #endregion Round 1 - Dest Ahead Sync with Identity
});
//# sourceMappingURL=sync-innerspace-dest-ahead.withid.respec.mjs.map