/**
 * @module agent-helpers
 *
 * Helper functions designed to simplify common ibGib operations for agentic usage.
 * These abstract away the low-level Factory_V1 details and provide clear, semantic
 * actions for creating and modifying data.
 */
import { extractErrorMsg, getUUID } 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 { Factory_V1 } from '@ibgib/ts-gib/dist/V1/factory.mjs';
import { mut8Timeline, appendToTimeline } from './timeline/timeline-api.mjs';
import { persistTransformResult, registerNewIbGib } from './witness/space/space-helper.mjs';
const logalot = GLOBAL_LOG_A_LOT;
/**
 * Creates a "Stone" ibGib - an immutable, primitive-like data record with no history/timeline.
 * Useful for static data, configurations, or standalone records.
 */
export async function createStoneTestHelper({ ib, data, rel8ns, uuid, noTimestamp, }) {
    const lc = `[${createStoneTestHelper.name}]`;
    try {
        if (logalot) {
            console.log(`${lc} starting... (I: e4b15ee74de22ec9a917c578adc83425)`);
        }
        if (!ib) {
            throw new Error(`ib required (E: 3d5df8fd5b880e0e0dcedd37dbc3f825)`);
        }
        // the parent primitive ib is almost always the atom of the ib.
        const atom = ib.split(' ').at(0);
        if (uuid) {
            if (!data) {
                data = {};
            }
            data.uuid = await getUUID();
        }
        const stone = await Factory_V1.stone({
            parentPrimitiveIb: atom,
            ib,
            data,
            rel8ns,
            uuid,
        });
        return stone;
    }
    catch (error) {
        console.error(`${lc} ${extractErrorMsg(error)}`);
        throw error;
    }
    finally {
        if (logalot) {
            console.log(`${lc} complete.`);
        }
    }
}
/**
 * Creates a new "Living" Timeline Root.
 * This is the starting point for any mutable data structure (a file, a user profile, a post).
 */
export async function createTimelineRootTestHelper({ ib, data, space, }) {
    const lc = `[${createTimelineRootTestHelper.name}]`;
    try {
        if (logalot) {
            console.log(`${lc} starting... (I: 8f50a8cf0268742048f7c188feb5a825)`);
        }
        // Determine the parent to fork based on the provided options.
        const atom = ib.split(' ').at(0);
        const parentIbGib = Factory_V1.primitive({ ib: atom });
        const resNew = await Factory_V1.firstGen({
            parentIbGib,
            ib,
            data,
            rel8ns: undefined,
            // these options are what make this a "timeline"
            dna: true,
            nCounter: true,
            tjp: { timestamp: true, uuid: true },
        });
        // Persist the new timeline and its DNA to the designated space.
        await persistTransformResult({ resTransform: resNew, space });
        await registerNewIbGib({
            ibGib: resNew.newIbGib,
            space,
            /**
             * We don't need to broadcast here because it's a new, independent
             * timeline, so there are no listeners yet.
             */
            fnBroadcast: undefined,
        });
        // We don't need to lock here because it's a new, independent timeline,
        // so there are no concurrency concerns yet.
        return resNew.newIbGib;
    }
    catch (error) {
        console.error(`${lc} ${extractErrorMsg(error)}`);
        throw error;
    }
    finally {
        if (logalot) {
            console.log(`${lc} complete.`);
        }
    }
}
/**
 * r0, r0r1, r0r1r2
 */
const TEST_IB_ROUNDS_REGEXP = /^(r\d)+$/;
/**
 * v0, v0v1, v0v1v2
 */
const TEST_IB_VERSIONS_REGEXP = /^(v\d)+$/;
/**
 * Parses ib to extract base, rounds, and version.
 * Schema: [base] [r1r2...] [v0v1...] (space-delimited)
 * Example: "alpha r0r1 v0v1v2" → { base: "alpha", rounds: "r1", version: 2 }
 */
export function parseTestIb(ib) {
    const lc = `[${parseTestIb.name}]`;
    try {
        if (logalot) {
            console.log(`${lc} starting... (I: 87b6d85567f2e2a4b84a4278fb1bc826)`);
        }
        const parts = ib.split(' ');
        if (parts.length === 1) {
            // Initial ib, no rounds or versions yet
            throw new Error(`(UNEXPECTED) does this ever hit? (E: 584978e0c644aec1e86f7e964a547526)`);
            // return {
            //     atom: parts[0],
            //     rounds: '',
            //     roundNumber: -1,
            //     versions: '',
            //     versionNumber: -1,
            //     comments_snake_plus_CamelCase: undefined,
            // };
        }
        const [atom, // alpha, beta, etc.
        rounds, versions, comments_snake_plus_CamelCase,] = parts;
        // ignoring possibility of later erroneous parts for now
        // Find round and version parts
        if (!rounds) {
            throw new Error(`invalid ib. rounds falsy (E: b706c8005e7f619e0386b6981d835826)`);
        }
        if (!TEST_IB_ROUNDS_REGEXP.test(rounds)) {
            throw new Error(`invalid ib. rounds (${rounds}) does not match TEST_IB_ROUNDS_REGEXP: ${TEST_IB_ROUNDS_REGEXP.source} (E: 5498b89c25321a97a8728be82b1f7b26)`);
        }
        const roundNumber = Number.parseInt(rounds.split('r').at(-1));
        if (!versions) {
            throw new Error(`invalid ib. versions falsy (E: 79aa180471b434c685ea27987b50e726)`);
        }
        if (!TEST_IB_VERSIONS_REGEXP.test(versions)) {
            throw new Error(`invalid ib. versions (${versions}) does not match TEST_IB_VERSIONS_REGEXP: ${TEST_IB_VERSIONS_REGEXP.source} (E: 5498b89c25321a97a8728be82b1f7b26)`);
        }
        const versionNumber = Number.parseInt(versions.split('v').at(-1));
        return {
            atom,
            rounds,
            roundNumber,
            versions,
            versionNumber,
            comments_snake_plus_CamelCase,
        };
    }
    catch (error) {
        console.error(`${lc} ${extractErrorMsg(error)}`);
        throw error;
    }
    finally {
        if (logalot) {
            console.log(`${lc} complete.`);
        }
    }
}
function getNewTestIb({ atom, comments_snake_plus_CamelCase, }) {
    const lc = `[${getNewTestIb.name}]`;
    try {
        if (logalot) {
            console.log(`${lc} starting... (I: 60aebdb828f72bbfbcbf401e7af09826)`);
        }
        if (!atom) {
            throw new Error(`atom required (E: dc041852cdd88d692a8c2168ae4c7626)`);
        }
        let ib = [
            atom,
            'r1',
            'v0',
        ].join(' ');
        if (comments_snake_plus_CamelCase) {
            ib += ' ' + comments_snake_plus_CamelCase;
        }
        return ib;
    }
    catch (error) {
        console.error(`${lc} ${extractErrorMsg(error)}`);
        throw error;
    }
    finally {
        if (logalot) {
            console.log(`${lc} complete.`);
        }
    }
}
/**
 * Stateful transformer for test operations.
 * Manages counters, rounds, and execution history.
 *
 * Usage:
 * ```typescript
 * const transformer = new TestTransformer();
 * const r1 = transformer.newRound({ name: 'r1_common_history' });
 *
 * const step = await transformer.mut8({
 * Test transformer class that provides fluent method chaining for timeline testing.
 * Automatically tracks rounds, steps, provides test data snapshots, and auto-versions ibs.
 *
 * Ib versioning schema: `[base] [r1r2...] [v0v1v2]` (space-delimited)
 * Example: `alpha r1 v0` → `alpha r1 v1` → `alpha r1r2 v0`
 */
export class TestTransformer {
    lc = `[${TestTransformer.name}]`;
    metaspace;
    sourceSpace;
    destSpace;
    mut8Counter = 0;
    rel8Counter = 0;
    rounds = [];
    get currentRound() {
        return this.rounds && this.rounds.length > 0 ?
            this.rounds.at(-1) :
            undefined;
    }
    ;
    constructor(metaspace, sourceSpace, destSpace) {
        this.metaspace = metaspace;
        this.sourceSpace = sourceSpace;
        this.destSpace = destSpace;
    }
    /**
     * Start a new round of testing operations.
     */
    newRound({ name, description }) {
        const round = { name, description, steps: [] };
        this.rounds.push(round);
        return round;
    }
    /**
     * Get all steps across all rounds.
     */
    getAllSteps() {
        return this.rounds.flatMap(r => r.steps);
    }
    /**
     * Find a round by name.
     */
    getRound(name) {
        return this.rounds.find(r => r.name === name);
    }
    /**
     * Reset all state (counters and rounds).
     */
    reset() {
        this.mut8Counter = 0;
        this.rel8Counter = 0;
        this.rounds = [];
    }
    /**
     * Get space by location.
     */
    getSpace(location) {
        return location === 'source' ? this.sourceSpace : this.destSpace;
    }
    /**
     * Generates next ib based on current ib and round context.
     * Increments version and appends round if needed.
     */
    generateNextIb({ currentIb, comments_snake_plus_CamelCase, }) {
        const lc = `${this.lc}[${this.generateNextIb.name}]`;
        try {
            if (logalot) {
                console.log(`${lc} starting... (I: 9291a8f3c6b168e53856c61ba9d5c826)`);
            }
            const { atom, rounds, roundNumber, versions, versionNumber } = parseTestIb(currentIb);
            if (!this.currentRound) {
                throw new Error(`(UNEXPECTED) this.currentRound falsy? (E: 4594188ef0a859759daaa4b8ea1a7926)`);
            }
            const currentRoundNumber = this.rounds.length;
            const needsNewRounds = roundNumber !== currentRoundNumber;
            /**
             * the next rounds is only different if we are in a different round.
             */
            const nextRounds = needsNewRounds ?
                `${rounds}r${roundNumber + 1}` :
                rounds;
            /**
             * always increment the version
             */
            const nextVersions = `${versions}v${versionNumber + 1}`;
            if (comments_snake_plus_CamelCase && comments_snake_plus_CamelCase.includes(' ')) {
                throw new Error(`comments_snake_plus_CamelCase contains spaces. must be underscore-delimited camelCase comments (E: de725d4090de648bf221cbaf4e041826)`);
            }
            let nextIb = `${atom} ${nextRounds} ${nextVersions}`;
            if (comments_snake_plus_CamelCase) {
                nextIb += ' ' + comments_snake_plus_CamelCase;
            }
            return nextIb;
        }
        catch (error) {
            console.error(`${lc} ${extractErrorMsg(error)}`);
            throw error;
        }
        finally {
            if (logalot) {
                console.log(`${lc} complete.`);
            }
        }
    }
    /**
     * Create a timeline root with auto-tracking to current round.
     *
     * @example
     * const transformer = new TestTransformer(metaspace, sourceSpace, destSpace);
     * transformer.newRound({ name: 'r1_setup' });
     * const step = await transformer.create({
     *   ib: 'alpha',
     *   data: { type: 'alpha' },
     *   in: 'source',
     * });
     */
    async create({ atom, data, in: location, name, comments_snake_plus_CamelCase, }) {
        const lc = `${this.lc}[create]`;
        if (!this.currentRound) {
            throw new Error(`${lc} No active round. Call newRound() first.`);
        }
        const space = this.getSpace(location);
        const timeline = await createTimelineRootTestHelper({
            ib: getNewTestIb({ atom, comments_snake_plus_CamelCase }),
            data,
            space,
        });
        const result = {
            ibGib: timeline,
            addr: getIbGibAddr({ ibGib: timeline }),
            space,
            metaspace: this.metaspace,
            infos: [],
            name: name || `${this.currentRound.name}_${atom}_create`,
        };
        this.currentRound.steps.push(result);
        return result;
    }
    /**
     * Perform a mutation with optional field mutation and auto-versioning.
     *
     * @example
     * // With field and auto value
     * const step = await transformer.mut8({
     *   stepInfo: prevStep,
     *   strField: 'commonField',
     * });
     * // Creates: alpha r1 v0, sets commonField: 'value_1'
     *
     * @example
     * // With field and custom value
     * const step = await transformer.mut8({
     *   stepInfo: prevStep,
     *   strField: { name: 'count', value: '42' },
     * });
     *
     * @example
     * // Ib-only mutation (no field)
     * const step = await transformer.mut8({ stepInfo: prevStep });
     */
    async mut8({ ibGib, stepInfo, in: location, strField, name, comments_snake_plus_CamelCase, }) {
        const lc = `${this.lc}[mut8]`;
        if (!this.currentRound) {
            throw new Error(`${lc} No active round. Call newRound() first.`);
        }
        // Extract context
        let targetIbGib;
        let space;
        if (stepInfo) {
            targetIbGib = stepInfo.ibGib;
            space = stepInfo.space;
        }
        else if (ibGib && location) {
            targetIbGib = ibGib;
            space = this.getSpace(location);
        }
        else {
            throw new Error(`${lc} Must provide either stepInfo or (ibGib + in)`);
        }
        // Generate next ib with auto-versioning
        const nextIb = this.generateNextIb({
            currentIb: targetIbGib.ib,
            comments_snake_plus_CamelCase,
        });
        // Build data to add/patch
        const dataToAddOrPatch = {};
        if (strField) {
            let fieldName;
            let fieldValue;
            if (typeof strField === 'string') {
                // Field name only, generate random value
                fieldName = strField;
                fieldValue = `value_${++this.mut8Counter}`;
            }
            else {
                // Field name and value provided
                fieldName = strField.name;
                fieldValue = strField.value;
                this.mut8Counter++; // Still increment counter
            }
            dataToAddOrPatch[fieldName] = fieldValue;
        }
        // Perform mutation
        const mutatedIbGib = await mut8Timeline({
            timeline: targetIbGib,
            mut8Opts: {
                mut8Ib: nextIb,
                dataToAddOrPatch,
            },
            metaspace: this.metaspace,
            space,
        });
        const mut8Info = {
            type: 'mut8',
            ib: nextIb,
            key: strField ? (typeof strField === 'string' ? strField : strField.name) : undefined,
            value: strField ? (typeof strField === 'string' ? `value_${this.mut8Counter}` : strField.value) : undefined,
        };
        const result = {
            ibGib: mutatedIbGib,
            addr: getIbGibAddr({ ibGib: mutatedIbGib }),
            space,
            metaspace: this.metaspace,
            infos: [mut8Info],
            name: name || `${this.currentRound.name}_${nextIb}`,
        };
        this.currentRound.steps.push(result);
        return result;
    }
    /**
     * Perform a relation with auto-generated rel8n name and auto-versioning.
     *
     * @example
     * const step = await transformer.rel8({
     *   stepInfo: prevStep,
     *   targetIbGibs: [beta, gamma],
     *   rel8nName: 'depends_on',
     * });
     */
    async rel8({ stepInfo, ibGib, in: location, targetIbGibs, rel8nName, name,
    // comments_snake_plus_CamelCase,
     }) {
        const lc = `${this.lc}[rel8]`;
        if (!this.currentRound) {
            throw new Error(`${lc} No active round. Call newRound() first.`);
        }
        // Extract context
        let sourceIbGib;
        let space;
        if (stepInfo) {
            sourceIbGib = stepInfo.ibGib;
            space = stepInfo.space;
        }
        else if (ibGib && location) {
            sourceIbGib = ibGib;
            space = this.getSpace(location);
        }
        else {
            throw new Error(`${lc} Must provide either stepInfo or (ibGib + in)`);
        }
        // Generate next ib with auto-versioning
        // we can't do this right now because the rel8 transform cannot itself
        // change the ib. this only currently is available through the mut8
        // call.
        // const nextIb = this.generateNextIb({currentIb: sourceIbGib.ib, comments_snake_plus_CamelCase});
        // Perform relation
        const relatedIbGib = await appendToTimeline({
            timeline: sourceIbGib,
            rel8nInfos: [{
                    rel8nName,
                    ibGibs: targetIbGibs
                }],
            metaspace: this.metaspace,
            space,
        });
        const rel8Info = {
            type: 'rel8',
            ib: sourceIbGib.ib,
            rel8nName,
            targetAddrs: targetIbGibs.map(x => getIbGibAddr({ ibGib: x })),
        };
        const result = {
            ibGib: relatedIbGib,
            addr: getIbGibAddr({ ibGib: relatedIbGib }),
            space,
            metaspace: this.metaspace,
            infos: [rel8Info],
            name: name || `${this.currentRound.name}_autoname_${sourceIbGib.ib}`,
        };
        this.currentRound.steps.push(result);
        return result;
    }
}
//# sourceMappingURL=test-helpers.mjs.map