/**
 * @module meta-stone helper/util/etc. functions
 *
 * this is where you will find helper functions like those that generate
 * and parse ibs for meta-stone.
 */
// import * as pathUtils from 'path';
// import { statSync } from 'node:fs';
// import { readFile, } from 'node:fs/promises';
// import * as readline from 'node:readline/promises';
// import { stdin, stdout } from 'node:process'; // decide if use this or not
import { extractErrorMsg, getTimestamp, getTimestampInTicks, } from '@ibgib/helper-gib/dist/helpers/utils-helper.mjs';
import { validateGib, validateIbGibIntrinsically, validateRel8nsIntrinsically } from '@ibgib/ts-gib/dist/V1/validate-helper.mjs';
import { getIbGibAddr } from '@ibgib/ts-gib/dist/helper.mjs';
import { getGibInfo } from '@ibgib/ts-gib/dist/V1/transforms/transform-helper.mjs';
import { GIB } from '@ibgib/ts-gib/dist/V1/constants.mjs';
import { GLOBAL_LOG_A_LOT } from '../../core-constants.mjs';
import { DEFAULT_META_STONE_N, DEFAULT_META_STONE_TIMESTAMP, DEFAULT_META_STONE_TJPGIB, META_STONE_ATOM, META_STONE_TARGET_REL8N_NAME, META_STONE_TARGET_TJP_REL8N_NAME, } from './meta-stone-constants.mjs';
import { constantIbGib, getTimestampInfo, getTjpAddr, isBinary } from '../other/ibgib-helper.mjs';
/**
 * for verbose logging
 */
const logalot = GLOBAL_LOG_A_LOT;
export function validateCommonMetaStoneIb({ ib, }) {
    const lc = `[${validateCommonMetaStoneIb.name}]`;
    try {
        if (logalot) {
            console.log(`${lc} starting...`);
        }
        const errors = [];
        const pieces = ib.split(' ');
        // atom
        const atom = pieces[0];
        if (atom !== META_STONE_ATOM) {
            errors.push(`${lc} invalid ib. atom is expected to be META_STONE_ATOM: ${META_STONE_ATOM} (E: a9812bffc18f44418aa091fb0ed260bd)`);
        }
        // validate tjpGib
        const tjpGib = pieces[1];
        const tjpGibValidationErrors = validateGib({ gib: tjpGib }) ?? [];
        if (tjpGibValidationErrors.length > 0) {
            errors.push(`invalid ib. pieces[1] should be a valid tjpGib. validation errors: ${tjpGibValidationErrors} (E: 233db2babfbe4d28a80f98261f1c0e01)`);
        }
        // n
        const nAsString = pieces[2];
        const parsedN = Number.parseInt(nAsString);
        if (isNaN(parsedN)) {
            errors.push(`invalid ib. space-delimited pieces[2] should be valid integer (E: 71df0d6abd7b4cf4b11621e6bfb556cd)`);
        }
        // timestampInTicks
        const timestampInTicks = pieces[3];
        if (timestampInTicks === 'undefined') {
            // this happens when it's a binary ibgib or just doesn't have a timestamp in the data map
        }
        else {
            const timestampInfo = getTimestampInfo({ timestamp: timestampInTicks });
            if (!timestampInfo.valid) {
                errors.push(`invalid ib. pieces[3] should be valid timestampInTicks. emsg: (${timestampInfo.emsg}) (E: 06dae01d66f54f54b71d9c920c730840)`);
            }
        }
        return errors;
    }
    catch (error) {
        console.error(`${lc} ${extractErrorMsg(error)}`);
        throw error;
    }
    finally {
        if (logalot) {
            console.log(`${lc} complete.`);
        }
    }
}
export function validateCommonMetaStoneData({ data, }) {
    const lc = `[${validateCommonMetaStoneData.name}]`;
    try {
        if (logalot) {
            console.log(`${lc} starting...`);
        }
        if (!data) {
            throw new Error(`data required (E: c50ac422af4501b72af12481fbd4961e)`);
        }
        const errors = [];
        // we need to be careful here. we have two different "data" objects: 1)
        // the metastone's data, and 2) the metastone's targetData.
        // first validate 1) the metastone's data itself (barring the
        // targetData)
        const { targetData, timestamp } = data;
        // timestamp is required
        const timestampInfo = getTimestampInfo({ timestamp });
        if (!timestampInfo.valid) {
            errors.push(`invalid timestampInTicks. should be valid ticks number. (E: 7e381ed5b0c8486b90cfd37676da3626)`);
        }
        const { tjpGib: targetTjpGib, n: targetN, timestamp: targetTimestamp, } = targetData;
        // target tjpGib
        const tjpGibValidationErrors = validateGib({ gib: targetTjpGib }) ?? [];
        if (tjpGibValidationErrors.length > 0) {
            errors.push(`invalid data (targetTjpGib). validation errors: ${tjpGibValidationErrors} (E: d216b12eb75544ef8c5ffb27cd132e2e)`);
        }
        // target n
        if (typeof targetN !== 'number') {
            errors.push(`invalid n. should be valid integer. if target.data.n is falsy, targetN should be -1 (E: 8f234967b388ad80776c8054d71fbc23)`);
        }
        // target timestamp (optional)
        if (targetTimestamp) {
            const targetTimestampInfo = getTimestampInfo({ timestamp: targetTimestamp });
            if (!targetTimestampInfo.valid) {
                errors.push(`invalid targetTimestamp. emsg: ${targetTimestampInfo.emsg} (E: c15d4fd2a2b74a0fab3223e83d954379)`);
            }
        }
        const timestampDate = new Date(timestamp);
        return errors;
    }
    catch (error) {
        console.error(`${lc} ${extractErrorMsg(error)}`);
        throw error;
    }
    finally {
        if (logalot) {
            console.log(`${lc} complete.`);
        }
    }
}
export function validateCommonMetaStoneRel8ns({ rel8ns, }) {
    const lc = `[${validateCommonMetaStoneRel8ns.name}]`;
    try {
        if (logalot) {
            console.log(`${lc} starting...`);
        }
        if (!rel8ns) {
            throw new Error(`rel8ns required (E: 7737ae550e2347e68c67b715c1221393)`);
        }
        const errors = [];
        // basic rel8ns validation
        const intrinsicErrors = validateRel8nsIntrinsically({ rel8ns: rel8ns ?? {} }) ?? [];
        if (intrinsicErrors.length > 0) {
            errors.push(`invalid rel8ns intrinsically. errors: ${intrinsicErrors.join('|')}. (E: abc91753f0f946afa0a3ea2c1917960f)`);
        }
        // [META_STONE_TARGET_REL8N_NAME]: IbGibAddr[];
        // all metastones should have exactly 1 targetAddr
        const targetAddrs = rel8ns[META_STONE_TARGET_REL8N_NAME] ?? [];
        if (targetAddrs.length === 0) {
            errors.push(`invalid metastone rel8ns. should have exactly 1 rel8nName ${META_STONE_TARGET_REL8N_NAME}. has 0. (E: 8181652da9e89613e204d245db12d723)`);
        }
        else if (targetAddrs.length > 1) {
            errors.push(`invalid metastone rel8ns. should have exactly 1 rel8nName ${META_STONE_TARGET_REL8N_NAME}. has ${targetAddrs.length}. (E: 2318047c6fea482d9f53febe02cf8912)`);
        }
        // [META_STONE_TARGET_TJP_REL8N_NAME]?: IbGibAddr[];
        // optional rel8n, should have 0 or 1
        // if we want to get picky, we could check to make sure it is a tjpAddr
        let targetTjpAddrs = rel8ns[META_STONE_TARGET_TJP_REL8N_NAME] ?? [];
        if (targetTjpAddrs.length > 1) {
            errors.push(`invalid metastone rel8ns. should have 0 or 1 rel8nName ${META_STONE_TARGET_TJP_REL8N_NAME}. has ${targetTjpAddrs.length}. (E: 34a8ae53825f46caa7ad024a64400db2)`);
        }
        return errors;
    }
    catch (error) {
        console.error(`${lc} ${extractErrorMsg(error)}`);
        throw error;
    }
    finally {
        if (logalot) {
            console.log(`${lc} complete.`);
        }
    }
}
/**
 * performs validation on the ib, data and rel8ns, as well as validates the
 * ibgib intrinsically (i.e. ensures the gib hash is correct)
 * @returns errors array if any found, otherwise undefined (if no errors)
 */
export async function validateCommonMetaStoneIbGib({ ibGib, }) {
    const lc = `[${validateCommonMetaStoneIbGib.name}]`;
    try {
        if (logalot) {
            console.log(`${lc} starting... (I: f32249d08105b997871a06cdee006311)`);
        }
        const intrinsicErrors = await validateIbGibIntrinsically({ ibGib: ibGib }) ?? [];
        if (!ibGib.data) {
            throw new Error(`MetaStoneIbGib.data required (E: 96432cf4f919d339277d9a1c61ea4264)`);
        }
        // validate ib happens in parse...if only i had a memory...
        const ibErrors = [];
        try {
            const { atom, tjpGib, n, timestampInTicks } = parseMetaStoneIb({ ib: ibGib.ib });
        }
        catch (error) {
            ibErrors.push(extractErrorMsg(error));
        }
        const dataErrors = validateCommonMetaStoneData({ data: ibGib.data });
        const rel8nsErrors = validateCommonMetaStoneRel8ns({ rel8ns: ibGib.rel8ns });
        const result = [
            ...(intrinsicErrors ?? []),
            ...(ibErrors ?? []),
            ...(dataErrors ?? []),
            ...(rel8nsErrors ?? []),
        ];
        if (result.length > 0) {
            return result;
        }
        else {
            return undefined;
        }
    }
    catch (error) {
        console.error(`${lc} ${extractErrorMsg(error)}`);
        throw error;
    }
    finally {
        if (logalot) {
            console.log(`${lc} complete.`);
        }
    }
}
/**
 * validates the given `data` and builds the ib based on that data.
 *
 * _NOTE: This is **NOT** the function to use to build a metastone based a target ibgib._
 *
 * @returns ib shaped for metastone (atow 11/2023 has tjpGib,n,timestampInTicks)
 */
export function getMetaStoneIb({ data, classname, }) {
    const lc = `[${getMetaStoneIb.name}]`;
    try {
        const validationErrors = validateCommonMetaStoneData({ data });
        if (validationErrors.length > 0) {
            throw new Error(`invalid MetaStone data: ${validationErrors} (E: ac642cc5915f0fc0508cb2fb728c86fa)`);
        }
        if (classname) {
            if (logalot) {
                console.log(`${lc} classname not required. no big deal that it is provided here. (I: db4d41471cb976795afd3fa89f8cb823)`);
            }
        }
        // we need to be careful here. we have two different "data" objects: 1)
        // the metastone's data, and 2) the metastone's targetData.
        // these are validated in common data
        const targetTimestamp = data.targetData.timestamp;
        const targetTimestampInTicks = !!targetTimestamp ?
            getTimestampInTicks(targetTimestamp) :
            DEFAULT_META_STONE_TIMESTAMP;
        return `${META_STONE_ATOM} ${data.targetData.tjpGib} ${data.targetData.n} ${targetTimestampInTicks}`;
    }
    catch (error) {
        console.error(`${lc} ${extractErrorMsg(error)}`);
        throw error;
    }
}
/**
 * Current schema is `${META_STONE_ATOM} ${data.targetData.tjpGib} ${data.targetData.n} ${targetTimestampInTicks}`;
 *
 * NOTE this ib is space-delimited
 */
export function parseMetaStoneIb({ ib, }) {
    const lc = `[${parseMetaStoneIb.name}]`;
    try {
        if (!ib) {
            throw new Error(`MetaStone ib required (E: 8e74e098d7aeef52d3952e35150d83ed)`);
        }
        const validationErrors = validateCommonMetaStoneIb({ ib });
        if (validationErrors.length > 0) {
            throw new Error(`invalid ib. validationErrors: ${validationErrors} (E: a6107f62200eb943e523f70f51300623)`);
        }
        const pieces = ib.split(' ');
        return {
            atom: pieces[0],
            tjpGib: pieces[1],
            n: Number.parseInt(pieces[2]),
            timestampInTicks: pieces[3],
        };
    }
    catch (error) {
        console.error(`${lc} ${extractErrorMsg(error)}`);
        throw error;
    }
}
/**
 * metastone ibgibs are for things like helping to keep track of the latest
 * ibgib in a timeline (similar to the HEAD pointer in e.g. git).
 *
 * @returns true if `addr` is for a metastone ibgib, else false
 */
export function isMetaStone({ addr, ibGib, }) {
    // ignore edge case of checking if both are provided that they both should
    // be equal.
    addr ||= getIbGibAddr({ ibGib });
    if (!addr) {
        throw new Error(`either addr or ibGib required. (E: 372b2574d5a9fac298119beed9f6e223)`);
    }
    return addr.startsWith(`${META_STONE_ATOM} `);
}
/**
 * factory function that generates a metastone (constant) ibgib based on the
 * given `ibGib`.
 */
export async function newUpMetaStone({ targetIbGib, }) {
    const lc = `[${newUpMetaStone.name}]`;
    try {
        if (logalot) {
            console.log(`${lc} starting... (I: d846313065c89c89e4b5afa2df2b7123)`);
        }
        const targetAddr = getIbGibAddr({ ibGib: targetIbGib });
        const targetTjpAddr = getTjpAddr({ ibGib: targetIbGib, defaultIfNone: "incomingAddr" });
        let metaStoneData;
        const metaStoneRel8ns = {
            [META_STONE_TARGET_REL8N_NAME]: [targetAddr],
            [META_STONE_TARGET_TJP_REL8N_NAME]: [targetTjpAddr],
        };
        // const targetGibInfo = getGibInfo({ ibGibAddr: targetAddr });
        /**
         * * if the targetIbGib is the tjp, then its gib is the tjpGib.
         * * if the targetIbGib has a tjp, then gibInfo.tjpGib is what we want.
         * * if the targetIbGib is a stone, then we pretend that it is the
         *   tjpGib (any stone might one day be raised up & come to life)
         */
        const targetTjpGib = targetIbGib.data?.isTjp ?
            targetIbGib.gib :
            getGibInfo({ ibGibAddr: targetAddr }).tjpGib ?? targetIbGib.gib;
        const metaStoneTimestampDate = new Date();
        /**
         * timestamp for the metastone itself (not the target.data.timestamp)
         */
        const metaStoneTimestamp = getTimestamp(metaStoneTimestampDate);
        const metaStoneTimestampMs = metaStoneTimestampDate.getMilliseconds();
        if (targetIbGib.data) {
            if (isBinary({ ibGib: targetIbGib })) {
                // binary data is just a uint8array
                metaStoneData = {
                    targetData: {
                        n: -1,
                        tjpGib: targetIbGib.gib,
                    },
                    timestamp: metaStoneTimestamp,
                    timestampMs: metaStoneTimestampMs,
                };
            }
            else {
                metaStoneData = {
                    targetData: {
                        n: targetIbGib.data.n ?? DEFAULT_META_STONE_N,
                        tjpGib: targetTjpGib ?? DEFAULT_META_STONE_TJPGIB,
                        timestamp: targetIbGib.data.timestamp ?? DEFAULT_META_STONE_TIMESTAMP,
                    },
                    timestamp: metaStoneTimestamp,
                    timestampMs: metaStoneTimestampMs,
                };
                if (targetIbGib.data?.isTjp) {
                    metaStoneData.targetData.isTjp = true;
                }
            }
        }
        else {
            // no target data
            metaStoneData = {
                targetData: {
                    n: -1,
                    tjpGib: targetIbGib.gib ?? GIB,
                },
                timestamp: metaStoneTimestamp,
                timestampMs: metaStoneTimestampMs,
            };
        }
        // now that we have the data, we can build the ib
        const metaStoneIb = getMetaStoneIb({ data: metaStoneData });
        // now that we have the data, rel8ns, and the ib, we can create the
        // stone (which is a constant ibgib).
        const metaStoneIbGib = await constantIbGib({
            parentPrimitiveIb: META_STONE_ATOM,
            ib: metaStoneIb,
            data: metaStoneData,
            rel8ns: metaStoneRel8ns,
        });
        if (logalot) {
            console.log(`${lc} metaStoneIbGib... (I: 2b16197478488b329246d22d2dc41423)`);
            console.dir(metaStoneIbGib);
        }
        return metaStoneIbGib;
    }
    catch (error) {
        console.error(`${lc} ${extractErrorMsg(error)}`);
        throw error;
    }
    finally {
        if (logalot) {
            console.log(`${lc} complete.`);
        }
    }
}
//# sourceMappingURL=meta-stone-helper.mjs.map