/**
 * @module node-filesystem-space-helper
 *
 */
import { readFile, } from 'node:fs/promises';
import { default as pathUtils } from 'path';
import { cwd } from 'node:process';
import { extractErrorMsg, unique, } from '@ibgib/helper-gib/dist/helpers/utils-helper.mjs';
import { getIbAndGib, } from '@ibgib/ts-gib/dist/helper.mjs';
import { GIB, } from '@ibgib/ts-gib/dist/V1/index.mjs';
import { validateIbGibIntrinsically } from '@ibgib/ts-gib/dist/V1/validate-helper.mjs';
import { GLOBAL_LOG_A_LOT } from '../../../../core-constants.mjs';
import { parseSpaceIb, } from '../../space-helper.mjs';
import { NodeFilesystemSpace_V1 } from './node-filesystem-space-v1.mjs';
import { B2TFS_DEFAULT_ENCODINGS_TO_TRY, FILE_ENCODINGS } from './node-filesystem-space-types.mjs';
const logalot = GLOBAL_LOG_A_LOT;
/**
 * tries to read the given path. if fails **FOR ANY REASON**, @returns `null`.
 *
 * does not throw
 *
 * @returns file contents as string or `null`
 */
export async function tryRead({ path, directory, encoding, }) {
    const lc = `[${tryRead.name}]`;
    try {
        if (logalot) {
            console.log(`${lc} starting...`);
            console.log(`${lc} cwd: ${cwd()}`);
            if (path.includes('bootstrap^gib')) {
                console.log(`${lc} trying bootstrap^gib... (I: dacfdbe5a2d640ab947a7c17e3c56f78)`);
            }
        }
        const fullPath = pathUtils.join(directory, path);
        if (logalot) {
            console.log(`${lc} fullPath: ${fullPath} (I: 459b416ac32c711bfbab54177839b323)`);
        }
        encoding ||= 'utf8';
        let resRead = undefined;
        resRead = await readFile(fullPath, { encoding: encoding });
        if (logalot) {
            console.log(`${lc} record found. data length: ${resRead?.length ?? 0}. fullPath: ${fullPath}. console.dir(resRead)... (I: 82e3ad9821bd4bf8a54c8facc61dbad0)`);
            console.dir(resRead);
        }
        return resRead;
    }
    catch (error) {
        if (logalot) {
            console.log(`${lc} fullPath not found from directory (${directory}) and path (${path})\nerror:\n${extractErrorMsg(error)} (I: 1fde689d29aa47fcb589b3e7dac8929b)`);
        }
        return null;
    }
    finally {
        if (logalot) {
            console.log(`${lc} complete. (I: e2615c944a464cd48a5635fe401562d9)`);
        }
    }
}
export async function tryRead_bin({ path, directory, encoding, }) {
    const lc = `[${tryRead_bin.name}]`;
    try {
        if (logalot) {
            console.log(`${lc} starting...`);
            console.log(`${lc} cwd: ${cwd()}`);
            if (path.includes('bootstrap^gib')) {
                console.log(`${lc} trying bootstrap^gib... (I: 5eb010444b814addb1a6bbe18ca3dbfa)`);
            }
        }
        const fullPath = pathUtils.join(directory, path);
        if (logalot) {
            console.log(`${lc} fullPath: ${fullPath} (I: c342bf56df354e9ab2653855a458b99d)`);
        }
        encoding ||= 'utf8';
        let resRead = undefined;
        let encodingsToTry = unique([
            encoding,
            ...FILE_ENCODINGS,
        ]);
        let { dataBuffer, dataString, inputPath, detectedEncoding } = await getFileDataAndEncoding({
            inputPath: path,
            encodingsToTry,
        });
        if (!dataBuffer) {
            throw new Error(`getFileDataAndEncoding yielded falsy dataBuffer (E: f7aac299212a1ae41d5bbef6079c4d24)`);
        }
        resRead = dataBuffer;
        if (logalot) {
            console.log(`${lc} record found. data length: ${resRead?.length ?? 0}. fullPath: ${fullPath}. console.dir(resRead)... (I: 438189ba2fd345d782f928ecc7dc6e58)`);
            console.dir(resRead);
        }
        return {
            dataBuffer, dataString, inputPath, detectedEncoding
        };
    }
    catch (error) {
        if (logalot) {
            console.log(`${lc} fullPath not found from directory (${directory}) and path (${path})\nerror:\n${extractErrorMsg(error)} (I: 882e54f079024c94864bdaa74979b397)`);
        }
        return null;
    }
    finally {
        if (logalot) {
            console.log(`${lc} complete. (I: 500e391835aa4d959adf4720fdc38c4b)`);
        }
    }
}
/**
 * basic validation for a node fs space ibgib
 */
export async function validateNodeFilesystemSpace_V1Intrinsically({ space }) {
    const lc = `[${validateNodeFilesystemSpace_V1Intrinsically.name}]`;
    try {
        if (logalot) {
            console.log(`${lc} starting... (I: fcfe57145426ab4a5a0e961e87001922)`);
        }
        const errors = (await validateIbGibIntrinsically({ ibGib: space })) ?? [];
        const { ib, gib, data, rel8ns } = space;
        if (!ib) {
            errors.push('ib required. (E: e69ddadd947d4534ae60af9eda1bda47)');
        }
        if (!gib) {
            errors.push('gib required. (E: 9794fb92dd79473b9139c001f0634ec2)');
        }
        if (gib === GIB) {
            errors.push('gib cannot be primitive. (E: 651c12f1303d45aba54aa228eca1e575)');
        }
        if (!data) {
            errors.push('data required. (E: 264d13ce6b47400e8943bf046c57e8bc)');
            return errors;
        }
        if (!data.name) {
            errors.push('space name required. (E: 4845041c9f2e44098e1e7bb30fe3c13d)');
        }
        // if (!data.classname) { errors.push('classname required. (E: cb645bebf8fc4baabfbf74be39563008)') }
        if (data.classname && data.classname !== NodeFilesystemSpace_V1.name) {
            errors.push(`unknown classname (${data.classname}). data.classname !== FilesystemSpace_V1.name (E: e159eb46ca5e4c2b850998af88cb5840)`);
        }
        if (!data.baseDir) {
            errors.push(`data.baseDir required (E: 373544741a974ab9bc0001d3d63c2601).`);
        }
        if (!data.baseSubPath) {
            errors.push(`data.baseSubPath required. (E: 7336f8eb7c7846d48993e0db893fb7cb)`);
        }
        if (!data.binSubPath) {
            errors.push(`data.binSubPath required. (E: 3544a80a3cec4fdfb35d3e6ccb36dbd6)`);
        }
        if (!data.dnaSubPath) {
            errors.push(`data.dnaSubPath required. (E: 4d6e529cdd0f47a492545c31e1dda865)`);
        }
        if (!data.ibgibsSubPath) {
            errors.push(`data.ibgibsSubPath required. (E: 11b90fdc1b1c41b492c26a61da3409b8)`);
        }
        if (!data.metaSubPath) {
            errors.push(`data.metaSubPath required. (E: 259113f4f0bc488785f9019160c8a226)`);
        }
        if (data.n && typeof data.n !== 'number') {
            errors.push(`data.n must be a number. (E: e7c76f44b1114dba949d06146a8c169b)`);
        }
        if (data.n === undefined) {
            // the very first space record (tjp) has an undefined n and no rel8ns
            if (rel8ns) {
                errors.push(`rel8ns not expected when data.n is falsy (custom temporal junction point indicator I suppose...) (E: 08604c8b33524009886ac2c63eb34c50)`);
            }
        }
        if (!data.spaceSubPath) {
            errors.push(`data.spaceSubPath required. (E: 1688a59dfc6447b3bccb7690b233e4fa)`);
        }
        if (!data.uuid) {
            errors.push(`data.uuid required. (E: dd984a04d0d14d16a4faa300ec125960)`);
        }
        if (!data.encoding) {
            errors.push(`data.encoding required. (E: e75958932f614364a1765a875acad2ce)`);
        }
        /** should probably get this from Capacitor... */
        const validEncodings = ["utf8", "ascii", "utf16"];
        if (!validEncodings.includes(data.encoding)) {
            errors.push(`invalid encoding: ${data.encoding}. validEncodings: ${validEncodings.join(', ')} (E: b88b909521db42cbae684c2937c77079)`);
        }
        // ensure ib matches up with internal data
        const { spaceClassname, spaceId, spaceName } = parseSpaceIb({ spaceIb: ib });
        if (data.classname && (spaceClassname !== data.classname)) {
            errors.push(`ib's spaceClassname (${spaceClassname}) must match data.classname (${data.classname}) (E: 913b8b98717647fea832e4cca80d8ee6)`);
        }
        if (spaceId !== data.uuid) {
            errors.push(`ib's spaceId (${spaceId}) must match data.uuid (${data.uuid}) (E: b20f36f32bc244008b4df856f892a62a)`);
        }
        if (spaceName !== data.name) {
            errors.push(`ib's spaceName (${spaceName}) must match data.name (${data.name}) (E: cf599a9bf875464d9ec99c79cdee0452)`);
        }
        // ensure rel8ns make sense
        if (data.n === undefined && (rel8ns?.past ?? []).length > 0) {
            errors.push(`"past" rel8n not expected when data.n is falsy (E: 77bb7a90b40a4d989a9e53053776be86)`);
        }
        if (data.n && (rel8ns?.past ?? []).length === 0) {
            errors.push(`"past" rel8n required when data.n is truthy (E: 735e07bac58b44deb2523e3d349d5479)`);
        }
        if (data.n === 0 && (rel8ns?.past ?? []).length !== 1) {
            errors.push(`"past" rel8n expected to have a single record when data.n === 0 (E: 8915bb8084414d3b810cff9bccfc3ec6)`);
        }
        if (rel8ns && (rel8ns.past?.length ?? -1) > 0) {
            const pastAddrs = rel8ns.past;
            pastAddrs.forEach(x => {
                const { ib: pastIb } = getIbAndGib({ ibGibAddr: x });
                const pastIbInfo = parseSpaceIb({ spaceIb: pastIb });
                if (pastIbInfo.spaceClassname && (pastIbInfo.spaceClassname !== spaceClassname)) {
                    errors.push(`rel8ns.past address classname (${pastIbInfo.spaceClassname}) must match current spaceClassname (${spaceClassname}) (E: 10b2f05597574558ad2f20db8e471a44)`);
                }
                if (pastIbInfo.spaceId !== spaceId) {
                    errors.push(`rel8ns.past address spaceId (${pastIbInfo.spaceId}) must match current spaceId (${spaceId}) (E: 78b665a31fc34c68be3884d4cd4da35a)`);
                }
                // i want to allow this, but for now we're going to require not changing the name...
                if (pastIbInfo.spaceName !== spaceName) {
                    errors.push(`rel8ns.past address spaceName (${pastIbInfo.spaceName}) must match current spaceName (${spaceName}) (E: 8a6621cbec9e48e7b6d880375c444bfe)`);
                }
            });
        }
        return errors;
    }
    catch (error) {
        console.error(`${lc} ${error.message}`);
        throw error;
    }
    finally {
        if (logalot) {
            console.log(`${lc} complete.`);
        }
    }
}
/**
 * gets the data and encoding of a given {@link inputPath} if that file exists.
 * if it doesn't exist, this will throw an ENOENT error, but this is not logged
 * as an error.
 *
 * this is because atow (03/2024) this is only used in a tryRead_bin function
 * where the file often does not exist.
 */
export async function getFileDataAndEncoding({ inputPath, encodingsToTry = B2TFS_DEFAULT_ENCODINGS_TO_TRY, }) {
    const lc = `[${getFileDataAndEncoding.name}]`;
    try {
        if (logalot) {
            console.log(`${lc} starting... (I: b4688e6e5f28efa9ed95729f10a6df24)`);
        }
        let resDataAndEncoding = undefined;
        const dataBuffer = await readFile(inputPath);
        if (dataBuffer.length === 0) {
            console.warn(`${lc} file at inputPath (${inputPath}) has dataBuffer.length === 0, i.e., no data content. we are treating this as utf8. (W: 2ea55367538146d695482cfb6ec1b79a)`);
            return {
                inputPath,
                dataBuffer,
                detectedEncoding: 'utf8',
                dataString: '',
            }; /* <<<< returns early */
        }
        encodingsToTry ??= B2TFS_DEFAULT_ENCODINGS_TO_TRY;
        const tryEncoding = async (encoding) => {
            let res;
            try {
                const str = dataBuffer.toString(encoding ?? undefined);
                /**
                 * doesn't include the node.js replacement unicode char
                 */
                const isProbablyCorrect = !str.includes('\ufffd');
                res = [isProbablyCorrect, str];
                return res;
            }
            catch (error) {
                debugger;
                res = [false, undefined];
            }
            return res;
        };
        for (let i = 0; i < encodingsToTry.length; i++) {
            const maybeEncoding = encodingsToTry[i];
            const [isProbablyCorrectEncoding, dataString] = await tryEncoding(maybeEncoding);
            if (isProbablyCorrectEncoding) {
                resDataAndEncoding = {
                    inputPath,
                    dataString,
                    detectedEncoding: maybeEncoding,
                    dataBuffer,
                };
                break;
            }
        }
        if (!resDataAndEncoding) {
            debugger; // !resDataAndEncoding -> want to see why resDataAndEncoding is still undefined
            throw new Error(`couldn't turn dataBuffer (length: ${dataBuffer.length}) into dataString with encodingsToTry: (${encodingsToTry}). no idea why. (E: 557c7722f2df7703429778662d3ea224)`);
            // resDataAndEncoding = {
            //     inputPath,
            //     detectedEncoding: undefined,
            //     dataString: undefined,
            //     dataBuffer,
            // }
        }
        return resDataAndEncoding;
    }
    catch (error) {
        // i'm just suppressing ENOENT at this time because in context, this is
        // often the case. we do, however, rethrow the error for proper flow.
        const emsg = extractErrorMsg(error);
        if (!emsg.startsWith('ENOENT:')) {
            console.error(`${lc} ${emsg}`);
        }
        throw error;
    }
    finally {
        if (logalot) {
            console.log(`${lc} complete.`);
        }
    }
}
//# sourceMappingURL=node-filesystem-space-helper.mjs.map