import { Factory_V1 } from "@ibgib/ts-gib/dist/V1/factory.mjs";
import { getIbGibAddr } from "@ibgib/ts-gib/dist/helper.mjs";
import { extractErrorMsg } from "@ibgib/helper-gib/dist/helpers/utils-helper.mjs";
import { rel8 } from "@ibgib/ts-gib/dist/V1/transforms/rel8.mjs";
import { getFromSpace, putInSpace } from "../../witness/space/space-helper.mjs";
import { applyTransforms } from "../../witness/space/reconciliation-space/reconciliation-space-helper.mjs";
import { GRAFT_INFO_ATOM, GRAFT_BASE_REL8N_NAME, GRAFT_ORPHAN_REL8N_NAME, GRAFT_INFO_REL8N_NAME } from "./graft-info-constants.mjs";
const lc = `[graft-info-helpers]`;
/**
 * Validates and constructs the `ib` object for a Graft Info ibGib.
 */
export async function getGraftInfoIb({ data }) {
    if (!data.algo) {
        throw new Error(`(UNEXPECTED) data.algo required for GraftInfoIb (E: 8a9b0c1d2e3f4g5h)`);
    }
    // graft_info algo
    return [GRAFT_INFO_ATOM, data.algo].join(' ');
}
/**
 * Parses a standard Graft Info 'ib' string.
 */
export async function parseGraftInfoIb({ ib }) {
    try {
        const parts = ib.split(' ');
        if (parts[0] !== GRAFT_INFO_ATOM) {
            throw new Error(`Atom mismatch. Expected ${GRAFT_INFO_ATOM} (E: 8f03c92a95144b618821915632599266)`);
        }
        if (parts.length < 2) {
            throw new Error(`Invalid graft info ib. Expected at least 2 parts. (E: 9c2b4c8a6d34469f8263544710183355)`);
        }
        const algo = parts.slice(1).join(' '); // allow spaces in algo? usually single word but safe to join.
        return {
            atom: GRAFT_INFO_ATOM,
            algo,
        };
    }
    catch (error) {
        console.error(`${lc} ${extractErrorMsg(error)}`);
        throw error;
    }
}
/**
 * Creates a new GraftInfo ibGib.
 */
export async function createGraftInfo({ algo, details, }) {
    const data = {
        algo,
        details,
    };
    const ib = await getGraftInfoIb({ data });
    const res = await Factory_V1.stone({
        ib,
        data,
        rel8ns: {}, // No default rel8ns (no 'ancestors' anymore)
        parentPrimitiveIb: GRAFT_INFO_ATOM,
        uuid: true,
    });
    return res;
}
/**
 * Naive line-by-line text merge using LCS.
 * Renamed to mergeTextLCS for clarity, but logic is generic LCS.
 */
export async function mergeTextLCS({ textA, textB, }) {
    if (textA === textB) {
        return textA;
    }
    const linesA = textA.split('\n');
    const linesB = textB.split('\n');
    const common = longestCommonSubsequence(linesA, linesB); // Array of { line, indexA, indexB }
    let resultLines = [];
    let currentA = 0;
    let currentB = 0;
    for (const match of common) {
        // Add unique lines from A (before this match)
        while (currentA < match.indexA) {
            resultLines.push(linesA[currentA]);
            currentA++;
        }
        // Add unique lines from B (before this match)
        while (currentB < match.indexB) {
            resultLines.push(linesB[currentB]);
            currentB++;
        }
        // Add the common line
        resultLines.push(match.line);
        currentA++;
        currentB++;
    }
    // Add remaining unique lines from A
    while (currentA < linesA.length) {
        resultLines.push(linesA[currentA]);
        currentA++;
    }
    // Add remaining unique lines from B
    while (currentB < linesB.length) {
        resultLines.push(linesB[currentB]);
        currentB++;
    }
    return resultLines.join('\n');
}
function longestCommonSubsequence(a, b) {
    const m = a.length;
    const n = b.length;
    const dp = Array(m + 1).fill(0).map(() => Array(n + 1).fill(0));
    // Fill DP table
    for (let i = 1; i <= m; i++) {
        for (let j = 1; j <= n; j++) {
            if (a[i - 1] === b[j - 1]) {
                dp[i][j] = dp[i - 1][j - 1] + 1;
            }
            else {
                dp[i][j] = Math.max(dp[i - 1][j], dp[i][j - 1]);
            }
        }
    }
    // Backtrack to find LCS
    let i = m, j = n;
    const lcs = [];
    while (i > 0 && j > 0) {
        if (a[i - 1] === b[j - 1]) {
            lcs.unshift({ line: a[i - 1], indexA: i - 1, indexB: j - 1 });
            i--;
            j--;
        }
        else if (dp[i - 1][j] > dp[i][j - 1]) {
            i--;
        }
        else {
            j--;
        }
    }
    return lcs;
}
/**
 * Grafts two divergent timelines by splicing missing DNA from one branch (Orphan) onto another (Base).
 *
 * Logic:
 * 1. Checks timestamps of tips. Earlier = Base, Later = Orphan.
 * 2. Fetches DNA from Orphan's branch (since LCA).
 * 3. Replays Orphan's DNA onto Base.
 * 4. Creates GraftInfo linking Base and Orphan.
 * 5. Rel8s the Result to GraftInfo.
 */
export async function graftTimelines({ tipA, branchA, tipB, branchB, space, metaspace, }) {
    const lc = `[${graftTimelines.name}]`;
    console.log(`${lc} [TEST DEBUG] starting... Metaspace present: ${!!metaspace}`);
    // 1. Determine Base and Orphan based on timestamp
    let baseTip;
    let orphanTip; // The one providing the DNA to be replayed
    let orphanBranch;
    const getTs = (ibGib) => {
        const ms = ibGib.data?.timestampMs ?? 0;
        if (!ms && ibGib.data?.timestamp) {
            return new Date(ibGib.data.timestamp).getTime();
        }
        return ms;
    };
    const tsA = getTs(tipA);
    const tsB = getTs(tipB);
    if (tsA <= tsB) {
        // A is earlier (or equal), use A as base
        baseTip = tipA;
        orphanTip = tipB;
        orphanBranch = branchB;
    }
    else {
        // B is earlier, use B as base
        baseTip = tipB;
        orphanTip = tipA;
        orphanBranch = branchA;
    }
    const baseAddr = getIbGibAddr({ ibGib: baseTip });
    const orphanAddr = getIbGibAddr({ ibGib: orphanTip });
    // 2. Gather DNA addresses from the Orphan branch
    const dnaAddrsToApply = [];
    for (const ibGib of orphanBranch) {
        const dnas = ibGib.rel8ns?.dna || [];
        dnaAddrsToApply.push(...dnas);
    }
    if (dnaAddrsToApply.length === 0) {
        return baseTip;
    }
    // 3. Fetch the DNA stones
    const dnaRes = await getFromSpace({ space, addrs: dnaAddrsToApply });
    if (!dnaRes.success || !dnaRes.ibGibs) {
        throw new Error(`Failed to fetch DNA stones for replay. (E: 3a2b1c4d5e6f7g8h)`);
    }
    const allDnaIbGibs = dnaRes.ibGibs;
    // 4. Replay Transforms onto Base
    const createdIbGibs_Running = [];
    const mergedTip = await applyTransforms({
        src: baseTip,
        dnaAddrsToApplyToStoreVersion: [...dnaAddrsToApply],
        allLocalIbGibs: allDnaIbGibs,
        createdIbGibs_Running,
    });
    // 5. Persist the new stones
    await putInSpace({ space, ibGibs: createdIbGibs_Running });
    // 6. Create Graft Info
    const graftInfo = await createGraftInfo({
        algo: 'optimistic_dna_replay',
        details: `Replayed ${dnaAddrsToApply.length} transforms from orphan (${orphanAddr}) onto base (${baseAddr}).`,
    });
    // Add relations via rel8
    const graftInfoRel8nsToAdd = {
        [GRAFT_BASE_REL8N_NAME]: [baseAddr],
        [GRAFT_ORPHAN_REL8N_NAME]: [orphanAddr],
    };
    const { newIbGib: graftInfoWithRel8ns } = await rel8({
        src: graftInfo,
        rel8nsToAddByAddr: graftInfoRel8nsToAdd,
        dna: true,
    });
    // Persist this update
    await putInSpace({ space, ibGibs: [graftInfoWithRel8ns] });
    // 7. Link GraftInfo to Result via 'graftinfo' rel8n
    const graftInfoAddr = getIbGibAddr({ ibGib: graftInfoWithRel8ns });
    const { newIbGib: finalIbGib, dnas: rel8Dnas, intermediateIbGibs } = await rel8({
        src: mergedTip,
        rel8nsToAddByAddr: {
            [GRAFT_INFO_REL8N_NAME]: [graftInfoAddr],
        },
        dna: true, // Auto-link 'dna'
        nCounter: true,
    });
    // Persist final result
    const finalStones = [finalIbGib, ...(rel8Dnas || []), ...(intermediateIbGibs || [])];
    await putInSpace({ space, ibGibs: finalStones });
    // 8. Register New Tip in Metaspace (CRITICAL for updating Timeline Index)
    if (metaspace && metaspace.registerNewIbGib) {
        await metaspace.registerNewIbGib({ ibGib: finalIbGib, space });
    }
    return finalIbGib;
}
//# sourceMappingURL=graft-info-helpers.mjs.map