import { extractErrorMsg, hash, pretty } from "@ibgib/helper-gib/dist/helpers/utils-helper.mjs";
import { getIbAndGib, getIbGibAddr } from "@ibgib/ts-gib/dist/helper.mjs";
import { validateIbGibIntrinsically } from "@ibgib/ts-gib/dist/V1/validate-helper.mjs";
import { mut8 } from "@ibgib/ts-gib/dist/V1/transforms/mut8.mjs";
import { Factory_V1 } from "@ibgib/ts-gib/dist/V1/factory.mjs";
import { getGib } from "@ibgib/ts-gib/dist/V1/transforms/transform-helper.mjs";
import { GLOBAL_LOG_A_LOT } from "../core-constants.mjs";
import { KEYSTONE_ATOM } from "./keystone-constants.mjs";
import { KeystoneReplenishStrategy, KEYSTONE_REPLENISH_STRATEGY_VALID_VALUES } from "./keystone-types.mjs";
import { KeystoneStrategyFactory } from "./strategy/keystone-strategy-factory.mjs";
const logalot = GLOBAL_LOG_A_LOT;
/**
 * space-delimited keystone ib containing select keystone metadata.
 *
 * NOTE: This must match {@link parseKeystoneIb}
 * @see {@link KeystoneIbInfo_V1}
 */
export async function getKeystoneIb({ keystoneData, }) {
    const lc = `[${getKeystoneIb.name}]`;
    try {
        if (logalot) {
            console.log(`${lc} starting... (I: c3022a146faac9730154f34d1439a225)`);
        }
        const atom = KEYSTONE_ATOM;
        const ib = [
            atom,
        ].join(' ');
        return ib;
    }
    catch (error) {
        console.error(`${lc} ${extractErrorMsg(error)}`);
        throw error;
    }
    finally {
        if (logalot) {
            console.log(`${lc} complete.`);
        }
    }
}
/**
 * NOTE: This must match {@link getKeystoneIb}
 * @see {@link KeystoneIbInfo_V1}
 */
export async function parseKeystoneIb({ ib, }) {
    const lc = `[${parseKeystoneIb.name}]`;
    try {
        if (logalot) {
            console.log(`${lc} starting... (I: 73cb6832984255ed48b2f44db6a21e25)`);
        }
        const [atom] = ib.split(' ');
        if (atom !== KEYSTONE_ATOM) {
            throw new Error(`invalid keystone ib. atom found in ib (${atom}) does not match keystone atom (${KEYSTONE_ATOM}) (E: 79b3d587824c4271b6e60acc76e0c825)`);
        }
        return {
            atom,
        };
    }
    catch (error) {
        console.error(`${lc} ${extractErrorMsg(error)}`);
        throw error;
    }
    finally {
        if (logalot) {
            console.log(`${lc} complete.`);
        }
    }
}
/**
 * The Policy Engine.
 * Calculates exactly which challenges MUST be consumed based on config and demands.
 * Enforces STRICT DISTINCTNESS (No double-dipping).
 */
export function getDeterministicRequirements({ pool, requiredChallengeIds, targetAddr }) {
    const behavior = pool.config.behavior;
    const mandatory = new Set();
    // Start with all available IDs.
    // We assume Object.keys respects insertion order (ES2015+), crucial for FIFO.
    let available = Object.keys(pool.challenges);
    // ---------------------------------------------------------
    // 1. Alice's Demands (Explicit Requirements)
    // ---------------------------------------------------------
    if (requiredChallengeIds && requiredChallengeIds.length > 0) {
        for (const id of requiredChallengeIds) {
            if (!pool.challenges[id]) {
                throw new Error(`(UNEXPECTED) Required challenge ID not found in pool: ${id} (E: 10c5619d80d8cf6cc84ffbf8be0dbc25)`);
            }
            // Strict: Consume it.
            if (!available.includes(id)) {
                // Should be caught by check above, but handles duplicates in 'demands'
                continue;
            }
            mandatory.add(id);
        }
        // Remove from available pool
        available = available.filter(id => !mandatory.has(id));
    }
    // ---------------------------------------------------------
    // 2. Target Binding (Explicit Buckets)
    // ---------------------------------------------------------
    if (behavior.targetBindingChars > 0 && targetAddr) {
        const { gib } = getIbAndGib({ ibGibAddr: targetAddr });
        if (gib) {
            // Get required hex prefixes (e.g. 'a', 'b', 'c', '1')
            const prefixes = gib.substring(0, behavior.targetBindingChars).toLowerCase();
            for (const char of prefixes) {
                // Look in the Explicit Bucket
                const bucket = pool.bindingMap[char] || [];
                // Find the first ID in this bucket that is still in 'available'
                const match = bucket.find(id => available.includes(id));
                if (!match) {
                    throw new Error(`Entropy Exhaustion. Cannot satisfy binding for char '${char}'. (E: 341b95dc3a58be3e083d1d9c4a0c4925)`);
                }
                // Strict: Consume it.
                mandatory.add(match);
                available = available.filter(id => id !== match);
            }
        }
    }
    // ---------------------------------------------------------
    // 3. FIFO (Sequential)
    // ---------------------------------------------------------
    if (behavior.selectSequentially > 0) {
        // Take the first N from the remaining available list
        if (available.length < behavior.selectSequentially) {
            console.error(`[getDeterministicRequirements] Entropy Exhaustion! AvailableCount: ${available.length}, Required Seq: ${behavior.selectSequentially}. Available IDs: ${available.join(',')}`);
            throw new Error(`Entropy Exhaustion. Insufficient challenges for FIFO requirement. (E: 9efa88003e1d5eccb837c85154ee5625)`);
        }
        const fifoIds = available.slice(0, behavior.selectSequentially);
        fifoIds.forEach(id => mandatory.add(id));
        // Remove from available
        available = available.slice(behavior.selectSequentially);
    }
    return { mandatoryIds: mandatory, availableIds: available };
}
/**
 * Helper to update the Binding Map when adding new Challenge IDs.
 * Uses "Implicit Bucketing" (ID start char) but can be extended for full coverage.
 */
export function addToBindingMap(map, challengeId) {
    const firstChar = challengeId.charAt(0).toLowerCase();
    // Validate it is hex
    if (/[0-9a-f]/.test(firstChar)) {
        if (!map[firstChar])
            map[firstChar] = [];
        map[firstChar].push(challengeId);
        // OPTIONAL: Implement Full Coverage Strategy here?
        // e.g. map[challengeId[1]].push(challengeId) ...
        // For V1, we stick to Native/Implicit bucket (Index 0).
    }
}
/**
 * Helper to clean up Binding Map when removing IDs.
 */
export function removeFromBindingMap(map, challengeId) {
    // Since we don't know exactly which buckets an ID is in (if we did multi-bucket),
    // we strictly should scan all. For V1 Native, we check first char.
    // SAFE IMPLEMENTATION: Scan all buckets.
    for (const key of Object.keys(map)) {
        map[key] = map[key].filter(id => id !== challengeId);
    }
}
/**
 * Selects the specific pool to use for an operation based on ID, filter criteria, or verb authorization.
 *
 * @returns The matching KeystoneChallengePool
 * @throws If no pool matches or if multiple pools match but one was expected.
 */
export function resolveTargetPool({ pools, poolId, poolFilter, verb, }) {
    const lc = `[resolveTargetPool]`;
    try {
        let pool;
        if (poolId) {
            // 1. Explicit ID Strategy
            pool = pools.find(p => p.id === poolId);
            if (!pool) {
                throw new Error(`Pool not found with ID: ${poolId} (E: 4a2b17428515c1e82813158581898125)`);
            }
        }
        else if (poolFilter) {
            // 2. Filter Strategy
            const matches = pools.filter(poolFilter);
            if (matches.length === 0) {
                throw new Error(`No pool matched the provided filter. (E: 5b3c27428515c1e82813158581898125)`);
            }
            // For now, we take the first match. In future, might want to be strict about uniqueness.
            pool = matches[0];
        }
        else {
            // 3. Auto-Resolution by Verb Strategy
            if (!verb) {
                throw new Error(`Cannot auto-resolve pool without a verb. (E: 6c4d37428515c1e82813158581898125)`);
            }
            // Priority A: Look for Specific Match (Pool explicitly lists this verb)
            pool = pools.find(p => p.config.allowedVerbs && p.config.allowedVerbs.includes(verb));
            // Priority B: Look for General/Default (No restrictions / Wildcard)
            if (!pool) {
                pool = pools.find(p => !p.config.allowedVerbs || p.config.allowedVerbs.length === 0);
            }
            if (!pool) {
                throw new Error(`No suitable pool found for verb: ${verb} (E: 7d5e47428515c1e82813158581898125)`);
            }
        }
        // 4. Authorization Check (Applies to all strategies if verb is present)
        if (verb && pool.config.allowedVerbs && pool.config.allowedVerbs.length > 0) {
            if (!pool.config.allowedVerbs.includes(verb)) {
                throw new Error(`Pool ${pool.id} is not authorized for verb: ${verb} (E: 8e6f57428515c1e82813158581898125)`);
            }
        }
        return pool;
    }
    catch (error) {
        console.error(`${lc} ${extractErrorMsg(error)}`);
        throw error;
    }
}
/**
 * Calculates the complete list of Challenge IDs to solve for a given operation.
 * Combines Deterministic requirements (Mandatory/Binding/FIFO) with Stochastic requirements.
 *
 * @returns Array of unique challenge IDs.
 */
export function selectChallengeIds({ pool, targetAddr, requiredChallengeIds, }) {
    const lc = `[selectChallengeIds]`;
    try {
        // 1. Get Deterministic Requirements
        const { mandatoryIds, availableIds } = getDeterministicRequirements({
            pool,
            requiredChallengeIds,
            targetAddr
        });
        // 2. Stochastic Selection
        const randomCount = pool.config.behavior.selectRandomly;
        const randomIds = [];
        if (randomCount > 0) {
            if (availableIds.length < randomCount) {
                throw new Error(`Insufficient challenges for random requirement. Need ${randomCount}, have ${availableIds.length} (E: 9f7a67428515c1e82813158581898125)`);
            }
            // Shuffle & Pick
            // Note: simple Math.random sort is sufficient for V1 stochastic selection 
            // as we are just picking from valid available options.
            const shuffled = [...availableIds].sort(() => 0.5 - Math.random());
            randomIds.push(...shuffled.slice(0, randomCount));
        }
        // 3. Combine
        return [...mandatoryIds, ...randomIds];
    }
    catch (error) {
        console.error(`${lc} ${extractErrorMsg(error)}`);
        throw error;
    }
}
/**
 * Generates an opaque, collision-resistant ID for a challenge.
 * atow (2025/12/22) - Hash(Salt + Timestamp + Index)
 */
export async function generateOpaqueChallengeId({ salt, timestamp, index }) {
    // Use first 16 chars of hex (64 bits) for brevity + safety.
    const raw = await hash({ s: `${salt}${timestamp}${index}` });
    return raw.substring(0, 16);
}
/**
 * Calculates the NEXT state of the Challenge Pools given a specific consumption event.
 * Handles TopUp, ReplaceAll, Consume, and ScorchedEarth strategies.
 *
 * @returns The new array of KeystoneChallengePools (including the modified one).
 */
export async function applyReplenishmentStrategy({ prevPools, targetPoolId, consumedIds, masterSecret, strategy, config, }) {
    const lc = `[applyReplenishmentStrategy]`;
    try {
        const newPools = JSON.parse(JSON.stringify(prevPools));
        const targetIdx = newPools.findIndex((p) => p.id === targetPoolId);
        if (targetIdx === -1) {
            throw new Error(`Target pool ${targetPoolId} not found in keystone data. (E: 75200388d22744838634524233772545)`);
        }
        const pool = newPools[targetIdx];
        const poolSecret = await strategy.derivePoolSecret({ masterSecret });
        const timestamp = Date.now().toString();
        const strategyType = config.behavior.replenish;
        // Clean up Binding Map for consumed IDs
        consumedIds.forEach(id => {
            if (pool.bindingMap) {
                removeFromBindingMap(pool.bindingMap, id);
            }
        });
        if (strategyType === KeystoneReplenishStrategy.topUp) {
            // Remove consumed
            consumedIds.forEach(id => delete pool.challenges[id]);
            // Add New
            for (let i = 0; i < consumedIds.length; i++) {
                const newId = await generateOpaqueChallengeId({
                    salt: config.salt, timestamp, index: i
                });
                const solution = await strategy.generateSolution({
                    poolSecret, poolId: pool.id, challengeId: newId
                });
                pool.challenges[newId] = await strategy.generateChallenge({ solution });
                // Update Binding Map
                if (!pool.bindingMap) {
                    pool.bindingMap = {};
                }
                addToBindingMap(pool.bindingMap, newId);
            }
        }
        else if (strategyType === KeystoneReplenishStrategy.replaceAll) {
            pool.challenges = {};
            pool.bindingMap = {};
            for (let i = 0; i < config.behavior.size; i++) {
                const newId = await generateOpaqueChallengeId({
                    salt: config.salt, timestamp, index: i
                });
                const solution = await strategy.generateSolution({
                    poolSecret, poolId: pool.id, challengeId: newId
                });
                pool.challenges[newId] = await strategy.generateChallenge({ solution });
                addToBindingMap(pool.bindingMap, newId);
            }
        }
        else if (strategyType === KeystoneReplenishStrategy.consume) {
            consumedIds.forEach(id => delete pool.challenges[id]);
        }
        else if (strategyType === KeystoneReplenishStrategy.scorchedEarth) {
            pool.challenges = {};
            pool.bindingMap = {};
        }
        else {
            throw new Error(`Unknown replenish strategy: ${strategyType}. Valid list: ${pretty(KEYSTONE_REPLENISH_STRATEGY_VALID_VALUES)} (E: 0acf56f1e1486240080e11e8046d0825)`);
        }
        return newPools;
    }
    catch (error) {
        console.error(`${lc} ${extractErrorMsg(error)}`);
        throw error;
    }
}
/**
 * High-level orchestration helper.
 * 1. Instantiates Strategy.
 * 2. Derives Secret.
 * 3. Solves Challenges (Generates Solutions).
 * 4. Replenishes Pool (Calculates the next state of ALL pools).
 * 5. Constructs Proof.
 *
 * @returns The resulting Proof and the full list of Next Pools.
 */
export async function solveAndReplenish({ targetPoolId, prevPools, masterSecret, challengeIds, claim, requiredChallengeIds, }) {
    const lc = `[solveAndReplenish]`;
    try {
        if (logalot) {
            console.log(`${lc} starting... poolId: ${targetPoolId}, ids: ${challengeIds.length}`);
        }
        const pool = prevPools.find(p => p.id === targetPoolId);
        if (!pool) {
            throw new Error(`Target pool not found: ${targetPoolId} (E: a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6)`);
        }
        const strategy = KeystoneStrategyFactory.create({ config: pool.config });
        const poolSecret = await strategy.derivePoolSecret({ masterSecret });
        const solutions = [];
        // 1. Solve
        for (const id of challengeIds) {
            const solution = await strategy.generateSolution({
                poolSecret, poolId: pool.id, challengeId: id,
            });
            solutions.push(solution);
        }
        // 2. Construct Proof
        const proof = {
            claim,
            solutions,
            requiredChallengeIds: (requiredChallengeIds && requiredChallengeIds.length > 0) ? requiredChallengeIds : undefined
        };
        // 3. Replenish
        const nextPools = await applyReplenishmentStrategy({
            prevPools,
            targetPoolId: pool.id,
            consumedIds: challengeIds,
            masterSecret,
            strategy,
            config: pool.config
        });
        return { proof, nextPools };
    }
    catch (error) {
        console.error(`${lc} ${extractErrorMsg(error)}`);
        throw error;
    }
}
/**
 * Validates the transition from Prev -> Curr.
 * Enforces Cryptography AND Behavioral Policy.
 *
 * @returns Array of validation error strings. Empty array means Valid.
 */
export async function validateKeystoneTransition({ currentIbGib, prevIbGib, }) {
    const lc = `[${validateKeystoneTransition.name}]`;
    const errors = [];
    try {
        if (!currentIbGib) {
            throw new Error(`(UNEXPECTED) currentIbGib falsy? (E: 3c0f02655fa8279e386a079ebb604b25)`);
        }
        if (!prevIbGib) {
            throw new Error(`(UNEXPECTED) prevIbGib falsy? (E: 0d07c812634d839c784f31b8848ba825)`);
        }
        // intrinsic validation
        const validationErrors = await validateIbGibIntrinsically({ ibGib: currentIbGib });
        if (validationErrors && validationErrors.length > 0) {
            errors.push(...validationErrors);
        }
        const currData = currentIbGib.data;
        const prevData = prevIbGib.data;
        for (const proof of currData.proofs) {
            if (proof.solutions.length === 0) {
                errors.push(`Proof ${proof.id || 'unknown'} has no solutions.`);
                continue;
            }
            const poolId = proof.solutions[0].poolId;
            // Standard Verification (Internal Pools Only)
            // The pool MUST be present in the previous frame.
            const pool = prevData.challengePools.find(p => p.id === poolId);
            if (!pool) {
                errors.push(`Proof references unknown pool: ${poolId}`);
                continue;
            }
            await verifyProofAgainstPool({ proof, pool, errors });
        } // End proof loop
        // Revocation Logic checks
        if (currData.revocationInfo) {
            const target = currData.revocationInfo.proof.claim.target;
            const expectedTarget = getIbGibAddr({ ibGib: prevIbGib });
            if (target !== expectedTarget) {
                errors.push(`Revocation target mismatch. Expected ${expectedTarget}, got ${target}`);
            }
        }
        return errors;
    }
    catch (error) {
        console.error(`${lc} ${extractErrorMsg(error)}`);
        throw error; // System errors still throw
    }
    finally {
        if (logalot) {
            console.log(`${lc} complete.`);
        }
    }
}
/**
 * Helper to verify a single proof against a specific pool.
 */
export async function verifyProofAgainstPool({ proof, pool, errors, }) {
    const lc = `[${verifyProofAgainstPool.name}]`;
    try {
        if (logalot) {
            console.log(`${lc} starting... (I: b8f9b6085888eea2258bf579ecd5e825)`);
        }
        // 0. VERB AUTH
        if (pool.config.allowedVerbs && pool.config.allowedVerbs.length > 0) {
            if (!proof.claim.verb || !pool.config.allowedVerbs.includes(proof.claim.verb)) {
                errors.push(`Policy Violation: Pool ${pool.id} used for unauthorized verb ${proof.claim.verb}`);
            }
        }
        // 1. Reconstruct Deterministic Requirements
        const { mandatoryIds, availableIds } = getDeterministicRequirements({
            pool,
            requiredChallengeIds: proof.requiredChallengeIds,
            targetAddr: proof.claim.target // Not used extensively in V1 logic yet, mainly for logging/context
        });
        // 2. Check Mandatory
        const proofIds = new Set(proof.solutions.map(s => s.challengeId));
        for (const id of mandatoryIds) {
            if (!proofIds.has(id)) {
                errors.push(`Policy Violation: Missing mandatory challenge ${id}`);
            }
        }
        // 3. Stochastic
        const randomCandidates = [...proofIds].filter(id => !mandatoryIds.has(id));
        const requiredRandomCount = pool.config.behavior.selectRandomly;
        if (randomCandidates.length < requiredRandomCount) {
            errors.push(`Policy Violation: Insufficient random count. Need ${requiredRandomCount}, got ${randomCandidates.length}`);
        }
        // 4. Validity (Double Dip / Existence)
        for (const id of randomCandidates) {
            if (!availableIds.includes(id)) {
                errors.push(`Policy Violation: ID ${id} is invalid or double-dipped.`);
            }
        }
        // 5. Crypto
        const strategy = KeystoneStrategyFactory.create({ config: pool.config });
        for (const solution of proof.solutions) {
            const challenge = pool.challenges[solution.challengeId];
            if (!challenge) {
                errors.push(`Crypto Violation: Challenge ${solution.challengeId} not found in pool.`);
            }
            else {
                const isValid = await strategy.validateSolution({ solution, challenge });
                if (!isValid) {
                    errors.push(`Crypto Violation: Solution for ${solution.challengeId} is invalid.`);
                }
            }
        }
    }
    catch (error) {
        console.error(`${lc} ${extractErrorMsg(error)}`);
        throw error;
    }
    finally {
        if (logalot) {
            console.log(`${lc} complete.`);
        }
    }
}
export async function evolvePersistAndRegisterKeystone({ prevIbGib, newData, metaspace, space, }) {
    const lc = `[${evolvePersistAndRegisterKeystone.name}]`;
    try {
        if (logalot) {
            console.log(`${lc} starting... (I: 8b10e8920f08b7842803665834cf8925)`);
        }
        if (!prevIbGib.data) {
            throw new Error(`(UNEXPECTED) prevIbGib.data falsy? (E: 5e84875bf992c585b979e6c8ed5bf225)`);
        }
        if (prevIbGib.data.revocationInfo) {
            throw new Error(`Keystone has already been revoked (prevIbGib.data.revocationInfo truthy), so we cannot evolve the keystone. Keystone addr: ${getIbGibAddr({ ibGib: prevIbGib })} (E: 45d7f846556829de6b2a701838c3f825)`);
        }
        const prevData = prevIbGib.data;
        /**
         * we want to completely replace these keys, so we will remove them
         * from the data. This occurs first in the underlying mut8
         * transform.
         * @see {@link mut8}
         */
        let dataToRemove = {};
        if (prevData.proofs) {
            dataToRemove.proofs = [];
        }
        if (prevData.challengePools) {
            dataToRemove.challengePools = [];
        }
        if (prevData.frameDetails) {
            dataToRemove.frameDetails = {};
        }
        if (Object.keys(dataToRemove).length === 0) {
            dataToRemove = undefined;
        }
        const resMut8 = await mut8({
            src: prevIbGib,
            dataToRemove,
            dataToAddOrPatch: newData,
            // dna: false, // explicitly set to false just to show
            nCounter: true,
        });
        if (!!resMut8.intermediateIbGibs) {
            throw new Error(`(UNEXPECTED) resMut8.intermediateIbGibs truthy? I'm not sure if we expect there to be intermediateIbGibs, but I feel like we shouldn't. Pretty sure we shouldn't, definitely don't *want* them. (E: ba40d55d7c2d36d438c413886f148625)`);
        }
        if (!!resMut8.dnas) {
            throw new Error(`(UNEXPECTED) resMut8.dnas truthy? We do not want dnas with keystones. (E: 49470513d018f97d28024f4e82da3b25)`);
        }
        const newKeystoneIbGib = resMut8.newIbGib;
        // run validation here
        const errors = await validateKeystoneTransition({
            currentIbGib: newKeystoneIbGib,
            prevIbGib,
        });
        if (errors.length > 0) {
            console.error(`${lc} Validation Failed:\n${errors.join('\n')}`);
            throw new Error(`(UNEXPECTED) invalid keystone after we just evolved it? Errors: ${errors.join('; ')} (E: ae2c58406c1db7687879dfb89fc1f825)`);
        }
        // save and register
        await metaspace.put({ ibGib: newKeystoneIbGib, space, });
        await metaspace.registerNewIbGib({ ibGib: newKeystoneIbGib, space, });
        return newKeystoneIbGib;
    }
    catch (error) {
        console.error(`${lc} ${extractErrorMsg(error)}`);
        throw error;
    }
    finally {
        if (logalot) {
            console.log(`${lc} complete.`);
        }
    }
}
/**
 * Creates a new keystone ibgib that has no dna and no past.
 */
export async function createKeystoneIbGibImpl({ data, metaspace, space, }) {
    const lc = `[${createKeystoneIbGibImpl.name}]`;
    try {
        if (logalot) {
            console.log(`${lc} starting... (I: 5e32389700e9899e788cbefacef7c825)`);
        }
        space ??= await metaspace.getLocalUserSpace({ lock: false });
        if (!space) {
            throw new Error(`(UNEXPECTED) space was falsy and we couldn't get default local user space from metaspace? (E: 9a6498cf16a8801f19ec376749742225)`);
        }
        // create the actual keystoneIbGib
        const resFirstGen = await Factory_V1.firstGen({
            parentIbGib: Factory_V1.primitive({ ib: KEYSTONE_ATOM }),
            ib: await getKeystoneIb({ keystoneData: data }),
            data,
            dna: false,
            nCounter: true,
            tjp: {
                timestamp: true,
                uuid: true,
            },
        });
        const keystoneIbGib = resFirstGen.newIbGib;
        if (!keystoneIbGib.data) {
            throw new Error(`(UNEXPECTED) keystoneIbGib.data falsy? We expect the data to be populated with real keystone data. (E: 38a358facdb89d16d81d48c8520d3d25)`);
        }
        if (!keystoneIbGib.rel8ns) {
            throw new Error(`(UNEXPECTED) keystoneIbGib.rel8ns falsy? we expect the rel8ns to have ancestor and past. (E: 20cb7723dc33ae1ef808fe76d1bf4b25)`);
        }
        if (!keystoneIbGib.rel8ns.past || keystoneIbGib.rel8ns.past.length === 0) {
            throw new Error(`(UNEXPECTED) keystoneIbGib.rel8ns.past falsy or empty? we expect the firstGen call to generate an interstitial ibgib that we will splice out. (E: 0fd8388d045ab9f37834c27d67e78825)`);
        }
        // reset n
        keystoneIbGib.data.n = 0;
        // reset tjp
        keystoneIbGib.data.isTjp = true;
        delete keystoneIbGib.rel8ns.tjp;
        // reset past
        delete keystoneIbGib.rel8ns.past;
        // recalculate gib
        keystoneIbGib.gib = await getGib({ ibGib: keystoneIbGib });
        // save and register
        await metaspace.put({ ibGib: keystoneIbGib, space, });
        await metaspace.registerNewIbGib({ ibGib: keystoneIbGib, space, });
        return keystoneIbGib;
    }
    catch (error) {
        console.error(`${lc} ${extractErrorMsg(error)}`);
        throw error;
    }
    finally {
        if (logalot) {
            console.log(`${lc} complete.`);
        }
    }
}
//# sourceMappingURL=keystone-helpers.mjs.map