/**
 * @module sync-conflict-text-merge.withid.respec
 *
 * Verifies text merge (LCS algorithm) in SyncSagaCoordinator under identity.
 */
import { respecfully, ifWe, iReckon } 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 { TestTransformer } from '../test-helpers.mjs';
import { SyncSagaCoordinator } from './sync-saga-coordinator.mjs';
import { getFromSpace } 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 { 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 { getIbGibsFromCache_fallbackToSpaces, getTjpAddr } from '../common/other/ibgib-helper.mjs';
import { SYNC_PEER_INNERSPACE_DEFAULT_DATA_V1 } from './sync-peer/sync-peer-innerspace/sync-peer-innerspace-constants.mjs';
import { SyncConflictStrategy } from './sync-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';
const logalot = GLOBAL_LOG_A_LOT;
const lc = sir;
const SENDER_SECRET = 'test-sender-secret-conflict-text-merge-withid';
const SENDER_IDENTITY_SYNC_POOL_CONFIG = createStandardPoolConfig({
    id: POOL_ID_SYNC,
    salt: 'senderidentitysyncsaltconflicttextmergewithid',
    verbs: [KEYSTONE_VERB_SYNC],
    size: 200,
    sequential: 1,
    random: 1,
    targetBinding: 2,
    replenishStrategy: KeystoneReplenishStrategy.topUp,
});
await respecfully(sir, `Text merge (LCS) conflict resolution 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: 'source test space',
    });
    await sourceSpace.initialized;
    const destSpace = new InnerSpace_V1({
        ...DEFAULT_INNER_SPACE_DATA_V1,
        name: 'dest',
        uuid: 'dest_uuid',
        description: 'dest test space',
    });
    await destSpace.initialized;
    const testTransformer = new TestTransformer(metaspace, sourceSpace, destSpace);
    if (logalot) {
        console.log(`${lc} Setting up Coordinators...`);
    }
    const senderCoordinator = new SyncSagaCoordinator();
    const receiverCoordinator = new SyncSagaCoordinator();
    // 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 identity in both spaces
    await metaspace.put({ ibGib: r1_senderIdentity_v0_source, space: destSpace });
    await metaspace.registerNewIbGib({ ibGib: r1_senderIdentity_v0_source, space: destSpace });
    let senderIdentityLatestSource = r1_senderIdentity_v0_source;
    async function newTestPeer() {
        const peer = new SyncPeerInnerspace_V1(clone(SYNC_PEER_INNERSPACE_DEFAULT_DATA_V1));
        await peer.initialized;
        await peer.initializeOpts({
            localMetaspace: metaspace,
            localSpace: sourceSpace,
            receiverSpace: destSpace,
            receiverCoordinator: receiverCoordinator,
            receiverMetaspace: metaspace,
        });
        return peer;
    }
    // #endregion Init/Setup
    // #region Round 1: Seed common history & initial text
    const _r1 = testTransformer.newRound({
        name: 'r1_seed_common_text',
        description: 'Create timeline with initial text, sync to establish common history'
    });
    if (logalot) {
        console.log(`${lc} R1: Creating initial alpha with text...`);
    }
    // Create alpha with initial text on source
    const INITIAL_TEXT = "This is the initial quiz question.\nIt has multiple lines.\nStudents will answer this.";
    const r1_alpha_v0_source = await testTransformer.create({
        atom: 'alpha',
        in: 'source',
        data: { text: INITIAL_TEXT },
        name: 'r1_alpha_v0_source'
    });
    const alpha_tjpAddr = getTjpAddr({ ibGib: r1_alpha_v0_source.ibGib, defaultIfNone: 'incomingAddr' });
    if (logalot) {
        console.log(`${lc} R1: Syncing initial alpha to dest...`);
    }
    let r1_syncSaga;
    const peerR1 = await newTestPeer();
    try {
        r1_syncSaga = await senderCoordinator.sync({
            peer: peerR1,
            localSpace: sourceSpace,
            metaspace,
            domainIbGibs: [r1_alpha_v0_source.ibGib],
            conflictStrategy: SyncConflictStrategy.optimisticWithLCS,
            senderIdentity: senderIdentityLatestSource,
            fnSenderSecret: async () => SENDER_SECRET,
        });
        await r1_syncSaga.done;
        if (logalot) {
            console.log(`${lc} R1 Sync Complete.`);
        }
    }
    catch (e) {
        console.error(`${lc} R1 Sync Failed:`, e);
        iReckon(sir, false).asTo(`R1 Sync failed: ${e}`).isGonnaBeTruthy();
        return;
    }
    // Verify both sides have same text
    await respecfully(sir, `r1 verify post`, async () => {
        try {
            const r1_alpha_sourceKV = await senderCoordinator.getKnowledgeMap({
                space: sourceSpace,
                metaspace,
                domainIbGibs: [r1_alpha_v0_source.ibGib]
            });
            const r1_alpha_source_tipAddr = r1_alpha_sourceKV[alpha_tjpAddr];
            if (!r1_alpha_source_tipAddr) {
                await ifWe(sir, 'r1_alpha_source_tipAddr is falsy?', async () => {
                    iReckon(sir, true).asTo('fail').isGonnaBe(false);
                });
                return;
            }
            const r1_alpha_destKV = await senderCoordinator.getKnowledgeMap({
                space: destSpace,
                metaspace,
                domainIbGibs: [r1_alpha_v0_source.ibGib]
            });
            const r1_alpha_dest_tipAddr = r1_alpha_destKV[alpha_tjpAddr];
            if (!r1_alpha_dest_tipAddr) {
                await ifWe(sir, 'r1_alpha_dest_tipAddr is falsy?', async () => {
                    iReckon(sir, true).asTo('fail').isGonnaBe(false);
                });
                return;
            }
            await ifWe(sir, 'r1 tip addrs match', async () => {
                iReckon(sir, r1_alpha_source_tipAddr).asTo('R1 source/dest have same tip').isGonnaBe(r1_alpha_dest_tipAddr);
            });
            await ifWe(sir, 'r1 text synced correctly', async () => {
                const resGet_destTip = await getFromSpace({ space: destSpace, addr: r1_alpha_dest_tipAddr });
                const destTipIbGib = resGet_destTip.ibGibs[0];
                iReckon(sir, destTipIbGib.data.text).asTo('Dest has initial text').isGonnaBe(INITIAL_TEXT);
            });
            await ifWe(sir, 'sessionIdentity evolved for Round 1', async () => {
                const latestS = peerR1.currentSessionIdentity;
                iReckon(sir, latestS).asTo('session identity exists').isGonnaBeTruthy();
                if (latestS) {
                    iReckon(sir, latestS.data?.n).asTo('session identity data.n').isGonnaBe(2);
                }
            });
        }
        catch (error) {
            console.error(`${lc} ${extractErrorMsg(error)}`);
            iReckon(sir, true).asTo('R1 verify errored').isGonnaBeFalse();
        }
    });
    // #endregion Round 1: Seed common history & initial text
    // Fetch evolved user identity from space
    const latestSenderIdentityAddr = await metaspace.getLatestAddr({
        addr: getIbGibAddr({ ibGib: r1_senderIdentity_v0_source }),
        space: sourceSpace,
    });
    if (!latestSenderIdentityAddr) {
        throw new Error("Could not find latest sender identity after R1");
    }
    const resGetSenderI = await getFromSpace({ space: sourceSpace, addr: latestSenderIdentityAddr });
    senderIdentityLatestSource = resGetSenderI.ibGibs[0];
    // Copy/register evolved user identity to dest space
    await metaspace.put({ ibGib: senderIdentityLatestSource, space: destSpace });
    await metaspace.registerNewIbGib({ ibGib: senderIdentityLatestSource, space: destSpace });
    // #region Round 2: Simple append - different parts of text
    const _r2 = testTransformer.newRound({
        name: 'r2_simple_append',
        description: 'Source appends to end, dest appends to beginning - should merge both'
    });
    // #region r2 source edits
    if (logalot) {
        console.log(`${lc} R2: Source appending to end of text...`);
    }
    const SOURCE_APPEND = "\nInstructor added hint.";
    const r2_alpha_v1_source_appendedText = await testTransformer.mut8({
        ibGib: r1_alpha_v0_source.ibGib,
        in: 'source',
        strField: { name: 'text', value: INITIAL_TEXT + SOURCE_APPEND },
        name: 'r2_alpha_v1_source_appendedText',
    });
    // #endregion r2 source edits
    // #region r2 dest edits
    if (logalot) {
        console.log(`${lc} R2: Dest prepending to beginning of text...`);
    }
    const resGet_r2_dest = await getFromSpace({ space: destSpace, addr: r1_alpha_v0_source.addr });
    const r2_v0_alpha_dest = resGet_r2_dest.ibGibs[0];
    const DEST_PREPEND = "Student note: Confusing!\n";
    const r2_alpha_v1_dest_prependedText = await testTransformer.mut8({
        ibGib: r2_v0_alpha_dest,
        in: 'dest',
        strField: { name: 'text', value: DEST_PREPEND + INITIAL_TEXT },
        name: 'r2_alpha_v1_dest_prependedText',
    });
    // #endregion r2 dest edits
    await respecfully(sir, `r2 verify pre`, async () => {
        await ifWe(sir, 'texts as expected', async () => {
            iReckon(sir, r2_alpha_v1_source_appendedText.ibGib.data?.text.includes(INITIAL_TEXT)).asTo('alpha source has initial text').isGonnaBeTrue();
            iReckon(sir, r2_alpha_v1_source_appendedText.ibGib.data?.text.includes(SOURCE_APPEND)).asTo('alpha source has appended text').isGonnaBeTrue();
            iReckon(sir, r2_alpha_v1_source_appendedText.ibGib.data?.text.includes(DEST_PREPEND)).asTo('alpha source does NOT have prepended text').isGonnaBeFalse();
            iReckon(sir, r2_alpha_v1_dest_prependedText.ibGib.data?.text.includes(INITIAL_TEXT)).asTo('alpha dest has initial text').isGonnaBeTrue();
            iReckon(sir, r2_alpha_v1_dest_prependedText.ibGib.data?.text.includes(SOURCE_APPEND)).asTo('alpha dest does NOT have appended text').isGonnaBeFalse();
            iReckon(sir, r2_alpha_v1_dest_prependedText.ibGib.data?.text.includes(DEST_PREPEND)).asTo('alpha dest has prepended text').isGonnaBeTrue();
        });
    });
    if (logalot) {
        console.log(`${lc} Running r2 Sync (simple data.text conflict)...`);
    }
    let r2_syncSaga;
    const peerR2 = await newTestPeer();
    try {
        r2_syncSaga = await senderCoordinator.sync({
            peer: peerR2,
            localSpace: sourceSpace,
            metaspace,
            domainIbGibs: [r2_alpha_v1_source_appendedText.ibGib],
            conflictStrategy: SyncConflictStrategy.optimisticWithLCS,
            senderIdentity: senderIdentityLatestSource,
            fnSenderSecret: async () => SENDER_SECRET,
        });
        await r2_syncSaga.done;
    }
    catch (e) {
        console.error(`${lc} R2 Sync Failed:`, e);
        iReckon(sir, false).asTo(`R2 failed: ${e}`).isGonnaBeTruthy();
        return;
    }
    await respecfully(sir, `r2 verify post`, async () => {
        const kv_source = await senderCoordinator.getKnowledgeMap({ space: sourceSpace, metaspace, domainIbGibs: [r1_alpha_v0_source.ibGib] });
        const kv_dest = await senderCoordinator.getKnowledgeMap({ space: destSpace, metaspace, domainIbGibs: [r1_alpha_v0_source.ibGib] });
        const r2_alpha_source_tipAddr = kv_source[alpha_tjpAddr];
        if (!r2_alpha_source_tipAddr) {
            await ifWe(sir, 'r2_alpha_source_tipAddr falsy?', async () => {
                iReckon(sir, true).asTo('fails').isGonnaBe(false);
            });
            return;
        }
        const r2_alpha_dest_tipAddr = kv_dest[alpha_tjpAddr];
        if (!r2_alpha_dest_tipAddr) {
            await ifWe(sir, 'r2_alpha_dest_tipAddr falsy?', async () => {
                iReckon(sir, true).asTo('fails').isGonnaBe(false);
            });
            return;
        }
        const [r2_alpha_source_tip] = await getIbGibsFromCache_fallbackToSpaces({
            addrs: [r2_alpha_source_tipAddr],
            space: sourceSpace,
        });
        const [r2_alpha_dest_tip] = await getIbGibsFromCache_fallbackToSpaces({
            addrs: [r2_alpha_dest_tipAddr],
            space: sourceSpace,
        });
        await ifWe(sir, 'r2 tip addrs match', async () => {
            iReckon(sir, r2_alpha_source_tipAddr).asTo('alpha').isGonnaBe(r2_alpha_dest_tipAddr);
        });
        await ifWe(sir, 'r2 text merged correctly', async () => {
            iReckon(sir, r2_alpha_source_tip.data?.text).asTo('source/dest same text').isGonnaBe(r2_alpha_dest_tip.data?.text);
            const text = r2_alpha_source_tip.data?.text;
            iReckon(sir, text).asTo('R2 has both prepend and append').isGonnaBe(DEST_PREPEND + INITIAL_TEXT + SOURCE_APPEND);
        });
        await ifWe(sir, 'sessionIdentity evolved for Round 2', async () => {
            const latestS = peerR2.currentSessionIdentity;
            iReckon(sir, latestS).asTo('session identity exists').isGonnaBeTruthy();
            if (latestS) {
                // Conflict graft sync triggers extra turn (n=3)
                iReckon(sir, latestS.data?.n).asTo('session identity data.n').isGonnaBe(3);
            }
        });
    });
    // #endregion Round 2: Simple append - different parts of text
});
//# sourceMappingURL=sync-conflict-text-merge.withid.respec.mjs.map